-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(provider): send notifications/messages to whatsapp or as text SM…
…S using Twilio (#91) * feat(provider): send notifications/messages to whatsapp or as text SMS using TWILIO(#88) GH-88 * feat(provider): send notifications/messages to whatsapp or as text SMS using TWILIO #88 GH-88 * send notifications/messages to whatsapp or as text SMS using Twilio * send notifications/messages to whatsapp or as text SMS using Twilio * send notifications/messages to whatsapp or as text SMS using Twilio * send notifications/messages to whatsapp or as text SMS using Twilio * send notifications/messages to whatsapp or as text SMS using Twilio * send notifications/messages to whatsapp or as text SMS using Twilio
- Loading branch information
1 parent
6f035a6
commit f9d9799
Showing
10 changed files
with
395 additions
and
4 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
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,157 @@ | ||
import {Constructor} from '@loopback/core'; | ||
import {expect, sinon} from '@loopback/testlab'; | ||
import proxyquire from 'proxyquire'; | ||
import {TwilioMessage, TwilioProvider} from '../../providers'; | ||
|
||
describe('Twilio Service', () => { | ||
const message: TwilioMessage = { | ||
receiver: { | ||
to: [], | ||
}, | ||
body: 'test', | ||
sentDate: new Date(), | ||
type: 0, | ||
subject: undefined, | ||
}; | ||
const messageText: TwilioMessage = { | ||
receiver: { | ||
to: [ | ||
{ | ||
id: 'XXXXXXXXXXX', | ||
type: 1, | ||
}, | ||
], | ||
}, | ||
body: 'Test SMS Text Notification', | ||
sentDate: new Date(), | ||
type: 2, | ||
subject: undefined, | ||
}; | ||
const messageTextMedia: TwilioMessage = { | ||
receiver: { | ||
to: [ | ||
{ | ||
id: 'XXXXXXXXXXX', | ||
type: 1, | ||
}, | ||
], | ||
}, | ||
body: 'Test SMS Notification with media', | ||
mediaUrl: ['https://demo.twilio.com/owl.png'], | ||
sentDate: new Date(), | ||
type: 2, | ||
subject: undefined, | ||
}; | ||
const messageWhatsApp: TwilioMessage = { | ||
receiver: { | ||
to: [ | ||
{ | ||
id: 'XXXXXXXXXXX', | ||
type: 0, | ||
}, | ||
], | ||
}, | ||
body: 'Test Whatsapp Notification', | ||
sentDate: new Date(), | ||
type: 2, | ||
subject: undefined, | ||
}; | ||
const messageWAMedia: TwilioMessage = { | ||
receiver: { | ||
to: [ | ||
{ | ||
id: 'XXXXXXXXXXX', | ||
type: 0, | ||
}, | ||
], | ||
}, | ||
body: 'Test Whatsapp message with media', | ||
mediaUrl: ['https://demo.twilio.com/owl.png'], | ||
sentDate: new Date(), | ||
type: 2, | ||
subject: undefined, | ||
}; | ||
const configration = { | ||
accountSid: 'ACTSIDDUMMY', | ||
authToken: 'AUTHDUMMY', | ||
waFrom: '', //Ex. whatsapp:+XXXXXXXXXXX | ||
smsFrom: '', | ||
opts: {dummy: true}, //Change dummy value to false when using unit test | ||
}; | ||
|
||
let TwilioProviderMock: Constructor<TwilioProvider>; | ||
beforeEach(setupMockTwilio); | ||
describe('twilio configration addition', () => { | ||
it('returns error message on passing reciever length as zero', async () => { | ||
const twilioProvider = new TwilioProviderMock(configration).value(); | ||
const result = await twilioProvider | ||
.publish(message) | ||
.catch(err => err.message); | ||
expect(result).which.eql('Message receiver not found in request'); | ||
}); | ||
|
||
it('returns error message when no twilio config', async () => { | ||
try { | ||
|
||
// NOSONAR | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const twilioProvider = new TwilioProvider(); | ||
|
||
} catch (err) { | ||
const result = err.message; | ||
expect(result).which.eql('Twilio Config missing !'); | ||
} | ||
}); | ||
|
||
it('returns the message (SMS text)', async () => { | ||
const twilioProvider = new TwilioProviderMock(configration).value(); | ||
const result = twilioProvider.publish(messageText); | ||
if (configration.opts?.dummy) { | ||
expect(result).to.have.Promise(); | ||
} else { | ||
await expect(result).to.be.fulfilled(); | ||
} | ||
}); | ||
|
||
it('returns the message (SMS with media)', async () => { | ||
const twilioProvider = new TwilioProviderMock(configration).value(); | ||
const result = twilioProvider.publish(messageTextMedia); | ||
if (configration.opts?.dummy) { | ||
expect(result).to.have.Promise(); | ||
} else { | ||
await expect(result).to.be.fulfilled(); | ||
} | ||
}); | ||
|
||
it('returns the message (Whatsapp)', async () => { | ||
const twilioProvider = new TwilioProviderMock(configration).value(); | ||
const result = twilioProvider.publish(messageWhatsApp); | ||
if (configration.opts?.dummy) { | ||
expect(result).to.have.Promise(); | ||
} else { | ||
await expect(result).to.be.fulfilled(); | ||
} | ||
}); | ||
|
||
it('returns the message (Whatsapp with Media)', async () => { | ||
const twilioProvider = new TwilioProviderMock(configration).value(); | ||
const result = twilioProvider.publish(messageWAMedia); | ||
if (configration.opts?.dummy) { | ||
expect(result).to.have.Promise(); | ||
} else { | ||
await expect(result).to.be.fulfilled(); | ||
} | ||
}); | ||
}); | ||
|
||
function setupMockTwilio() { | ||
const mockTwilio = sinon.stub(); | ||
mockTwilio.prototype.publish = sinon.stub().returns(Promise.resolve()); | ||
TwilioProviderMock = proxyquire( | ||
'../../providers/sms/twilio/twilio.provider', | ||
{ | ||
'twilio.twilio': mockTwilio, | ||
}, | ||
).TwilioProvider; | ||
} | ||
}); |
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,2 +1,3 @@ | ||
export * from './sns'; | ||
export * from './twilio'; | ||
export * from './types'; |
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,3 @@ | ||
export * from './keys'; | ||
export * from './twilio.provider'; | ||
export * from './types'; |
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,8 @@ | ||
import {BindingKey} from '@loopback/core'; | ||
import {TwilioAuthConfig} from '../twilio/types'; | ||
|
||
export namespace TwilioBindings { | ||
export const Config = BindingKey.create<TwilioAuthConfig | null>( | ||
'sf.notification.config.twilio', | ||
); | ||
} |
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,77 @@ | ||
import {inject, Provider} from '@loopback/core'; | ||
import {HttpErrors} from '@loopback/rest'; | ||
import twilio, {Twilio} from 'twilio'; | ||
import {TwilioBindings} from './keys'; | ||
|
||
import { | ||
TwilioAuthConfig, | ||
TwilioCreateMessageParams, | ||
TwilioMessage, | ||
TwilioNotification, | ||
TwilioSubscriberType, | ||
} from '../twilio/types'; | ||
|
||
export class TwilioProvider implements Provider<TwilioNotification> { | ||
twilioService: Twilio; | ||
constructor( | ||
@inject(TwilioBindings.Config, { | ||
optional: true, | ||
}) | ||
private readonly twilioConfig?: TwilioAuthConfig, | ||
) { | ||
if (this.twilioConfig) { | ||
this.twilioService = twilio( | ||
this.twilioConfig.accountSid, | ||
this.twilioConfig.authToken, | ||
); | ||
} else { | ||
throw new HttpErrors.PreconditionFailed('Twilio Config missing !'); | ||
} | ||
} | ||
|
||
value() { | ||
return { | ||
publish: async (message: TwilioMessage) => { | ||
if (message.receiver.to.length === 0) { | ||
throw new HttpErrors.BadRequest( | ||
'Message receiver not found in request', | ||
); | ||
} | ||
const publishes = message.receiver.to.map(async receiver => { | ||
const msg: string = message.body; | ||
const twilioMsgObj: TwilioCreateMessageParams = { | ||
body: msg, | ||
from: | ||
receiver.type && | ||
receiver.type === TwilioSubscriberType.TextSMSUser | ||
? String(this.twilioConfig?.smsFrom) | ||
: String(this.twilioConfig?.waFrom), | ||
to: | ||
receiver.type && | ||
receiver.type === TwilioSubscriberType.TextSMSUser | ||
? `+${receiver.id}` | ||
: `whatsapp:+${receiver.id}`, | ||
}; | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
message.mediaUrl && (twilioMsgObj.mediaUrl = message.mediaUrl); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
receiver.type && | ||
receiver.type === TwilioSubscriberType.TextSMSUser && | ||
this.twilioConfig?.smsStatusCallback && | ||
(twilioMsgObj.statusCallback = | ||
this.twilioConfig?.smsStatusCallback); | ||
|
||
// eslint-disable-next-line no-unused-expressions | ||
!receiver.type && | ||
this.twilioConfig?.waStatusCallback && | ||
(twilioMsgObj.statusCallback = this.twilioConfig?.waStatusCallback); | ||
|
||
return this.twilioService.messages.create(twilioMsgObj); | ||
}); | ||
await Promise.all(publishes); | ||
}, | ||
}; | ||
} | ||
} |
Oops, something went wrong.