From f455e7994ef657395d594f48b64853985219206d Mon Sep 17 00:00:00 2001 From: Vinay Keerthi <11478411+stonecharioteer@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:07:24 +0530 Subject: [PATCH 1/3] fix: respect status parameter when creating articles via API (#12846) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description The Articles API was ignoring the `status` parameter when creating new articles. All articles were forced to be drafts due to a hardcoded `@article.draft!` call in the controller, even when users explicitly sent `status: 1` (published) in their API request. This PR removes the hardcoded draft enforcement and allows the status parameter to be respected while maintaining backward compatibility. Fixes #12063 ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? **Before:** - API POST with `status: 1` → Created as draft (ignored parameter) - API POST without status → Created as draft **After:** - API POST with `status: 1` → Created as published ✅ - API POST without status → Created as draft (backward compatible) ✅ - UI creates articles → Still creates as draft (UI doesn't send status) ✅ **Tests run:** ```bash bundle exec rspec spec/controllers/api/v1/accounts/articles_controller_spec.rb # 17 examples, 0 failures ``` Updated tests: 1. Changed 2 existing tests that were verifying the broken behavior (expecting draft when published was sent) 2. Added new test to verify articles default to draft when status is not provided 3. All existing tests pass, confirming backward compatibility ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes Co-authored-by: Sojan Jose --- .../api/v1/accounts/articles_controller.rb | 5 ++-- .../v1/accounts/articles_controller_spec.rb | 24 +++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index da2be2312..8a6fd61f8 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -22,9 +22,10 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController def edit; end def create - @article = @portal.articles.create!(article_params) + params_with_defaults = article_params + params_with_defaults[:status] ||= :draft + @article = @portal.articles.create!(params_with_defaults) @article.associate_root_article(article_params[:associated_article_id]) - @article.draft! render json: { error: @article.errors.messages }, status: :unprocessable_entity and return unless @article.valid? end diff --git a/spec/controllers/api/v1/accounts/articles_controller_spec.rb b/spec/controllers/api/v1/accounts/articles_controller_spec.rb index 42fd062dc..e74be76e2 100644 --- a/spec/controllers/api/v1/accounts/articles_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/articles_controller_spec.rb @@ -36,7 +36,7 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do expect(response).to have_http_status(:success) json_response = response.parsed_body expect(json_response['payload']['title']).to eql('MyTitle') - expect(json_response['payload']['status']).to eql('draft') + expect(json_response['payload']['status']).to eql('published') expect(json_response['payload']['position']).to be(3) end @@ -59,10 +59,30 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do expect(response).to have_http_status(:success) json_response = response.parsed_body expect(json_response['payload']['title']).to eql('MyTitle') - expect(json_response['payload']['status']).to eql('draft') + expect(json_response['payload']['status']).to eql('published') expect(json_response['payload']['position']).to be(3) end + it 'creates article as draft when status is not provided' do + article_params = { + article: { + category_id: category.id, + description: 'test description', + title: 'DraftTitle', + slug: 'draft-title', + content: 'This is my draft content.', + author_id: agent.id + } + } + post "/api/v1/accounts/#{account.id}/portals/#{portal.slug}/articles", + params: article_params, + headers: admin.create_new_auth_token + expect(response).to have_http_status(:success) + json_response = response.parsed_body + expect(json_response['payload']['title']).to eql('DraftTitle') + expect(json_response['payload']['status']).to eql('draft') + end + it 'associate to the root article' do root_article = create(:article, category: category, slug: 'root-article', portal: portal, account_id: account.id, author_id: agent.id, associated_article_id: nil) From 4f09c2203c41e7847d04e60c89317b2999706a21 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Thu, 13 Nov 2025 12:46:55 +0530 Subject: [PATCH 2/3] feat: allow querying reporting events via the API (#12832) --- app/models/reporting_event.rb | 17 ++ config/routes.rb | 2 + .../accounts/reporting_events_controller.rb | 30 +++ .../v1/accounts/conversations_controller.rb | 4 + .../reporting_events.json.jbuilder | 3 + .../reporting_events/index.json.jbuilder | 11 + .../v1/models/_reporting_event.json.jbuilder | 12 + .../accounts/conversations_controller_spec.rb | 142 +++++++++++ .../reporting_events_controller_spec.rb | 217 ++++++++++++++++ spec/factories/reporting_events.rb | 12 +- swagger/definitions/index.yml | 6 + .../definitions/resource/reporting_event.yml | 47 ++++ .../resource/reporting_event_meta.yml | 11 + .../reporting_events_list_response.yml | 10 + .../conversation/reporting_events.yml | 29 +++ .../application/reporting_events/index.yml | 47 ++++ swagger/paths/index.yml | 16 ++ swagger/swagger.json | 240 ++++++++++++++++++ swagger/tag_groups/application_swagger.json | 240 ++++++++++++++++++ swagger/tag_groups/client_swagger.json | 94 +++++++ swagger/tag_groups/other_swagger.json | 94 +++++++ swagger/tag_groups/platform_swagger.json | 94 +++++++ 22 files changed, 1373 insertions(+), 5 deletions(-) create mode 100644 enterprise/app/controllers/api/v1/accounts/reporting_events_controller.rb create mode 100644 enterprise/app/views/api/v1/accounts/conversations/reporting_events.json.jbuilder create mode 100644 enterprise/app/views/api/v1/accounts/reporting_events/index.json.jbuilder create mode 100644 enterprise/app/views/api/v1/models/_reporting_event.json.jbuilder create mode 100644 spec/enterprise/controllers/api/v1/accounts/reporting_events_controller_spec.rb create mode 100644 swagger/definitions/resource/reporting_event.yml create mode 100644 swagger/definitions/resource/reporting_event_meta.yml create mode 100644 swagger/definitions/resource/reporting_events_list_response.yml create mode 100644 swagger/paths/application/conversation/reporting_events.yml create mode 100644 swagger/paths/application/reporting_events/index.yml diff --git a/app/models/reporting_event.rb b/app/models/reporting_event.rb index 2f141786b..f083e32a1 100644 --- a/app/models/reporting_event.rb +++ b/app/models/reporting_event.rb @@ -35,4 +35,21 @@ class ReportingEvent < ApplicationRecord belongs_to :user, optional: true belongs_to :inbox, optional: true belongs_to :conversation, optional: true + + # Scopes for filtering + scope :filter_by_date_range, lambda { |range| + where(created_at: range) if range.present? + } + + scope :filter_by_inbox_id, lambda { |inbox_id| + where(inbox_id: inbox_id) if inbox_id.present? + } + + scope :filter_by_user_id, lambda { |user_id| + where(user_id: user_id) if user_id.present? + } + + scope :filter_by_name, lambda { |name| + where(name: name) if name.present? + } end diff --git a/config/routes.rb b/config/routes.rb index 639c51da7..9c6866e1d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -141,6 +141,7 @@ Rails.application.routes.draw do post :custom_attributes get :attachments get :inbox_assistant + get :reporting_events if ChatwootApp.enterprise? end end @@ -186,6 +187,7 @@ Rails.application.routes.draw do get :download end end + resources :reporting_events, only: [:index] if ChatwootApp.enterprise? resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy] resources :custom_filters, only: [:index, :show, :create, :update, :destroy] resources :inboxes, only: [:index, :show, :create, :update, :destroy] do diff --git a/enterprise/app/controllers/api/v1/accounts/reporting_events_controller.rb b/enterprise/app/controllers/api/v1/accounts/reporting_events_controller.rb new file mode 100644 index 000000000..eb282b0ee --- /dev/null +++ b/enterprise/app/controllers/api/v1/accounts/reporting_events_controller.rb @@ -0,0 +1,30 @@ +class Api::V1::Accounts::ReportingEventsController < Api::V1::Accounts::EnterpriseAccountsController + include DateRangeHelper + + RESULTS_PER_PAGE = 25 + + before_action :check_admin_authorization? + before_action :set_reporting_events, only: [:index] + before_action :set_current_page, only: [:index] + + def index + @reporting_events = @reporting_events.page(@current_page).per(RESULTS_PER_PAGE) + @total_count = @reporting_events.total_count + end + + private + + def set_reporting_events + @reporting_events = Current.account.reporting_events + .includes(:conversation, :user, :inbox) + .filter_by_date_range(range) + .filter_by_inbox_id(params[:inbox_id]) + .filter_by_user_id(params[:user_id]) + .filter_by_name(params[:name]) + .order(created_at: :desc) + end + + def set_current_page + @current_page = (params[:page] || 1).to_i + end +end diff --git a/enterprise/app/controllers/enterprise/api/v1/accounts/conversations_controller.rb b/enterprise/app/controllers/enterprise/api/v1/accounts/conversations_controller.rb index c0ecc2a45..f87c06658 100644 --- a/enterprise/app/controllers/enterprise/api/v1/accounts/conversations_controller.rb +++ b/enterprise/app/controllers/enterprise/api/v1/accounts/conversations_controller.rb @@ -11,6 +11,10 @@ module Enterprise::Api::V1::Accounts::ConversationsController end end + def reporting_events + @reporting_events = @conversation.reporting_events.order(created_at: :asc) + end + def permitted_update_params super.merge(params.permit(:sla_policy_id)) end diff --git a/enterprise/app/views/api/v1/accounts/conversations/reporting_events.json.jbuilder b/enterprise/app/views/api/v1/accounts/conversations/reporting_events.json.jbuilder new file mode 100644 index 000000000..691296522 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/conversations/reporting_events.json.jbuilder @@ -0,0 +1,3 @@ +json.array! @reporting_events do |reporting_event| + json.partial! 'api/v1/models/reporting_event', formats: [:json], reporting_event: reporting_event +end diff --git a/enterprise/app/views/api/v1/accounts/reporting_events/index.json.jbuilder b/enterprise/app/views/api/v1/accounts/reporting_events/index.json.jbuilder new file mode 100644 index 000000000..ae8531955 --- /dev/null +++ b/enterprise/app/views/api/v1/accounts/reporting_events/index.json.jbuilder @@ -0,0 +1,11 @@ +json.payload do + json.array! @reporting_events do |reporting_event| + json.partial! 'api/v1/models/reporting_event', formats: [:json], reporting_event: reporting_event + end +end + +json.meta do + json.count @total_count + json.current_page @current_page + json.total_pages @reporting_events.total_pages +end diff --git a/enterprise/app/views/api/v1/models/_reporting_event.json.jbuilder b/enterprise/app/views/api/v1/models/_reporting_event.json.jbuilder new file mode 100644 index 000000000..f8e5f08ba --- /dev/null +++ b/enterprise/app/views/api/v1/models/_reporting_event.json.jbuilder @@ -0,0 +1,12 @@ +json.id reporting_event.id +json.name reporting_event.name +json.value reporting_event.value +json.value_in_business_hours reporting_event.value_in_business_hours +json.event_start_time reporting_event.event_start_time +json.event_end_time reporting_event.event_end_time +json.account_id reporting_event.account_id +json.inbox_id reporting_event.inbox_id +json.user_id reporting_event.user_id +json.conversation_id reporting_event.conversation_id +json.created_at reporting_event.created_at +json.updated_at reporting_event.updated_at diff --git a/spec/enterprise/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/conversations_controller_spec.rb index bbb3f1eb3..472dc959b 100644 --- a/spec/enterprise/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/enterprise/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -102,4 +102,146 @@ RSpec.describe 'Conversations API', type: :request do end end end + + describe 'GET /api/v1/accounts/{account.id}/conversations/:id/reporting_events' do + let(:conversation) { create(:conversation, account: account) } + let(:inbox) { conversation.inbox } + let(:agent) { administrator } + + before do + # Create reporting events for this conversation + @event1 = create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'first_response', + value: 120, + created_at: 3.hours.ago) + + @event2 = create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'reply_time', + value: 45, + created_at: 2.hours.ago) + + @event3 = create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'resolution', + value: 300, + created_at: 1.hour.ago) + + # Create an event for a different conversation (should not be included) + other_conversation = create(:conversation, account: account) + create(:reporting_event, + account: account, + conversation: other_conversation, + inbox: other_conversation.inbox, + user: agent, + name: 'other_conversation_event', + value: 60) + end + + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events", + as: :json + + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated user with conversation access' do + it 'returns all reporting events for the conversation' do + get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events", + headers: administrator.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + # Should return array directly (no pagination) + expect(json_response).to be_an(Array) + expect(json_response.size).to eq(3) + + # Check they are sorted by created_at asc (oldest first) + expect(json_response.first['name']).to eq('first_response') + expect(json_response.last['name']).to eq('resolution') + + # Verify it doesn't include events from other conversations + event_names = json_response.map { |e| e['name'] } + expect(event_names).not_to include('other_conversation_event') + end + + it 'returns empty array when conversation has no reporting events' do + conversation_without_events = create(:conversation, account: account) + + get "/api/v1/accounts/#{account.id}/conversations/#{conversation_without_events.display_id}/reporting_events", + headers: administrator.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response).to be_an(Array) + expect(json_response).to be_empty + end + end + + context 'when agent has limited access' do + let(:limited_agent) { create(:user, account: account, role: :agent) } + + it 'returns unauthorized for unassigned conversation without permission' do + get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events", + headers: limited_agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:unauthorized) + end + + it 'returns reporting events when agent is assigned to the conversation' do + conversation.update!(assignee: limited_agent) + # Also create inbox member for the agent + create(:inbox_member, user: limited_agent, inbox: conversation.inbox) + + get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events", + headers: limited_agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response).to be_an(Array) + expect(json_response.size).to eq(3) + end + end + + context 'when agent has team access' do + let(:team_agent) { create(:user, account: account, role: :agent) } + let(:team) { create(:team, account: account) } + + before do + create(:team_member, team: team, user: team_agent) + conversation.update!(team: team) + end + + it 'allows accessing conversation reporting events via team membership' do + get "/api/v1/accounts/#{account.id}/conversations/#{conversation.display_id}/reporting_events", + headers: team_agent.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response).to be_an(Array) + expect(json_response.size).to eq(3) + end + end + end end diff --git a/spec/enterprise/controllers/api/v1/accounts/reporting_events_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/reporting_events_controller_spec.rb new file mode 100644 index 000000000..6b3ffc4ba --- /dev/null +++ b/spec/enterprise/controllers/api/v1/accounts/reporting_events_controller_spec.rb @@ -0,0 +1,217 @@ +require 'rails_helper' + +RSpec.describe 'Enterprise Reporting Events API', type: :request do + let!(:account) { create(:account) } + let!(:admin) { create(:user, account: account, role: :administrator) } + let!(:agent) { create(:user, account: account, role: :agent) } + let!(:inbox) { create(:inbox, account: account) } + let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: agent) } + + describe 'GET /api/v1/accounts/{account.id}/reporting_events' do + context 'when it is an unauthenticated user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/reporting_events", + as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated normal agent user' do + it 'returns unauthorized' do + get "/api/v1/accounts/#{account.id}/reporting_events", + headers: agent.create_new_auth_token, + as: :json + expect(response).to have_http_status(:unauthorized) + end + end + + context 'when it is an authenticated admin user' do + before do + create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'first_response', + value: 120, + created_at: 3.days.ago) + create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'resolution', + value: 300, + created_at: 2.days.ago) + create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: 'reply_time', + value: 45, + created_at: 1.day.ago) + end + + it 'fetches reporting events with pagination' do + get "/api/v1/accounts/#{account.id}/reporting_events", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + # Check structure and pagination + expect(json_response).to have_key('payload') + expect(json_response).to have_key('meta') + expect(json_response['meta']['count']).to eq(3) + + # Check events are sorted by created_at desc (newest first) + events = json_response['payload'] + expect(events.size).to eq(3) + expect(events.first['name']).to eq('reply_time') + expect(events.last['name']).to eq('first_response') + end + + it 'filters reporting events by date range using since and until' do + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { since: 2.5.days.ago.to_time.to_i.to_s, until: 1.5.days.ago.to_time.to_i.to_s }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['meta']['count']).to eq(1) + expect(json_response['payload'].first['name']).to eq('resolution') + end + + it 'filters reporting events by inbox_id' do + other_inbox = create(:inbox, account: account) + other_conversation = create(:conversation, account: account, inbox: other_inbox) + create(:reporting_event, + account: account, + conversation: other_conversation, + inbox: other_inbox, + user: agent, + name: 'other_inbox_event') + + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { inbox_id: inbox.id }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['meta']['count']).to eq(3) + expect(json_response['payload'].map { |e| e['name'] }).not_to include('other_inbox_event') + end + + it 'filters reporting events by user_id (agent)' do + other_agent = create(:user, account: account, role: :agent) + create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: other_agent, + name: 'other_agent_event') + + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { user_id: agent.id }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['meta']['count']).to eq(3) + expect(json_response['payload'].map { |e| e['name'] }).not_to include('other_agent_event') + end + + it 'filters reporting events by name' do + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { name: 'first_response' }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['meta']['count']).to eq(1) + expect(json_response['payload'].first['name']).to eq('first_response') + end + + it 'supports combining multiple filters' do + # Create more test data + other_conversation = create(:conversation, account: account, inbox: inbox, assignee: agent) + create(:reporting_event, + account: account, + conversation: other_conversation, + inbox: inbox, + user: agent, + name: 'first_response', + value: 90, + created_at: 2.days.ago) + + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { + inbox_id: inbox.id, + user_id: agent.id, + name: 'first_response', + since: 4.days.ago.to_time.to_i.to_s, + until: Time.zone.now.to_time.to_i.to_s + }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['meta']['count']).to eq(2) + expect(json_response['payload'].map { |e| e['name'] }).to all(eq('first_response')) + end + + context 'with pagination' do + before do + # Create more events to test pagination + 30.times do |i| + create(:reporting_event, + account: account, + conversation: conversation, + inbox: inbox, + user: agent, + name: "event_#{i}", + created_at: i.hours.ago) + end + end + + it 'returns 25 events per page by default' do + get "/api/v1/accounts/#{account.id}/reporting_events", + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['payload'].size).to eq(25) + expect(json_response['meta']['count']).to eq(33) # 30 + 3 original events + expect(json_response['meta']['current_page']).to eq(1) + end + + it 'supports page navigation' do + get "/api/v1/accounts/#{account.id}/reporting_events", + params: { page: 2 }, + headers: admin.create_new_auth_token, + as: :json + + expect(response).to have_http_status(:success) + json_response = response.parsed_body + + expect(json_response['payload'].size).to eq(8) # Remaining events + expect(json_response['meta']['current_page']).to eq(2) + end + end + end + end +end diff --git a/spec/factories/reporting_events.rb b/spec/factories/reporting_events.rb index 1db6a87df..aff731ba1 100644 --- a/spec/factories/reporting_events.rb +++ b/spec/factories/reporting_events.rb @@ -1,11 +1,13 @@ FactoryBot.define do factory :reporting_event do - name { 'MyString' } + name { 'first_response' } value { 1.5 } value_in_business_hours { 1 } - account_id { 1 } - inbox_id { 1 } - user_id { 1 } - conversation_id { 1 } + account + inbox { association :inbox, account: account } + user { association :user, account: account } + conversation { association :conversation, account: account, inbox: inbox } + event_start_time { 2.hours.ago } + event_end_time { 1.hour.ago } end end diff --git a/swagger/definitions/index.yml b/swagger/definitions/index.yml index b244deb7f..c24831f84 100644 --- a/swagger/definitions/index.yml +++ b/swagger/definitions/index.yml @@ -248,3 +248,9 @@ contact_conversations_response: $ref: ./resource/contact_conversations_response.yml contactable_inboxes_response: $ref: ./resource/contactable_inboxes_response.yml +reporting_event: + $ref: ./resource/reporting_event.yml +reporting_event_meta: + $ref: ./resource/reporting_event_meta.yml +reporting_events_list_response: + $ref: ./resource/reporting_events_list_response.yml diff --git a/swagger/definitions/resource/reporting_event.yml b/swagger/definitions/resource/reporting_event.yml new file mode 100644 index 000000000..85cf71b8c --- /dev/null +++ b/swagger/definitions/resource/reporting_event.yml @@ -0,0 +1,47 @@ +type: object +properties: + id: + type: number + description: ID of the reporting event + name: + type: string + description: Name of the event (e.g., first_response, resolution, reply_time) + value: + type: number + format: double + description: Value of the metric in seconds + value_in_business_hours: + type: number + format: double + description: Value of the metric in seconds, calculated only for business hours + event_start_time: + type: string + format: date-time + description: The timestamp when the event started + event_end_time: + type: string + format: date-time + description: The timestamp when the event ended + account_id: + type: number + description: ID of the account + conversation_id: + type: number + nullable: true + description: ID of the conversation + inbox_id: + type: number + nullable: true + description: ID of the inbox + user_id: + type: number + nullable: true + description: ID of the user/agent + created_at: + type: string + format: date-time + description: The timestamp when the reporting event was created + updated_at: + type: string + format: date-time + description: The timestamp when the reporting event was last updated diff --git a/swagger/definitions/resource/reporting_event_meta.yml b/swagger/definitions/resource/reporting_event_meta.yml new file mode 100644 index 000000000..5d2646c51 --- /dev/null +++ b/swagger/definitions/resource/reporting_event_meta.yml @@ -0,0 +1,11 @@ +type: object +properties: + count: + type: integer + description: Total number of reporting events + current_page: + type: integer + description: Current page number + total_pages: + type: integer + description: Total number of pages diff --git a/swagger/definitions/resource/reporting_events_list_response.yml b/swagger/definitions/resource/reporting_events_list_response.yml new file mode 100644 index 000000000..847c5336a --- /dev/null +++ b/swagger/definitions/resource/reporting_events_list_response.yml @@ -0,0 +1,10 @@ +type: object +properties: + meta: + $ref: '#/components/schemas/reporting_event_meta' + description: Metadata about the reporting events list response + payload: + type: array + items: + $ref: '#/components/schemas/reporting_event' + description: List of reporting events diff --git a/swagger/paths/application/conversation/reporting_events.yml b/swagger/paths/application/conversation/reporting_events.yml new file mode 100644 index 000000000..f19ccbe8b --- /dev/null +++ b/swagger/paths/application/conversation/reporting_events.yml @@ -0,0 +1,29 @@ +tags: + - Conversations +operationId: get-conversation-reporting-events +summary: Conversation Reporting Events +security: + - userApiKey: [] +description: Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order. +responses: + '200': + description: Success + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/reporting_event' + description: Array of reporting events for the conversation + '403': + description: Access denied + content: + application/json: + schema: + $ref: '#/components/schemas/bad_request_error' + '404': + description: Conversation not found + content: + application/json: + schema: + $ref: '#/components/schemas/bad_request_error' diff --git a/swagger/paths/application/reporting_events/index.yml b/swagger/paths/application/reporting_events/index.yml new file mode 100644 index 000000000..d55c99545 --- /dev/null +++ b/swagger/paths/application/reporting_events/index.yml @@ -0,0 +1,47 @@ +tags: + - Reports +operationId: get-account-reporting-events +summary: Account Reporting Events +security: + - userApiKey: [] +description: Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page. +parameters: + - $ref: '#/components/parameters/page' + - in: query + name: since + schema: + type: string + description: The timestamp from where events should start (Unix timestamp in seconds) + - in: query + name: until + schema: + type: string + description: The timestamp from where events should stop (Unix timestamp in seconds) + - in: query + name: inbox_id + schema: + type: number + description: Filter events by inbox ID + - in: query + name: user_id + schema: + type: number + description: Filter events by user/agent ID + - in: query + name: name + schema: + type: string + description: Filter events by event name (e.g., first_response, resolution, reply_time) +responses: + '200': + description: Success + content: + application/json: + schema: + $ref: '#/components/schemas/reporting_events_list_response' + '403': + description: Access denied - Only administrators can access this endpoint + content: + application/json: + schema: + $ref: '#/components/schemas/bad_request_error' diff --git a/swagger/paths/index.yml b/swagger/paths/index.yml index b2fcb4594..d5126a8d1 100644 --- a/swagger/paths/index.yml +++ b/swagger/paths/index.yml @@ -389,6 +389,15 @@ post: $ref: ./application/conversation/labels/create.yml +# Conversation Reporting Events + +/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events: + parameters: + - $ref: '#/components/parameters/account_id' + - $ref: '#/components/parameters/conversation_id' + get: + $ref: ./application/conversation/reporting_events.yml + # Inboxes /api/v1/accounts/{account_id}/inboxes: $ref: ./application/inboxes/index.yml @@ -535,6 +544,13 @@ ### Reports +# Account Reporting Events +/api/v1/accounts/{account_id}/reporting_events: + parameters: + - $ref: '#/components/parameters/account_id' + get: + $ref: ./application/reporting_events/index.yml + # List /api/v2/accounts/{account_id}/reports: parameters: diff --git a/swagger/swagger.json b/swagger/swagger.json index 9bb9ff95a..7a3e5b02d 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -5140,6 +5140,65 @@ } } }, + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events": { + "parameters": [ + { + "$ref": "#/components/parameters/account_id" + }, + { + "$ref": "#/components/parameters/conversation_id" + } + ], + "get": { + "tags": [ + "Conversations" + ], + "operationId": "get-conversation-reporting-events", + "summary": "Conversation Reporting Events", + "security": [ + { + "userApiKey": [] + } + ], + "description": "Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order.", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "Array of reporting events for the conversation" + } + } + } + }, + "403": { + "description": "Access denied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + }, + "404": { + "description": "Conversation not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + } + } + } + }, "/api/v1/accounts/{account_id}/inboxes": { "get": { "tags": [ @@ -7311,6 +7370,93 @@ } } }, + "/api/v1/accounts/{account_id}/reporting_events": { + "parameters": [ + { + "$ref": "#/components/parameters/account_id" + } + ], + "get": { + "tags": [ + "Reports" + ], + "operationId": "get-account-reporting-events", + "summary": "Account Reporting Events", + "security": [ + { + "userApiKey": [] + } + ], + "description": "Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page.", + "parameters": [ + { + "$ref": "#/components/parameters/page" + }, + { + "in": "query", + "name": "since", + "schema": { + "type": "string" + }, + "description": "The timestamp from where events should start (Unix timestamp in seconds)" + }, + { + "in": "query", + "name": "until", + "schema": { + "type": "string" + }, + "description": "The timestamp from where events should stop (Unix timestamp in seconds)" + }, + { + "in": "query", + "name": "inbox_id", + "schema": { + "type": "number" + }, + "description": "Filter events by inbox ID" + }, + { + "in": "query", + "name": "user_id", + "schema": { + "type": "number" + }, + "description": "Filter events by user/agent ID" + }, + { + "in": "query", + "name": "name", + "schema": { + "type": "string" + }, + "description": "Filter events by event name (e.g., first_response, resolution, reply_time)" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/reporting_events_list_response" + } + } + } + }, + "403": { + "description": "Access denied - Only administrators can access this endpoint", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + } + } + } + }, "/api/v2/accounts/{account_id}/reports": { "parameters": [ { @@ -12066,6 +12212,100 @@ "description": "List of contactable inboxes for the contact" } } + }, + "reporting_event": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the reporting event" + }, + "name": { + "type": "string", + "description": "Name of the event (e.g., first_response, resolution, reply_time)" + }, + "value": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds" + }, + "value_in_business_hours": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds, calculated only for business hours" + }, + "event_start_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event started" + }, + "event_end_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event ended" + }, + "account_id": { + "type": "number", + "description": "ID of the account" + }, + "conversation_id": { + "type": "number", + "nullable": true, + "description": "ID of the conversation" + }, + "inbox_id": { + "type": "number", + "nullable": true, + "description": "ID of the inbox" + }, + "user_id": { + "type": "number", + "nullable": true, + "description": "ID of the user/agent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was created" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was last updated" + } + } + }, + "reporting_event_meta": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of reporting events" + }, + "current_page": { + "type": "integer", + "description": "Current page number" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + } + } + }, + "reporting_events_list_response": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/reporting_event_meta" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "List of reporting events" + } + } } }, "parameters": { diff --git a/swagger/tag_groups/application_swagger.json b/swagger/tag_groups/application_swagger.json index 600df4597..72902b3b8 100644 --- a/swagger/tag_groups/application_swagger.json +++ b/swagger/tag_groups/application_swagger.json @@ -3683,6 +3683,65 @@ } } }, + "/api/v1/accounts/{account_id}/conversations/{conversation_id}/reporting_events": { + "parameters": [ + { + "$ref": "#/components/parameters/account_id" + }, + { + "$ref": "#/components/parameters/conversation_id" + } + ], + "get": { + "tags": [ + "Conversations" + ], + "operationId": "get-conversation-reporting-events", + "summary": "Conversation Reporting Events", + "security": [ + { + "userApiKey": [] + } + ], + "description": "Get reporting events for a specific conversation. This endpoint returns events such as first response time, resolution time, and other metrics for the conversation, sorted by creation time in ascending order.", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "Array of reporting events for the conversation" + } + } + } + }, + "403": { + "description": "Access denied", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + }, + "404": { + "description": "Conversation not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + } + } + } + }, "/api/v1/accounts/{account_id}/inboxes": { "get": { "tags": [ @@ -5854,6 +5913,93 @@ } } }, + "/api/v1/accounts/{account_id}/reporting_events": { + "parameters": [ + { + "$ref": "#/components/parameters/account_id" + } + ], + "get": { + "tags": [ + "Reports" + ], + "operationId": "get-account-reporting-events", + "summary": "Account Reporting Events", + "security": [ + { + "userApiKey": [] + } + ], + "description": "Get paginated reporting events for the account. This endpoint returns reporting events such as first response time, resolution time, and other metrics. Only administrators can access this endpoint. Results are paginated with 25 items per page.", + "parameters": [ + { + "$ref": "#/components/parameters/page" + }, + { + "in": "query", + "name": "since", + "schema": { + "type": "string" + }, + "description": "The timestamp from where events should start (Unix timestamp in seconds)" + }, + { + "in": "query", + "name": "until", + "schema": { + "type": "string" + }, + "description": "The timestamp from where events should stop (Unix timestamp in seconds)" + }, + { + "in": "query", + "name": "inbox_id", + "schema": { + "type": "number" + }, + "description": "Filter events by inbox ID" + }, + { + "in": "query", + "name": "user_id", + "schema": { + "type": "number" + }, + "description": "Filter events by user/agent ID" + }, + { + "in": "query", + "name": "name", + "schema": { + "type": "string" + }, + "description": "Filter events by event name (e.g., first_response, resolution, reply_time)" + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/reporting_events_list_response" + } + } + } + }, + "403": { + "description": "Access denied - Only administrators can access this endpoint", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/bad_request_error" + } + } + } + } + } + } + }, "/api/v2/accounts/{account_id}/reports": { "parameters": [ { @@ -10573,6 +10719,100 @@ "description": "List of contactable inboxes for the contact" } } + }, + "reporting_event": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the reporting event" + }, + "name": { + "type": "string", + "description": "Name of the event (e.g., first_response, resolution, reply_time)" + }, + "value": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds" + }, + "value_in_business_hours": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds, calculated only for business hours" + }, + "event_start_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event started" + }, + "event_end_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event ended" + }, + "account_id": { + "type": "number", + "description": "ID of the account" + }, + "conversation_id": { + "type": "number", + "nullable": true, + "description": "ID of the conversation" + }, + "inbox_id": { + "type": "number", + "nullable": true, + "description": "ID of the inbox" + }, + "user_id": { + "type": "number", + "nullable": true, + "description": "ID of the user/agent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was created" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was last updated" + } + } + }, + "reporting_event_meta": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of reporting events" + }, + "current_page": { + "type": "integer", + "description": "Current page number" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + } + } + }, + "reporting_events_list_response": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/reporting_event_meta" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "List of reporting events" + } + } } }, "parameters": { diff --git a/swagger/tag_groups/client_swagger.json b/swagger/tag_groups/client_swagger.json index 16c1d5bc7..6d8b7701d 100644 --- a/swagger/tag_groups/client_swagger.json +++ b/swagger/tag_groups/client_swagger.json @@ -5019,6 +5019,100 @@ "description": "List of contactable inboxes for the contact" } } + }, + "reporting_event": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the reporting event" + }, + "name": { + "type": "string", + "description": "Name of the event (e.g., first_response, resolution, reply_time)" + }, + "value": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds" + }, + "value_in_business_hours": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds, calculated only for business hours" + }, + "event_start_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event started" + }, + "event_end_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event ended" + }, + "account_id": { + "type": "number", + "description": "ID of the account" + }, + "conversation_id": { + "type": "number", + "nullable": true, + "description": "ID of the conversation" + }, + "inbox_id": { + "type": "number", + "nullable": true, + "description": "ID of the inbox" + }, + "user_id": { + "type": "number", + "nullable": true, + "description": "ID of the user/agent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was created" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was last updated" + } + } + }, + "reporting_event_meta": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of reporting events" + }, + "current_page": { + "type": "integer", + "description": "Current page number" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + } + } + }, + "reporting_events_list_response": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/reporting_event_meta" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "List of reporting events" + } + } } }, "parameters": { diff --git a/swagger/tag_groups/other_swagger.json b/swagger/tag_groups/other_swagger.json index c8a5294d3..9e8c57c04 100644 --- a/swagger/tag_groups/other_swagger.json +++ b/swagger/tag_groups/other_swagger.json @@ -4434,6 +4434,100 @@ "description": "List of contactable inboxes for the contact" } } + }, + "reporting_event": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the reporting event" + }, + "name": { + "type": "string", + "description": "Name of the event (e.g., first_response, resolution, reply_time)" + }, + "value": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds" + }, + "value_in_business_hours": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds, calculated only for business hours" + }, + "event_start_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event started" + }, + "event_end_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event ended" + }, + "account_id": { + "type": "number", + "description": "ID of the account" + }, + "conversation_id": { + "type": "number", + "nullable": true, + "description": "ID of the conversation" + }, + "inbox_id": { + "type": "number", + "nullable": true, + "description": "ID of the inbox" + }, + "user_id": { + "type": "number", + "nullable": true, + "description": "ID of the user/agent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was created" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was last updated" + } + } + }, + "reporting_event_meta": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of reporting events" + }, + "current_page": { + "type": "integer", + "description": "Current page number" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + } + } + }, + "reporting_events_list_response": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/reporting_event_meta" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "List of reporting events" + } + } } }, "parameters": { diff --git a/swagger/tag_groups/platform_swagger.json b/swagger/tag_groups/platform_swagger.json index f816b8c94..7c79f1c5b 100644 --- a/swagger/tag_groups/platform_swagger.json +++ b/swagger/tag_groups/platform_swagger.json @@ -5195,6 +5195,100 @@ "description": "List of contactable inboxes for the contact" } } + }, + "reporting_event": { + "type": "object", + "properties": { + "id": { + "type": "number", + "description": "ID of the reporting event" + }, + "name": { + "type": "string", + "description": "Name of the event (e.g., first_response, resolution, reply_time)" + }, + "value": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds" + }, + "value_in_business_hours": { + "type": "number", + "format": "double", + "description": "Value of the metric in seconds, calculated only for business hours" + }, + "event_start_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event started" + }, + "event_end_time": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the event ended" + }, + "account_id": { + "type": "number", + "description": "ID of the account" + }, + "conversation_id": { + "type": "number", + "nullable": true, + "description": "ID of the conversation" + }, + "inbox_id": { + "type": "number", + "nullable": true, + "description": "ID of the inbox" + }, + "user_id": { + "type": "number", + "nullable": true, + "description": "ID of the user/agent" + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was created" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "description": "The timestamp when the reporting event was last updated" + } + } + }, + "reporting_event_meta": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Total number of reporting events" + }, + "current_page": { + "type": "integer", + "description": "Current page number" + }, + "total_pages": { + "type": "integer", + "description": "Total number of pages" + } + } + }, + "reporting_events_list_response": { + "type": "object", + "properties": { + "meta": { + "$ref": "#/components/schemas/reporting_event_meta" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/components/schemas/reporting_event" + }, + "description": "List of reporting events" + } + } } }, "parameters": { From bdcb1934c00db407140c5444104b57201e3d430a Mon Sep 17 00:00:00 2001 From: Gabriel Jablonski Date: Thu, 13 Nov 2025 04:58:15 -0300 Subject: [PATCH 3/3] feat(webhooks): add name to webhook (#12641) ## Description When working with webhooks, it's easy to lose track of which URL is which. Adding a `name` (optional) column to the webhook model is a straight-forward solution to make it significantly easier to identify webhooks. ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Model and controller specs, and also running in production over several months without any issues. | Before | After | | --- | --- | | image copy 3 | image | | image copy 2 | image copy | ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules --- .../api/v1/accounts/webhooks_controller.rb | 2 +- .../dashboard/i18n/locale/en/integrations.json | 4 ++++ .../integrations/Webhooks/WebhookForm.vue | 14 ++++++++++++++ .../settings/integrations/Webhooks/WebhookRow.vue | 12 ++++++++++-- app/models/webhook.rb | 1 + .../v1/accounts/webhooks/_webhook.json.jbuilder | 1 + db/migrate/20251010143218_add_name_to_webhooks.rb | 5 +++++ db/schema.rb | 1 + .../api/v1/accounts/webhook_controller_spec.rb | 15 +++++++++++++-- spec/factories/webhooks.rb | 1 + .../request/webhooks/create_update_payload.yml | 3 +++ swagger/definitions/resource/webhook.yml | 3 +++ swagger/swagger.json | 8 ++++++++ swagger/tag_groups/application_swagger.json | 8 ++++++++ swagger/tag_groups/client_swagger.json | 8 ++++++++ swagger/tag_groups/other_swagger.json | 8 ++++++++ swagger/tag_groups/platform_swagger.json | 8 ++++++++ 17 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20251010143218_add_name_to_webhooks.rb diff --git a/app/controllers/api/v1/accounts/webhooks_controller.rb b/app/controllers/api/v1/accounts/webhooks_controller.rb index 7ea257ed2..9f8e94821 100644 --- a/app/controllers/api/v1/accounts/webhooks_controller.rb +++ b/app/controllers/api/v1/accounts/webhooks_controller.rb @@ -23,7 +23,7 @@ class Api::V1::Accounts::WebhooksController < Api::V1::Accounts::BaseController private def webhook_params - params.require(:webhook).permit(:inbox_id, :url, subscriptions: []) + params.require(:webhook).permit(:inbox_id, :name, :url, subscriptions: []) end def fetch_webhook diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index 78804220d..f74e674d7 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -46,6 +46,10 @@ "CONVERSATION_TYPING_OFF": "Conversation Typing Off" } }, + "NAME": { + "LABEL": "Webhook Name", + "PLACEHOLDER": "Enter the name of the webhook" + }, "END_POINT": { "LABEL": "Webhook URL", "PLACEHOLDER": "Example: {webhookExampleURL}", diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue index 430658329..3f4b31299 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue @@ -55,6 +55,7 @@ export default { data() { return { url: this.value.url || '', + name: this.value.name || '', subscriptions: this.value.subscriptions || [], supportedWebhookEvents: SUPPORTED_WEBHOOK_EVENTS, }; @@ -68,11 +69,15 @@ export default { } ); }, + webhookNameInputPlaceholder() { + return this.$t('INTEGRATION_SETTINGS.WEBHOOK.FORM.NAME.PLACEHOLDER'); + }, }, methods: { onSubmit() { this.$emit('submit', { url: this.url, + name: this.name, subscriptions: this.subscriptions, }); }, @@ -97,6 +102,15 @@ export default { {{ $t('INTEGRATION_SETTINGS.WEBHOOK.FORM.END_POINT.ERROR') }} + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue index e2bf986e0..3dfb177e5 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue @@ -37,8 +37,16 @@ const subscribedEvents = computed(() => {