Skip to content

Commit

Permalink
feat(core): twilio channel
Browse files Browse the repository at this point in the history
Create methos, class and channel for use twilio channel

BREAKING CHANGE: project structure
  • Loading branch information
houssenedao committed Dec 22, 2021
1 parent 7c34099 commit 7c0a8d3
Show file tree
Hide file tree
Showing 12 changed files with 495 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lib/bootstrap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './twilio.channel';
export * from './twilio-channel.message';
137 changes: 137 additions & 0 deletions lib/bootstrap/twilio-channel.message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { TwilioChannelModuleOptions } from '../interfaces';
import { TwilioChannelException } from '../exceptions';

/**
* Service name message
* @class TwilioChannelMessage
*/
export class TwilioChannelMessage {
/**
* @property
* @private
*/
private accountSid: string;

/**
* @property
* @private
*/
private authToken: string;

/**
* @property
* @private
*/
private sender: string;

/**
* @property
* @private
*/
private message: string;

/**
* @property
* @private
*/
private toPhoneNumber: string;

/**
* @constructor
* @param {TwilioChannelModuleOptions} options
*/
constructor(options?: TwilioChannelModuleOptions) {
this.accountSid = options?.twilioAccountSid;
this.authToken = options?.twilioAuthToken;
this.sender = options?.twilioSender;
}

/**
* @method
*/
get getAccountSid() {
return this.accountSid;
}

/**
* @method
* @param sid
*/
setAccountSid(sid: string) {
this.accountSid = sid;

return this;
}

/**
* @method
*/
get getAccountToken() {
return this.authToken;
}

/**
* @method
* @param token
*/
setAccountToken(token: string) {
this.authToken = token;

return this;
}

/**
* @method
*/
get getSender() {
return this.sender;
}

/**
* @method
* @param sender
*/
setSender(sender: string) {
this.sender = sender;

return this;
}

/**
* @method
*/
get getToPhoneNumber() {
return this.toPhoneNumber;
}

/**
* @method
* @param toPhoneNumber
*/
setToPhoneNumber(toPhoneNumber: string) {
if (!toPhoneNumber.startsWith('+')) {
throw new TwilioChannelException('Your phone number must start with +.');
}

this.toPhoneNumber = toPhoneNumber;

return this;
}

/**
* @method
*/
get getMessage() {
return this.message;
}

/**
* @method
* @param message
*/
setMessage(message: string) {
this.message = message;

return this;
}
}
147 changes: 147 additions & 0 deletions lib/bootstrap/twilio.channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import { Inject, Injectable, Optional } from '@nestjs/common';
import { INestjsNotificationChannel } from '@sinuos/nestjs-notification';
import { Twilio } from 'twilio';
import { TWILIO_CHANNEL_OPTIONS } from '../constants';
import { ITwilioChannel, TwilioChannelModuleOptions } from '../interfaces';
import { TwilioChannelException } from '../exceptions';

