feat: Add webhook events for contact created, updated (#6415)

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Shubham Kumar
2023-02-14 02:58:27 +05:30
committed by GitHub
parent ba69f4cd35
commit 29025759d6
13 changed files with 113 additions and 16 deletions

View File

@@ -163,7 +163,7 @@ RSpec/NamedSubject:
Enabled: false
# we should bring this down
RSpec/MultipleMemoizedHelpers:
Max: 12
Max: 14
AllCops:
NewCops: enable

View File

@@ -14,7 +14,9 @@
"CONVERSATION_UPDATED": "Conversation Updated",
"MESSAGE_CREATED": "Message created",
"MESSAGE_UPDATED": "Message updated",
"WEBWIDGET_TRIGGERED": "Live chat widget opened by the user"
"WEBWIDGET_TRIGGERED": "Live chat widget opened by the user",
"CONTACT_CREATED": "Contact created",
"CONTACT_UPDATED": "Contact update"
}
},
"END_POINT": {

View File

@@ -61,6 +61,8 @@ const SUPPORTED_WEBHOOK_EVENTS = [
'message_created',
'message_updated',
'webwidget_triggered',
'contact_created',
'contact_updated',
];
export default {

View File

@@ -51,10 +51,25 @@ class WebhookListener < BaseListener
deliver_webhook_payloads(payload, inbox)
end
def contact_created(event)
contact, account = extract_contact_and_account(event)
payload = contact.webhook_data.merge(event: __method__.to_s)
deliver_account_webhooks(payload, account)
end
def contact_updated(event)
contact, account = extract_contact_and_account(event)
changed_attributes = extract_changed_attributes(event)
return if changed_attributes.blank?
payload = contact.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes)
deliver_account_webhooks(payload, account)
end
private
def deliver_account_webhooks(payload, inbox)
inbox.account.webhooks.account_type.each do |webhook|
def deliver_account_webhooks(payload, account)
account.webhooks.account_type.each do |webhook|
next unless webhook.subscriptions.include?(payload[:event])
WebhookJob.perform_later(webhook.url, payload)
@@ -69,7 +84,7 @@ class WebhookListener < BaseListener
end
def deliver_webhook_payloads(payload, inbox)
deliver_account_webhooks(payload, inbox)
deliver_account_webhooks(payload, inbox.account)
deliver_api_inbox_webhooks(payload, inbox)
end
end

View File

@@ -121,11 +121,16 @@ class Contact < ApplicationRecord
def webhook_data
{
id: id,
name: name,
account: account.webhook_data,
additional_attributes: additional_attributes,
avatar: avatar_url,
type: 'contact',
account: account.webhook_data
custom_attributes: custom_attributes,
email: email,
id: id,
identifier: identifier,
name: name,
phone_number: phone_number,
thumbnail: avatar_url
}
end
@@ -180,7 +185,7 @@ class Contact < ApplicationRecord
end
def dispatch_update_event
Rails.configuration.dispatcher.dispatch(CONTACT_UPDATED, Time.zone.now, contact: self)
Rails.configuration.dispatcher.dispatch(CONTACT_UPDATED, Time.zone.now, contact: self, changed_attributes: previous_changes)
end
def dispatch_destroy_event

View File

@@ -25,8 +25,8 @@ class Webhook < ApplicationRecord
validate :validate_webhook_subscriptions
enum webhook_type: { account_type: 0, inbox_type: 1 }
ALLOWED_WEBHOOK_EVENTS = %w[conversation_status_changed conversation_updated conversation_created message_created message_updated
webwidget_triggered].freeze
ALLOWED_WEBHOOK_EVENTS = %w[conversation_status_changed conversation_updated conversation_created contact_created contact_updated
message_created message_updated webwidget_triggered].freeze
private

View File

@@ -0,0 +1,9 @@
class UpdateDefaultOnSubscriptionsWebhooks < ActiveRecord::Migration[6.1]
def change
change_column_default :webhooks, :subscriptions,
from: %w[conversation_status_changed conversation_updated conversation_created
message_created message_updated webwidget_triggered],
to: %w[conversation_status_changed conversation_updated conversation_created
contact_created contact_updated message_created message_updated webwidget_triggered]
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2022_12_30_113108) do
ActiveRecord::Schema.define(version: 2023_02_09_033203) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
@@ -851,7 +851,7 @@ ActiveRecord::Schema.define(version: 2022_12_30_113108) 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.jsonb "subscriptions", default: ["conversation_status_changed", "conversation_updated", "conversation_created", "contact_created", "contact_updated", "message_created", "message_updated", "webwidget_triggered"]
t.index ["account_id", "url"], name: "index_webhooks_on_account_id_and_url", unique: true
end

