chore: Provider APIs for SMS Channel - Bandwidth (#3889)

fixes: #3888
This commit is contained in:
Sojan Jose
2022-02-03 15:22:13 -08:00
committed by GitHub
parent fba7f40bee
commit cf10f3d03b
40 changed files with 879 additions and 51 deletions

View File

@@ -99,6 +99,53 @@ describe ::ContactInboxBuilder do
end
end
describe 'sms inbox' do
let!(:sms_channel) { create(:channel_sms, account: account) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: account) }
it 'does not create contact inbox when contact inbox already exists with the source id provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
contact_id: contact.id,
inbox_id: sms_inbox.id,
source_id: contact.phone_number
).perform
expect(contact_inbox.id).to be(existing_contact_inbox.id)
end
it 'does not create contact inbox when contact inbox already exists with phone number and source id is not provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
contact_id: contact.id,
inbox_id: sms_inbox.id
).perform
expect(contact_inbox.id).to be(existing_contact_inbox.id)
end
it 'creates a new contact inbox when different source id is provided' do
existing_contact_inbox = create(:contact_inbox, contact: contact, inbox: sms_inbox, source_id: contact.phone_number)
contact_inbox = described_class.new(
contact_id: contact.id,
inbox_id: sms_inbox.id,
source_id: '+224213223422'
).perform
expect(contact_inbox.id).not_to be(existing_contact_inbox.id)
expect(contact_inbox.source_id).not_to be('+224213223422')
end
it 'creates a contact inbox with contact phone number when source id not provided and no contact inbox exists' do
contact_inbox = described_class.new(
contact_id: contact.id,
inbox_id: sms_inbox.id
).perform
expect(contact_inbox.source_id).not_to be(contact.phone_number)
end
end
describe 'email inbox' do
let!(:email_channel) { create(:channel_email, account: account) }
let!(:email_inbox) { create(:inbox, channel: email_channel, account: account) }

View File

@@ -309,6 +309,18 @@ RSpec.describe 'Inboxes API', type: :request do
expect(response.body).to include('callback_webhook_url')
end
it 'creates a sms inbox when administrator' do
post "/api/v1/accounts/#{account.id}/inboxes",
headers: admin.create_new_auth_token,
params: { name: 'Sms Inbox',
channel: { type: 'sms', phone_number: '+123456789', provider_config: { test: 'test' } } },
as: :json
expect(response).to have_http_status(:success)
expect(response.body).to include('Sms Inbox')
expect(response.body).to include('+123456789')
end
it 'creates the webwidget inbox that allow messages after conversation is resolved' do
post "/api/v1/accounts/#{account.id}/inboxes",
headers: admin.create_new_auth_token,

View File

@@ -0,0 +1,12 @@
require 'rails_helper'
RSpec.describe 'Webhooks::SmsController', type: :request do
describe 'POST /webhooks/sms/{:phone_number}' do
it 'call the sms events job with the params' do
allow(Webhooks::SmsEventsJob).to receive(:perform_later)
expect(Webhooks::SmsEventsJob).to receive(:perform_later)
post '/webhooks/sms/123221321', params: { content: 'hello' }
expect(response).to have_http_status(:success)
end
end
end

View File

@@ -0,0 +1,16 @@
FactoryBot.define do
factory :channel_sms, class: 'Channel::Sms' do
sequence(:phone_number) { |n| "+123456789#{n}1" }
account
provider_config do
{ 'account_id' => '1',
'application_id' => '1',
'api_key' => '1',
'api_secret' => '1' }
end
after(:create) do |channel_sms|
create(:inbox, channel: channel_sms, account: channel_sms.account)
end
end
end

View File

@@ -75,5 +75,14 @@ RSpec.describe SendReplyJob, type: :job do
expect(process_service).to receive(:perform)
described_class.perform_now(message.id)
end
it 'calls ::Sms::SendOnSmsService when its sms message' do
sms_channel = create(:channel_sms)
message = create(:message, conversation: create(:conversation, inbox: sms_channel.inbox))
allow(::Sms::SendOnSmsService).to receive(:new).with(message: message).and_return(process_service)
expect(::Sms::SendOnSmsService).to receive(:new).with(message: message)
expect(process_service).to receive(:perform)
described_class.perform_now(message.id)
end
end
end