@Injectable()
export class TwilioChannel implements INestjsNotificationChannel {
/**
* @property
* @private
*/
private readonly accountSid = process.env.TWILIO_ACCOUNT_SID;

/**
* @property
* @private
*/
private readonly authToken = process.env.TWILIO_AUTH_TOKEN;

/**
* @property
* @private
*/
private readonly sender = process.env.TWILIO_SENDER;

/**
* Twilio channel constructor.
* @constructor
* @param {TwilioChannelModuleOptions} options
*/
constructor(
@Optional()
@Inject(TWILIO_CHANNEL_OPTIONS)
options: TwilioChannelModuleOptions,
) {
if (options?.twilioAccountSid) {
this.accountSid = options.twilioAccountSid;
}

if (options?.twilioAuthToken) {
this.authToken = options.twilioAuthToken;
}

if (options?.twilioSender) {
this.sender = options.twilioSender;
}
}

/**
* Send notify action
* @public
* @param {ITwilioChannel} notification
* @return Promise<AxiosResponse<any>>
*/
public async send(notification: ITwilioChannel): Promise<any> {
// Validator notification requirements.
this.validator(notification);

// get message content.
const message = TwilioChannel.getData(notification);

// twilio username.
const username = this.accountSid ?? message.getAccountSid;

// twilio password
const password = this.authToken ?? message.getAccountToken;

// sender
const sender = this.sender || message.getSender;

/** @const twilioClient - Init twilio client */
const twilioClient = new Twilio(username, password);

// return twilio message instance
try {
return await twilioClient.messages.create({
body: message.getMessage,
to: message.getToPhoneNumber,
from: sender,
});
} catch (e) {
throw new TwilioChannelException(e.message);
}
}

/**
* Get data.
* @method
* @param {ITwilioChannel} notification
* @private
*/
private static getData(notification: ITwilioChannel) {
return TwilioChannel.getChannelData(notification);
}

/**
* Validator.
* @method
* @param {ITwilioChannel} notification
* @private
*/
private validator(notification: ITwilioChannel) {
const message = TwilioChannel.getData(notification);

/** Account sid is empty */
if (!this.accountSid && !message.getAccountSid) {
throw new TwilioChannelException('Account sid is required');
}

/** Auth token is empty */
if (!this.authToken && !message.getAccountToken) {
throw new TwilioChannelException('Account token is required');
}

/** Sender is empty */
if (!this.sender && !message.getSender) {
throw new TwilioChannelException('Sender is required');
}

/** Phone number is empty */
if (!message.getToPhoneNumber) {
throw new TwilioChannelException('To phone number is required');
}

/** Message is empty */
if (!message.getMessage) {
throw new TwilioChannelException('Message is required');
}
}

/**
* Get the data for the notification.
* @method
* @param notification
*/
private static getChannelData(notification: ITwilioChannel) {
if (typeof notification.toTwilio === 'function') {
return notification.toTwilio();
}

throw new TwilioChannelException(
'Notification is missing toTwilio method.',
);
}
}
1 change: 1 addition & 0 deletions lib/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './twilio.constant';
1 change: 1 addition & 0 deletions lib/constants/twilio.constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TWILIO_CHANNEL_OPTIONS = 'TWILIO_CHANNEL_OPTIONS';
1 change: 1 addition & 0 deletions lib/exceptions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './twilio-channel.exception';
15 changes: 15 additions & 0 deletions lib/exceptions/twilio-channel.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* @class TwilioChannelException
* @extends Error
*/
export class TwilioChannelException extends Error {
/**
* @constructor
* @param message
*/
constructor(message: string) {
super(message);

this.name = 'TwilioChannelException';
}
}
3 changes: 3 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './bootstrap';
export * from './interfaces';
export * from './twilio-channel.module';
2 changes: 2 additions & 0 deletions lib/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './twilio-channel-module.interface';
export * from './twilio-channel.interface';
42 changes: 42 additions & 0 deletions lib/interfaces/twilio-channel-module.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ModuleMetadata, Provider, Type } from '@nestjs/common';

/**
* @interface TwilioChannelModuleOptionsFactory
* @property createTwilioChannelOptions()
*/
export interface TwilioChannelModuleOptionsFactory {
createTwilioChannelOptions():
| Promise<TwilioChannelModuleOptions>
| TwilioChannelModuleOptions;
}

/**
* @interface TwilioChannelModuleAsyncOptions
* @extends {Pick<ModuleMetadata, 'imports'>}
* @property useExisting
* @property useClass
* @property useFactory
* @property inject
* @property extraProviders
*/
export interface TwilioChannelModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<TwilioChannelModuleOptionsFactory>;
useClass?: Type<TwilioChannelModuleOptionsFactory>;
useFactory?: (
...args: any[]
) => Promise<TwilioChannelModuleOptions> | TwilioChannelModuleOptions;
inject?: any[];
extraProviders?: Provider[];
}

/**
* @interface TwilioChannelModuleOptions
* @property {string} twilioAccountSid
* @property {string} twilioAuthToken
*/
export interface TwilioChannelModuleOptions {
twilioAccountSid?: string;
twilioAuthToken?: string;
twilioSender?: string;
}
15 changes: 15 additions & 0 deletions lib/interfaces/twilio-channel.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NestJsNotification } from '@sinuos/nestjs-notification';

/**
* Twilio channel model
* @interface ITwilioChannel
* @extends NestJsNotification
*/
export interface ITwilioChannel extends NestJsNotification {
/**
* Get representation of the notification.
* @property
* @returns {any}
*/
toTwilio?(): any;
}
Loading

0 comments on commit 7c0a8d3

Please sign in to comment.