View File

@@ -84,8 +84,8 @@ RSpec.describe 'Webhooks API', type: :request do
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]
).to eql %w[conversation_status_changed conversation_updated conversation_created contact_created contact_updated
message_created message_updated webwidget_triggered]
end
end
end

View File

@@ -8,6 +8,8 @@ FactoryBot.define do
conversation_status_changed
conversation_updated
conversation_created
contact_created
contact_updated
message_created
message_updated
webwidget_triggered

View File

@@ -5,6 +5,7 @@ describe WebhookListener do
let(:report_identity) { Reports::UpdateAccountIdentity.new(account, Time.zone.now) }
let!(:user) { create(:user, account: account) }
let!(:inbox) { create(:inbox, account: account) }
let!(:contact) { create(:contact, account: account) }
let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user) }
let!(:message) do
create(:message, message_type: 'outgoing',
@@ -12,6 +13,7 @@ describe WebhookListener do
end
let!(:message_created_event) { Events::Base.new(event_name, Time.zone.now, message: message) }
let!(:conversation_created_event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation) }
let!(:contact_event) { Events::Base.new(event_name, Time.zone.now, contact: contact) }
describe '#message_created' do
let(:event_name) { :'message.created' }
@@ -159,4 +161,60 @@ describe WebhookListener do
end
end
end
describe '#contact_created' do
let(:event_name) { :'contact.created' }
context 'when webhook is not configured' do
it 'does not trigger webhook' do
expect(WebhookJob).to receive(:perform_later).exactly(0).times
listener.contact_created(contact_event)
end
end
context 'when webhook is configured' do
it 'triggers webhook' do
webhook = create(:webhook, account: account)
expect(WebhookJob).to receive(:perform_later).with(webhook.url, contact.webhook_data.merge(event: 'contact_created')).once
listener.contact_created(contact_event)
end
end
end
describe '#contact_updated' do
let(:event_name) { :'contact.updated' }
let!(:contact_updated_event) { Events::Base.new(event_name, Time.zone.now, contact: contact, changed_attributes: changed_attributes) }
let(:changed_attributes) { { 'name' => ['Jane', 'Jane Doe'] } }
context 'when webhook is not configured' do
it 'does not trigger webhook' do
expect(WebhookJob).to receive(:perform_later).exactly(0).times
listener.contact_updated(contact_updated_event)
end
end
context 'when webhook is configured and there is no changed attributes' do
let(:changed_attributes) { {} }
it 'triggers webhook' do
create(:webhook, account: account)
expect(WebhookJob).to receive(:perform_later).exactly(0).times
listener.contact_updated(contact_updated_event)
end
end
context 'when webhook is configured and there are changed attributes' do
it 'triggers webhook' do
webhook = create(:webhook, account: account)
expect(WebhookJob).to receive(:perform_later).with(
webhook.url,
contact.webhook_data.merge(
event: 'contact_updated',
changed_attributes: [{ 'name' => { :current_value => 'Jane Doe', :previous_value => 'Jane' } }]
)
).once
listener.contact_updated(contact_updated_event)
end
end
end
end

View File

@@ -14,6 +14,8 @@ properties:
"conversation_created",
"conversation_status_changed",
"conversation_updated",
"contact_created",
"contact_updated",
"message_created",
"message_updated",
"webwidget_triggered"

View File

@@ -5264,6 +5264,8 @@
"conversation_created",
"conversation_status_changed",
"conversation_updated",
"contact_created",
"contact_updated",
"message_created",
"message_updated",
"webwidget_triggered"