View File

@@ -0,0 +1,56 @@
require 'rails_helper'
RSpec.describe Webhooks::SmsEventsJob, type: :job do
subject(:job) { described_class.perform_later(params) }
let!(:sms_channel) { create(:channel_sms) }
let!(:params) do
{
time: '2022-02-02T23:14:05.309Z',
type: 'message-received',
to: sms_channel.phone_number,
description: 'Incoming message received',
message: {
'id': '3232420-2323-234324',
'owner': sms_channel.phone_number,
'applicationId': '2342349-324234d-32432432',
'time': '2022-02-02T23:14:05.262Z',
'segmentCount': 1,
'direction': 'in',
'to': [
sms_channel.phone_number
],
'from': '+14234234234',
'text': 'test message'
}
}
end
it 'enqueues the job' do
expect { job }.to have_enqueued_job(described_class)
.with(params)
.on_queue('default')
end
context 'when invalid params' do
it 'returns nil when no bot_token' do
expect(described_class.perform_now({})).to be_nil
end
it 'returns nil when invalid type' do
expect(described_class.perform_now({ type: 'invalid' })).to be_nil
end
end
context 'when valid params' do
it 'calls Sms::IncomingMessageService' do
process_service = double
allow(Sms::IncomingMessageService).to receive(:new).and_return(process_service)
allow(process_service).to receive(:perform)
expect(Sms::IncomingMessageService).to receive(:new).with(inbox: sms_channel.inbox,
params: params[:message].with_indifferent_access)
expect(process_service).to receive(:perform)
described_class.perform_now(params)
end
end
end

View File

