Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Bandwidth sms channel delivery reports (#8198)
- Loading branch information
Showing
5 changed files
with
190 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,28 @@ | ||
class Webhooks::SmsEventsJob < ApplicationJob | ||
queue_as :default | ||
|
||
SUPPORTED_EVENTS = %w[message-received message-delivered message-failed].freeze | ||
|
||
def perform(params = {}) | ||
return unless params[:type] == 'message-received' | ||
return unless SUPPORTED_EVENTS.include?(params[:type]) | ||
|
||
channel = Channel::Sms.find_by(phone_number: params[:to]) | ||
return unless channel | ||
|
||
# TODO: pass to appropriate provider service from here | ||
Sms::IncomingMessageService.new(inbox: channel.inbox, params: params[:message].with_indifferent_access).perform | ||
process_event_params(channel, params) | ||
end | ||
|
||
private | ||
|
||
def process_event_params(channel, params) | ||
if delivery_event?(params) | ||
Sms::DeliveryStatusService.new(channel: channel, params: params[:message].with_indifferent_access).perform | ||
else | ||
Sms::IncomingMessageService.new(inbox: channel.inbox, params: params[:message].with_indifferent_access).perform | ||
end | ||
end | ||
|
||
def delivery_event?(params) | ||
params[:type] == 'message-delivered' || params[:type] == 'message-failed' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
class Sms::DeliveryStatusService | ||
pattr_initialize [:inbox!, :params!] | ||
|
||
def perform | ||
return unless supported_status? | ||
|
||
process_status if message.present? | ||
end | ||
|
||
private | ||
|
||
def process_status | ||
@message.status = status | ||
@message.external_error = external_error if error_occurred? | ||
@message.save! | ||
end | ||
|
||
def supported_status? | ||
%w[message-delivered message-failed].include?(params[:type]) | ||
end | ||
|
||
# Relevant documentation: | ||
# https://dev.bandwidth.com/docs/mfa/webhooks/international/message-delivered | ||
# https://dev.bandwidth.com/docs/mfa/webhooks/international/message-failed | ||
def status | ||
type_mapping = { | ||
'message-delivered' => 'delivered', | ||
'message-failed' => 'failed' | ||
} | ||
|
||
type_mapping[params[:type]] | ||
end | ||
|
||
def external_error | ||
return nil unless error_occurred? | ||
|
||
error_message = params[:description] | ||
error_code = params[:errorCode] | ||
|
||
"#{error_code} - #{error_message}" | ||
end | ||
|
||
def error_occurred? | ||
params[:errorCode] && params[:type] == 'message-failed' | ||
end | ||
|
||
def message | ||
return unless params[:message][:id] | ||
|
||
@message ||= inbox.messages.find_by(source_id: params[:message][:id]) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
require 'rails_helper' | ||
|
||
describe Sms::DeliveryStatusService do | ||
describe '#perform' do | ||
let!(:account) { create(:account) } | ||
let!(:sms_channel) { create(:channel_sms) } | ||
let!(:contact) { create(:contact, account: account, phone_number: '+12345') } | ||
let(:contact_inbox) { create(:contact_inbox, source_id: '+12345', contact: contact, inbox: sms_channel.inbox) } | ||
let!(:conversation) { create(:conversation, contact: contact, inbox: sms_channel.inbox, contact_inbox: contact_inbox) } | ||
|
||
describe '#perform' do | ||
context 'when message delivery status is fired' do | ||
before do | ||
create(:message, account: account, inbox: sms_channel.inbox, conversation: conversation, status: :sent, | ||
source_id: 'SMd560ac79e4a4d36b3ce59f1f50471986') | ||
end | ||
|
||
it 'updates the message if the message status is delivered' do | ||
params = { | ||
time: '2022-02-02T23:14:05.309Z', | ||
type: 'message-delivered', | ||
to: sms_channel.phone_number, | ||
description: 'ok', | ||
message: { | ||
'id': conversation.messages.last.source_id | ||
} | ||
} | ||
|
||
described_class.new(params: params, inbox: sms_channel.inbox).perform | ||
expect(conversation.reload.messages.last.status).to eq('delivered') | ||
end | ||
|
||
it 'updates the message if the message status is failed' do | ||
params = { | ||
time: '2022-02-02T23:14:05.309Z', | ||
type: 'message-failed', | ||
to: sms_channel.phone_number, | ||
description: 'Undeliverable', | ||
errorCode: 995, | ||
message: { | ||
'id': conversation.messages.last.source_id | ||
} | ||
} | ||
|
||
described_class.new(params: params, inbox: sms_channel.inbox).perform | ||
expect(conversation.reload.messages.last.status).to eq('failed') | ||
|
||
expect(conversation.reload.messages.last.external_error).to eq('995 - Undeliverable') | ||
end | ||
|
||
it 'does not update the message if the status is not a support status' do | ||
params = { | ||
time: '2022-02-02T23:14:05.309Z', | ||
type: 'queued', | ||
to: sms_channel.phone_number, | ||
description: 'ok', | ||
message: { | ||
'id': conversation.messages.last.source_id | ||
} | ||
} | ||
|
||
described_class.new(params: params, inbox: sms_channel.inbox).perform | ||
expect(conversation.reload.messages.last.status).to eq('sent') | ||
end | ||
|
||
it 'does not update the message if the message is not present' do | ||
params = { | ||
time: '2022-02-02T23:14:05.309Z', | ||
type: 'message-delivered', | ||
to: sms_channel.phone_number, | ||
description: 'ok', | ||
message: { | ||
'id': '123' | ||
} | ||
} | ||
|
||
described_class.new(params: params, inbox: sms_channel.inbox).perform | ||
expect(conversation.reload.messages.last.status).to eq('sent') | ||
end | ||
end | ||
end | ||
end | ||
end |