chore: Webhook event data improvements (#4317)
This commit is contained in:
parent
15fd37b124
commit
bfe6324d9a
5 changed files with 116 additions and 12 deletions
|
@ -22,4 +22,12 @@ class BaseListener
|
||||||
contact = event.data[:contact]
|
contact = event.data[:contact]
|
||||||
[contact, contact.account]
|
[contact, contact.account]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def extract_changed_attributes(event)
|
||||||
|
changed_attributes = event.data[:changed_attributes]
|
||||||
|
|
||||||
|
return if changed_attributes.blank?
|
||||||
|
|
||||||
|
changed_attributes.map { |k, v| { k => { previous_value: v[0], current_value: v[1] } } }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,23 +2,34 @@ class WebhookListener < BaseListener
|
||||||
# FIXME: deprecate the opened and resolved events in future in favor of status changed event.
|
# FIXME: deprecate the opened and resolved events in future in favor of status changed event.
|
||||||
def conversation_resolved(event)
|
def conversation_resolved(event)
|
||||||
conversation = extract_conversation_and_account(event)[0]
|
conversation = extract_conversation_and_account(event)[0]
|
||||||
|
changed_attributes = extract_changed_attributes(event)
|
||||||
inbox = conversation.inbox
|
inbox = conversation.inbox
|
||||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
payload = conversation.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes)
|
||||||
deliver_webhook_payloads(payload, inbox)
|
deliver_webhook_payloads(payload, inbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
# FIXME: deprecate the opened and resolved events in future in favor of status changed event.
|
# FIXME: deprecate the opened and resolved events in future in favor of status changed event.
|
||||||
def conversation_opened(event)
|
def conversation_opened(event)
|
||||||
conversation = extract_conversation_and_account(event)[0]
|
conversation = extract_conversation_and_account(event)[0]
|
||||||
|
changed_attributes = extract_changed_attributes(event)
|
||||||
inbox = conversation.inbox
|
inbox = conversation.inbox
|
||||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
payload = conversation.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes)
|
||||||
deliver_webhook_payloads(payload, inbox)
|
deliver_webhook_payloads(payload, inbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_status_changed(event)
|
def conversation_status_changed(event)
|
||||||
conversation = extract_conversation_and_account(event)[0]
|
conversation = extract_conversation_and_account(event)[0]
|
||||||
|
changed_attributes = extract_changed_attributes(event)
|
||||||
inbox = conversation.inbox
|
inbox = conversation.inbox
|
||||||
payload = conversation.webhook_data.merge(event: __method__.to_s)
|
payload = conversation.webhook_data.merge(event: __method__.to_s, changed_attributes: changed_attributes)
|
||||||
|
deliver_webhook_payloads(payload, inbox)
|
||||||
|
end
|
||||||
|
|
||||||
|
def conversation_updated(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)
|
deliver_webhook_payloads(payload, inbox)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ class Conversation < ApplicationRecord
|
||||||
return unless previous_changes.keys.present? && (previous_changes.keys & %w[team_id assignee_id status snoozed_until
|
return unless previous_changes.keys.present? && (previous_changes.keys & %w[team_id assignee_id status snoozed_until
|
||||||
custom_attributes]).present?
|
custom_attributes]).present?
|
||||||
|
|
||||||
dispatcher_dispatch(CONVERSATION_UPDATED)
|
dispatcher_dispatch(CONVERSATION_UPDATED, previous_changes)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self_assign?(assignee_id)
|
def self_assign?(assignee_id)
|
||||||
|
@ -207,14 +207,15 @@ class Conversation < ApplicationRecord
|
||||||
CONVERSATION_READ => -> { saved_change_to_contact_last_seen_at? },
|
CONVERSATION_READ => -> { saved_change_to_contact_last_seen_at? },
|
||||||
CONVERSATION_CONTACT_CHANGED => -> { saved_change_to_contact_id? }
|
CONVERSATION_CONTACT_CHANGED => -> { saved_change_to_contact_id? }
|
||||||
}.each do |event, condition|
|
}.each do |event, condition|
|
||||||
condition.call && dispatcher_dispatch(event)
|
condition.call && dispatcher_dispatch(event, status_change)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def dispatcher_dispatch(event_name)
|
def dispatcher_dispatch(event_name, changed_attributes = nil)
|
||||||
return if Current.executed_by.present? && Current.executed_by.instance_of?(AutomationRule)
|
return if Current.executed_by.present? && Current.executed_by.instance_of?(AutomationRule)
|
||||||
|
|
||||||
Rails.configuration.dispatcher.dispatch(event_name, Time.zone.now, conversation: self, notifiable_assignee_change: notifiable_assignee_change?)
|
Rails.configuration.dispatcher.dispatch(event_name, Time.zone.now, conversation: self, notifiable_assignee_change: notifiable_assignee_change?,
|
||||||
|
changed_attributes: changed_attributes)
|
||||||
end
|
end
|
||||||
|
|
||||||
def conversation_status_changed_to_open?
|
def conversation_status_changed_to_open?
|
||||||
|
|
|
@ -105,4 +105,80 @@ describe WebhookListener do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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
|
||||||
|
Events::Base.new(
|
||||||
|
event_name, Time.zone.now,
|
||||||
|
conversation: conversation.reload,
|
||||||
|
changed_attributes: {
|
||||||
|
custom_attributes: [{ test: nil }, { test: 'testing custom attri webhook' }]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
let(:event_name) { :'conversation.updated' }
|
||||||
|
|
||||||
|
context 'when webhook is not configured' do
|
||||||
|
it 'does not trigger webhook' do
|
||||||
|
expect(WebhookJob).to receive(:perform_later).exactly(0).times
|
||||||
|
listener.conversation_updated(conversation_updated_event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when webhook is configured' do
|
||||||
|
it 'triggers webhook' do
|
||||||
|
webhook = create(:webhook, inbox: inbox, account: account)
|
||||||
|
|
||||||
|
conversation.update(custom_attributes: { test: 'testing custom attri webhook' })
|
||||||
|
|
||||||
|
expect(WebhookJob).to receive(:perform_later).with(
|
||||||
|
webhook.url,
|
||||||
|
conversation.webhook_data.merge(
|
||||||
|
event: 'conversation_updated',
|
||||||
|
changed_attributes: [
|
||||||
|
{
|
||||||
|
custom_attributes: {
|
||||||
|
previous_value: { test: nil },
|
||||||
|
current_value: { test: 'testing custom attri webhook' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
).once
|
||||||
|
|
||||||
|
listener.conversation_updated(conversation_updated_event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,7 +54,8 @@ RSpec.describe Conversation, type: :model do
|
||||||
it 'runs after_create callbacks' do
|
it 'runs after_create callbacks' do
|
||||||
# send_events
|
# send_events
|
||||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
.with(described_class::CONVERSATION_CREATED, kind_of(Time), conversation: conversation, notifiable_assignee_change: false)
|
.with(described_class::CONVERSATION_CREATED, kind_of(Time), conversation: conversation, notifiable_assignee_change: false,
|
||||||
|
changed_attributes: nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -115,14 +116,21 @@ RSpec.describe Conversation, type: :model do
|
||||||
assignee: new_assignee,
|
assignee: new_assignee,
|
||||||
label_list: [label.title]
|
label_list: [label.title]
|
||||||
)
|
)
|
||||||
|
status_change = conversation.status_change
|
||||||
|
changed_attributes = conversation.previous_changes
|
||||||
|
|
||||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
.with(described_class::CONVERSATION_RESOLVED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true)
|
.with(described_class::CONVERSATION_RESOLVED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true,
|
||||||
|
changed_attributes: status_change)
|
||||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
.with(described_class::CONVERSATION_READ, kind_of(Time), conversation: conversation, notifiable_assignee_change: true)
|
.with(described_class::CONVERSATION_READ, kind_of(Time), conversation: conversation, notifiable_assignee_change: true,
|
||||||
|
changed_attributes: nil)
|
||||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
.with(described_class::ASSIGNEE_CHANGED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true)
|
.with(described_class::ASSIGNEE_CHANGED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true,
|
||||||
|
changed_attributes: nil)
|
||||||
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
|
||||||
.with(described_class::CONVERSATION_UPDATED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true)
|
.with(described_class::CONVERSATION_UPDATED, kind_of(Time), conversation: conversation, notifiable_assignee_change: true,
|
||||||
|
changed_attributes: changed_attributes)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'will not run conversation_updated event for empty updates' do
|
it 'will not run conversation_updated event for empty updates' do
|
||||||
|
|
Loading…
Reference in a new issue