@@ -78,6 +78,27 @@ RSpec.describe Campaign, type: :model do
end
end
context 'when SMS campaign' do
let!(:sms_channel) { create(:channel_sms) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
let(:campaign) { build(:campaign, inbox: sms_inbox) }
it 'only saves campaign type as oneoff and wont leave scheduled_at empty' do
campaign.campaign_type = 'ongoing'
campaign.save!
expect(campaign.reload.campaign_type).to eq 'one_off'
expect(campaign.scheduled_at.present?).to eq true
end
it 'calls sms service on trigger!' do
sms_service = double
expect(Sms::OneoffSmsCampaignService).to receive(:new).with(campaign: campaign).and_return(sms_service)
expect(sms_service).to receive(:perform)
campaign.save!
campaign.trigger!
end
end
context 'when Website campaign' do
let(:campaign) { build(:campaign) }

View File

@@ -15,8 +15,8 @@ describe Contacts::ContactableInboxesService do
let!(:email_inbox) { create(:inbox, channel: email_channel, account: account) }
let!(:api_channel) { create(:channel_api, account: account) }
let!(:api_inbox) { create(:inbox, channel: api_channel, account: account) }
let!(:website_channel) { create(:channel_widget, account: account) }
let!(:website_inbox) { create(:inbox, channel: website_channel, account: account) }
let!(:website_inbox) { create(:inbox, channel: create(:channel_widget, account: account), account: account) }
let!(:sms_inbox) { create(:inbox, channel: create(:channel_sms, account: account), account: account) }
describe '#get' do
it 'returns the contactable inboxes for the contact' do
@@ -25,7 +25,7 @@ describe Contacts::ContactableInboxesService do
expect(contactable_inboxes).to include({ source_id: contact.phone_number, inbox: twilio_sms_inbox })
expect(contactable_inboxes).to include({ source_id: "whatsapp:#{contact.phone_number}", inbox: twilio_whatsapp_inbox })
expect(contactable_inboxes).to include({ source_id: contact.email, inbox: email_inbox })
expect(contactable_inboxes.pluck(:inbox)).to include(api_inbox)
expect(contactable_inboxes).to include({ source_id: contact.phone_number, inbox: sms_inbox })
end
it 'doest not return the non contactable inboxes for the contact' do

View File

@@ -0,0 +1,31 @@
require 'rails_helper'
describe Sms::IncomingMessageService do
describe '#perform' do
let!(:sms_channel) { create(:channel_sms) }
context 'when valid text message params' do
it 'creates appropriate conversations, message and contacts' do
params = {
'id': '3232420-2323-234324',
'owner': sms_channel.phone_number,
'applicationId': '2342349-324234d-32432432',
'time': '2022-02-02T23:14:05.262Z',
'segmentCount': 1,
'direction': 'in',
'to': [
sms_channel.phone_number
],
'from': '+14234234234',
'text': 'test message'
}.with_indifferent_access
described_class.new(inbox: sms_channel.inbox, params: params).perform
expect(sms_channel.inbox.conversations.count).not_to eq(0)
expect(Contact.all.first.name).to eq('+1 423-423-4234')
expect(sms_channel.inbox.messages.first.content).to eq('test message')
end
end
end
end

View File

@@ -0,0 +1,47 @@
require 'rails_helper'
describe Sms::OneoffSmsCampaignService do
subject(:sms_campaign_service) { described_class.new(campaign: campaign) }
let(:account) { create(:account) }
let!(:sms_channel) { create(:channel_sms) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
let(:label1) { create(:label, account: account) }
let(:label2) { create(:label, account: account) }
let!(:campaign) do
create(:campaign, inbox: sms_inbox, account: account,
audience: [{ type: 'Label', id: label1.id }, { type: 'Label', id: label2.id }])
end
describe 'perform' do
before do
stub_request(:post, 'https://messaging.bandwidth.com/api/v2/users/1/messages').to_return(
status: 200,
body: { 'id' => '1' }.to_json,
headers: {}
)
end
it 'raises error if the campaign is completed' do
campaign.completed!
expect { sms_campaign_service.perform }.to raise_error 'Completed Campaign'
end
it 'raises error invalid campaign when its not a oneoff sms campaign' do
campaign = create(:campaign)
expect { described_class.new(campaign: campaign).perform }.to raise_error "Invalid campaign #{campaign.id}"
end
it 'send messages to contacts in the audience and marks the campaign completed' do
contact_with_label1, contact_with_label2, contact_with_both_labels = FactoryBot.create_list(:contact, 3, :with_phone_number, account: account)
contact_with_label1.update_labels([label1.title])
contact_with_label2.update_labels([label2.title])
contact_with_both_labels.update_labels([label1.title, label2.title])
sms_campaign_service.perform
assert_requested(:post, 'https://messaging.bandwidth.com/api/v2/users/1/messages', times: 3)
expect(campaign.reload.completed?).to eq true
end
end
end

View File

@@ -0,0 +1,28 @@
require 'rails_helper'
describe Sms::SendOnSmsService do
describe '#perform' do
context 'when a valid message' do
let(:sms_request) { double }
let!(:sms_channel) { create(:channel_sms) }
let!(:contact_inbox) { create(:contact_inbox, inbox: sms_channel.inbox, source_id: '+123456789') }
let!(:conversation) { create(:conversation, contact_inbox: contact_inbox, inbox: sms_channel.inbox) }
it 'calls channel.send_message' do
message = create(:message, message_type: :outgoing, content: 'test',
conversation: conversation)
allow(HTTParty).to receive(:post).and_return(sms_request)
allow(sms_request).to receive(:success?).and_return(true)
allow(sms_request).to receive(:parsed_response).and_return({ 'id' => '123456789' })
expect(HTTParty).to receive(:post).with(
'https://messaging.bandwidth.com/api/v2/users/1/messages',
basic_auth: { username: '1', password: '1' },
headers: { 'Content-Type' => 'application/json' },
body: { 'to' => '+123456789', 'from' => sms_channel.phone_number, 'text' => 'test', 'applicationId' => '1' }.to_json
)
described_class.new(message: message).perform
expect(message.reload.source_id).to eq('123456789')
end
end
end
end