From 899176a793c43572fd7fda9c52a99af78baa10bc Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Mon, 25 Apr 2022 17:44:42 +0530 Subject: [PATCH] feat: Add event subscription option to webhooks (#4540) Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> Co-authored-by: Muhsin Keloth --- .../api/v1/accounts/webhooks_controller.rb | 2 +- .../assets/scss/_utility-helpers.scss | 4 + .../dashboard/components/widgets/ShowMore.vue | 50 ++++++++ .../i18n/locale/en/integrations.json | 52 ++++---- .../dashboard/i18n/locale/en/settings.json | 4 + .../settings/integrations/EditWebHook.vue | 108 ---------------- .../settings/integrations/NewWebHook.vue | 121 ------------------ .../integrations/Webhooks/EditWebHook.vue | 61 +++++++++ .../{Webhook.vue => Webhooks/Index.vue} | 60 +++------ .../integrations/Webhooks/NewWebHook.vue | 59 +++++++++ .../integrations/Webhooks/WebhookForm.vue | 108 ++++++++++++++++ .../integrations/Webhooks/WebhookRow.vue | 83 ++++++++++++ .../Webhooks/specs/webhookMixin.spec.js | 26 ++++ .../integrations/Webhooks/webhookMixin.js | 10 ++ .../integrations/integrations.routes.js | 2 +- .../dashboard/store/modules/webhooks.js | 2 +- app/listeners/webhook_listener.rb | 30 ++--- app/models/webhook.rb | 29 +++-- .../accounts/webhooks/_webhook.json.jbuilder | 1 + config/locales/en.yml | 2 + ...424081117_add_subscriptions_to_webhooks.rb | 12 ++ db/schema.rb | 3 +- .../v1/accounts/webhook_controller_spec.rb | 30 +++++ spec/factories/webhooks.rb | 10 ++ spec/listeners/webhook_listener_spec.rb | 42 ++---- 25 files changed, 552 insertions(+), 359 deletions(-) create mode 100644 app/javascript/dashboard/components/widgets/ShowMore.vue delete mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue delete mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/EditWebHook.vue rename app/javascript/dashboard/routes/dashboard/settings/integrations/{Webhook.vue => Webhooks/Index.vue} (73%) create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/NewWebHook.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/specs/webhookMixin.spec.js create mode 100644 app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/webhookMixin.js create mode 100644 db/migrate/20220424081117_add_subscriptions_to_webhooks.rb diff --git a/app/controllers/api/v1/accounts/webhooks_controller.rb b/app/controllers/api/v1/accounts/webhooks_controller.rb index 0add18047..7ea257ed2 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) + params.require(:webhook).permit(:inbox_id, :url, subscriptions: []) end def fetch_webhook diff --git a/app/javascript/dashboard/assets/scss/_utility-helpers.scss b/app/javascript/dashboard/assets/scss/_utility-helpers.scss index abea0e28c..71977cf2b 100644 --- a/app/javascript/dashboard/assets/scss/_utility-helpers.scss +++ b/app/javascript/dashboard/assets/scss/_utility-helpers.scss @@ -2,6 +2,10 @@ margin-right: var(--space-small); } +.margin-bottom-small { + margin-bottom: var(--space-small); +} + .margin-right-smaller { margin-right: var(--space-smaller); } diff --git a/app/javascript/dashboard/components/widgets/ShowMore.vue b/app/javascript/dashboard/components/widgets/ShowMore.vue new file mode 100644 index 000000000..c4755aed9 --- /dev/null +++ b/app/javascript/dashboard/components/widgets/ShowMore.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/javascript/dashboard/i18n/locale/en/integrations.json b/app/javascript/dashboard/i18n/locale/en/integrations.json index 4562183c7..2094f269b 100644 --- a/app/javascript/dashboard/i18n/locale/en/integrations.json +++ b/app/javascript/dashboard/i18n/locale/en/integrations.json @@ -2,6 +2,29 @@ "INTEGRATION_SETTINGS": { "HEADER": "Integrations", "WEBHOOK": { + "SUBSCRIBED_EVENTS": "Subscribed Events", + "FORM": { + "CANCEL": "Cancel", + "DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.", + "SUBSCRIPTIONS": { + "LABEL": "Events", + "EVENTS": { + "CONVERSATION_CREATED": "Conversation Created", + "CONVERSATION_STATUS_CHANGED": "Conversation Status Changed", + "CONVERSATION_UPDATED": "Conversation Updated", + "MESSAGE_CREATED": "Message created", + "MESSAGE_UPDATED": "Message updated", + "WEBWIDGET_TRIGGERED": "Live chat widget opened by the user" + } + }, + "END_POINT": { + "LABEL": "Webhook URL", + "PLACEHOLDER": "Example: https://example/api/webhook", + "ERROR": "Please enter a valid URL" + }, + "EDIT_SUBMIT": "Update webhook", + "ADD_SUBMIT": "Create webhook" + }, "TITLE": "Webhook", "CONFIGURE": "Configure", "HEADER": "Webhook settings", @@ -17,35 +40,16 @@ "EDIT": { "BUTTON_TEXT": "Edit", "TITLE": "Edit webhook", - "CANCEL": "Cancel", - "DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.", - "FORM": { - "END_POINT": { - "LABEL": "Webhook URL", - "PLACEHOLDER": "Example: https://example/api/webhook", - "ERROR": "Please enter a valid URL" - }, - "SUBMIT": "Edit webhook" - }, "API": { - "SUCCESS_MESSAGE": "Webhook URL updated successfully", + "SUCCESS_MESSAGE": "Webhook configuration updated successfully", "ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later" } }, "ADD": { "CANCEL": "Cancel", "TITLE": "Add new webhook", - "DESC": "Webhook events provide you the realtime information about what's happening in your Chatwoot account. Please enter a valid URL to configure a callback.", - "FORM": { - "END_POINT": { - "LABEL": "Webhook URL", - "PLACEHOLDER": "Example: https://example/api/webhook", - "ERROR": "Please enter a valid URL" - }, - "SUBMIT": "Create webhook" - }, "API": { - "SUCCESS_MESSAGE": "Webhook added successfully", + "SUCCESS_MESSAGE": "Webhook configuration added successfully", "ERROR_MESSAGE": "Could not connect to Woot Server, Please try again later" } }, @@ -57,16 +61,16 @@ }, "CONFIRM": { "TITLE": "Confirm Deletion", - "MESSAGE": "Are you sure to delete ", + "MESSAGE": "Are you sure to delete the webhook? (%{webhookURL})", "YES": "Yes, Delete ", "NO": "No, Keep it" } } }, "SLACK": { - "HELP_TEXT" : { + "HELP_TEXT" : { "TITLE": "Using Slack Integration", - "BODY": "

Chatwoot will now sync all the incoming conversations into the customer-conversations channel inside your slack workplace.

Replying to a conversation thread in customer-conversations slack channel will create a response back to the customer through chatwoot.

Start the replies with note: to create private notes instead of replies.

If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.

When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.

" + "BODY": "

Chatwoot will now sync all the incoming conversations into the customer-conversations channel inside your slack workplace.

Replying to a conversation thread in customer-conversations slack channel will create a response back to the customer through chatwoot.

Start the replies with note: to create private notes instead of replies.

If the replier on slack has an agent profile in chatwoot under the same email, the replies will be associated accordingly.

When the replier doesn't have an associated agent profile, the replies will be made from the bot profile.

" } }, "DELETE": { diff --git a/app/javascript/dashboard/i18n/locale/en/settings.json b/app/javascript/dashboard/i18n/locale/en/settings.json index 5727e09d0..63b070b0e 100644 --- a/app/javascript/dashboard/i18n/locale/en/settings.json +++ b/app/javascript/dashboard/i18n/locale/en/settings.json @@ -127,6 +127,10 @@ "BUTTON_TEXT": "Copy", "COPY_SUCCESSFUL": "Code copied to clipboard successfully" }, + "SHOW_MORE_BLOCK": { + "SHOW_MORE": "Show More", + "SHOW_LESS": "Show Less" + }, "FILE_BUBBLE": { "DOWNLOAD": "Download", "UPLOADING": "Uploading..." diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue deleted file mode 100644 index cdfa2998a..000000000 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/EditWebHook.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue deleted file mode 100644 index 35e3df04d..000000000 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/NewWebHook.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/EditWebHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/EditWebHook.vue new file mode 100644 index 000000000..624a41d05 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/EditWebHook.vue @@ -0,0 +1,61 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/Index.vue similarity index 73% rename from app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue rename to app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/Index.vue index e5bb621f0..8bd36c0a8 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhook.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/Index.vue @@ -4,7 +4,7 @@ color-scheme="success" class-names="button--fixed-right-top" icon="add-circle" - @click="openAddPopup()" + @click="openAddPopup" > {{ $t('INTEGRATION_SETTINGS.WEBHOOK.HEADER_BTN_TXT') }} @@ -37,35 +37,14 @@ - - - {{ webHookItem.url }} - - - - - - - - + @@ -83,24 +62,27 @@ - + - @@ -112,11 +94,13 @@ import NewWebhook from './NewWebHook'; import EditWebhook from './EditWebHook'; import alertMixin from 'shared/mixins/alertMixin'; import globalConfigMixin from 'shared/mixins/globalConfigMixin'; +import WebhookRow from './WebhookRow'; export default { components: { NewWebhook, EditWebhook, + WebhookRow, }, mixins: [alertMixin, globalConfigMixin], data() { @@ -179,11 +163,3 @@ export default { }, }; - diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/NewWebHook.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/NewWebHook.vue new file mode 100644 index 000000000..7c90241c4 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/NewWebHook.vue @@ -0,0 +1,59 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue new file mode 100644 index 000000000..3d6be7ae0 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookForm.vue @@ -0,0 +1,108 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue new file mode 100644 index 000000000..0e754d93d --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/WebhookRow.vue @@ -0,0 +1,83 @@ + + + diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/specs/webhookMixin.spec.js b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/specs/webhookMixin.spec.js new file mode 100644 index 000000000..c617b64b0 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/specs/webhookMixin.spec.js @@ -0,0 +1,26 @@ +import { createWrapper } from '@vue/test-utils'; +import webhookMixin from '../webhookMixin'; +import Vue from 'vue'; + +describe('webhookMixin', () => { + describe('#getEventLabel', () => { + it('returns correct i18n translation:', () => { + const Component = { + render() {}, + title: 'WebhookComponent', + mixins: [webhookMixin], + methods: { + $t(text) { + return text; + }, + }, + }; + const Constructor = Vue.extend(Component); + const vm = new Constructor().$mount(); + const wrapper = createWrapper(vm); + expect(wrapper.vm.getEventLabel('message_created')).toEqual( + `INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.MESSAGE_CREATED` + ); + }); + }); +}); diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/webhookMixin.js b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/webhookMixin.js new file mode 100644 index 000000000..38283fe90 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/Webhooks/webhookMixin.js @@ -0,0 +1,10 @@ +export default { + methods: { + getEventLabel(event) { + const eventName = event.toUpperCase(); + return this.$t( + `INTEGRATION_SETTINGS.WEBHOOK.FORM.SUBSCRIPTIONS.EVENTS.${eventName}` + ); + }, + }, +}; diff --git a/app/javascript/dashboard/routes/dashboard/settings/integrations/integrations.routes.js b/app/javascript/dashboard/routes/dashboard/settings/integrations/integrations.routes.js index 36596ad0e..4240062e6 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/integrations/integrations.routes.js +++ b/app/javascript/dashboard/routes/dashboard/settings/integrations/integrations.routes.js @@ -1,6 +1,6 @@ import Index from './Index'; import SettingsContent from '../Wrapper'; -import Webhook from './Webhook'; +import Webhook from './Webhooks/Index'; import ShowIntegration from './ShowIntegration'; import { frontendURL } from '../../../../helper/URLHelper'; diff --git a/app/javascript/dashboard/store/modules/webhooks.js b/app/javascript/dashboard/store/modules/webhooks.js index 11c0dec34..eb096468e 100644 --- a/app/javascript/dashboard/store/modules/webhooks.js +++ b/app/javascript/dashboard/store/modules/webhooks.js @@ -14,7 +14,7 @@ const state = { export const getters = { getWebhooks(_state) { - return _state.records; + return _state.records.sort((w1, w2) => w1.id - w2.id); }, getUIFlags(_state) { return _state.uiFlags; diff --git a/app/listeners/webhook_listener.rb b/app/listeners/webhook_listener.rb index b0629a469..11a86e639 100644 --- a/app/listeners/webhook_listener.rb +++ b/app/listeners/webhook_listener.rb @@ -1,22 +1,4 @@ class WebhookListener < BaseListener - # FIXME: deprecate the opened and resolved events in future in favor of status changed event. - def conversation_resolved(event) - conversation = extract_conversation_and_account(event)[0] - changed_attributes = extract_changed_attributes(event) - inbox = conversation.inbox - payload = conversation.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes) - deliver_webhook_payloads(payload, inbox) - end - - # FIXME: deprecate the opened and resolved events in future in favor of status changed event. - def conversation_opened(event) - conversation = extract_conversation_and_account(event)[0] - changed_attributes = extract_changed_attributes(event) - inbox = conversation.inbox - payload = conversation.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes) - deliver_webhook_payloads(payload, inbox) - end - def conversation_status_changed(event) conversation = extract_conversation_and_account(event)[0] changed_attributes = extract_changed_attributes(event) @@ -71,15 +53,23 @@ class WebhookListener < BaseListener private - def deliver_webhook_payloads(payload, inbox) - # Account webhooks + def deliver_account_webhooks(payload, inbox) inbox.account.webhooks.account.each do |webhook| + next unless webhook.subscriptions.include?(payload[:event]) + WebhookJob.perform_later(webhook.url, payload) end + end + def deliver_api_inbox_webhooks(payload, inbox) return unless inbox.channel_type == 'Channel::Api' return if inbox.channel.webhook_url.blank? WebhookJob.perform_later(inbox.channel.webhook_url, payload) end + + def deliver_webhook_payloads(payload, inbox) + deliver_account_webhooks(payload, inbox) + deliver_api_inbox_webhooks(payload, inbox) + end end diff --git a/app/models/webhook.rb b/app/models/webhook.rb index a78e7c267..fe97fe583 100644 --- a/app/models/webhook.rb +++ b/app/models/webhook.rb @@ -2,13 +2,14 @@ # # Table name: webhooks # -# id :bigint not null, primary key -# url :string -# webhook_type :integer default("account") -# created_at :datetime not null -# updated_at :datetime not null -# account_id :integer -# inbox_id :integer +# id :bigint not null, primary key +# subscriptions :jsonb +# url :string +# webhook_type :integer default("account") +# created_at :datetime not null +# updated_at :datetime not null +# account_id :integer +# inbox_id :integer # # Indexes # @@ -21,6 +22,18 @@ class Webhook < ApplicationRecord validates :account_id, presence: true validates :url, uniqueness: { scope: [:account_id] }, format: URI::DEFAULT_PARSER.make_regexp(%w[http https]) - + validate :validate_webhook_subscriptions enum webhook_type: { account: 0, inbox: 1 } + + ALLOWED_WEBHOOK_EVENTS = %w[conversation_status_changed conversation_updated conversation_created message_created message_updated + webwidget_triggered].freeze + + private + + def validate_webhook_subscriptions + invalid_subscriptions = !subscriptions.instance_of?(Array) || + subscriptions.blank? || + (subscriptions.uniq - ALLOWED_WEBHOOK_EVENTS).length.positive? + errors.add(:subscriptions, I18n.t('errors.webhook.invalid')) if invalid_subscriptions + end end diff --git a/app/views/api/v1/accounts/webhooks/_webhook.json.jbuilder b/app/views/api/v1/accounts/webhooks/_webhook.json.jbuilder index b02de2098..fac8fdcf2 100644 --- a/app/views/api/v1/accounts/webhooks/_webhook.json.jbuilder +++ b/app/views/api/v1/accounts/webhooks/_webhook.json.jbuilder @@ -1,6 +1,7 @@ json.id webhook.id json.url webhook.url json.account_id webhook.account_id +json.subscriptions webhook.subscriptions if webhook.inbox json.inbox do json.id webhook.inbox.id diff --git a/config/locales/en.yml b/config/locales/en.yml index 6a928e75a..0c92c32eb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -36,6 +36,8 @@ en: reset_password_failure: Uh ho! We could not find any user with the specified email. errors: + webhook: + invalid: Invalid events signup: disposable_email: We do not allow disposable emails invalid_email: You have entered an invalid email diff --git a/db/migrate/20220424081117_add_subscriptions_to_webhooks.rb b/db/migrate/20220424081117_add_subscriptions_to_webhooks.rb new file mode 100644 index 000000000..b4e8d4dfc --- /dev/null +++ b/db/migrate/20220424081117_add_subscriptions_to_webhooks.rb @@ -0,0 +1,12 @@ +class AddSubscriptionsToWebhooks < ActiveRecord::Migration[6.1] + def change + add_column :webhooks, :subscriptions, :jsonb, default: %w[ + conversation_status_changed + conversation_updated + conversation_created + message_created + message_updated + webwidget_triggered + ] + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d03ce428..a30f6e6cf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_04_18_094715) do +ActiveRecord::Schema.define(version: 2022_04_24_081117) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -763,6 +763,7 @@ ActiveRecord::Schema.define(version: 2022_04_18_094715) do t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.integer "webhook_type", default: 0 + t.jsonb "subscriptions", default: ["conversation_status_changed", "conversation_updated", "conversation_created", "message_created", "message_updated", "webwidget_triggered"] t.index ["account_id", "url"], name: "index_webhooks_on_account_id_and_url", unique: true end diff --git a/spec/controllers/api/v1/accounts/webhook_controller_spec.rb b/spec/controllers/api/v1/accounts/webhook_controller_spec.rb index ccafe2f56..aeea4d45f 100644 --- a/spec/controllers/api/v1/accounts/webhook_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/webhook_controller_spec.rb @@ -57,6 +57,36 @@ RSpec.describe 'Webhooks API', type: :request do expect(response).to have_http_status(:unprocessable_entity) expect(JSON.parse(response.body)['message']).to eql 'Url is invalid' end + + it 'throws error if subscription events are invalid' do + post "/api/v1/accounts/#{account.id}/webhooks", + params: { url: 'https://hello.com', subscriptions: ['conversation_random_event'] }, + headers: administrator.create_new_auth_token, + as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['message']).to eql 'Subscriptions Invalid events' + end + + it 'throws error if subscription events are empty' do + post "/api/v1/accounts/#{account.id}/webhooks", + params: { url: 'https://hello.com', subscriptions: [] }, + headers: administrator.create_new_auth_token, + as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(JSON.parse(response.body)['message']).to eql 'Subscriptions Invalid events' + end + + it 'use default if subscription events are nil' do + post "/api/v1/accounts/#{account.id}/webhooks", + params: { url: 'https://hello.com', subscriptions: nil }, + headers: administrator.create_new_auth_token, + as: :json + expect(response).to have_http_status(:ok) + expect( + JSON.parse(response.body)['payload']['webhook']['subscriptions'] + ).to eql %w[conversation_status_changed conversation_updated conversation_created message_created message_updated + webwidget_triggered] + end end end diff --git a/spec/factories/webhooks.rb b/spec/factories/webhooks.rb index e80ed1aeb..b49434fce 100644 --- a/spec/factories/webhooks.rb +++ b/spec/factories/webhooks.rb @@ -3,5 +3,15 @@ FactoryBot.define do account_id { 1 } inbox_id { 1 } url { 'https://api.chatwoot.com' } + subscriptions do + %w[ + conversation_status_changed + conversation_updated + conversation_created + message_created + message_updated + webwidget_triggered + ] + end end end diff --git a/spec/listeners/webhook_listener_spec.rb b/spec/listeners/webhook_listener_spec.rb index bcc990c5b..5dac1a8ec 100644 --- a/spec/listeners/webhook_listener_spec.rb +++ b/spec/listeners/webhook_listener_spec.rb @@ -23,14 +23,22 @@ describe WebhookListener do end end - context 'when webhook is configured' do - it 'triggers webhook' do + context 'when webhook is configured and event is subscribed' do + it 'triggers the webhook event' do webhook = create(:webhook, inbox: inbox, account: account) expect(WebhookJob).to receive(:perform_later).with(webhook.url, message.webhook_data.merge(event: 'message_created')).once listener.message_created(message_created_event) end end + context 'when webhook is configured and event is not subscribed' do + it 'does not trigger the webhook event' do + create(:webhook, subscriptions: ['conversation_created'], inbox: inbox, account: account) + expect(WebhookJob).not_to receive(:perform_later) + listener.message_created(message_created_event) + end + end + context 'when inbox is an API Channel' do it 'triggers webhook if webhook_url is present' do channel_api = create(:channel_api, account: account) @@ -106,36 +114,6 @@ describe WebhookListener do end end - describe '#conversation_resolved' do - let!(:conversation_resolved_event) do - Events::Base.new(event_name, Time.zone.now, conversation: conversation.reload, changed_attributes: { status: [:open, :resolved] }) - end - let(:event_name) { :'conversation.resolved' } - - context 'when webhook is not configured' do - it 'does not trigger webhook' do - expect(WebhookJob).to receive(:perform_later).exactly(0).times - listener.conversation_resolved(conversation_resolved_event) - end - end - - context 'when webhook is configured' do - it 'triggers webhook' do - webhook = create(:webhook, inbox: inbox, account: account) - - conversation.update(status: :resolved) - - expect(WebhookJob).to receive(:perform_later).with(webhook.url, - conversation.webhook_data.merge(event: 'conversation_resolved', - changed_attributes: [{ status: { - current_value: :resolved, previous_value: :open - } }])).once - - listener.conversation_resolved(conversation_resolved_event) - end - end - end - describe '#conversation_updated' do let(:custom_attributes) { { test: nil } } let!(:conversation_updated_event) do