@qrvey/sms 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +74 -0
  2. package/__tests__/__mocks__/SMSSender.mock.ts +10 -0
  3. package/coverageReader.sh +50 -0
  4. package/dist/cjs/errors/index.js +19 -0
  5. package/dist/cjs/errors/index.js.map +1 -0
  6. package/dist/cjs/errors/notProvidedCredentials.error.js +11 -0
  7. package/dist/cjs/errors/notProvidedCredentials.error.js.map +1 -0
  8. package/dist/cjs/errors/unknownProvider.error.js +11 -0
  9. package/dist/cjs/errors/unknownProvider.error.js.map +1 -0
  10. package/dist/cjs/index.js +6 -0
  11. package/dist/cjs/index.js.map +1 -0
  12. package/dist/cjs/interfaces/index.js +18 -0
  13. package/dist/cjs/interfaces/index.js.map +1 -0
  14. package/dist/cjs/interfaces/smsSender.interface.js +3 -0
  15. package/dist/cjs/interfaces/smsSender.interface.js.map +1 -0
  16. package/dist/cjs/services/clients/awsSmsSender.client.js +37 -0
  17. package/dist/cjs/services/clients/awsSmsSender.client.js.map +1 -0
  18. package/dist/cjs/services/clients/azureSmsSender.client.js +36 -0
  19. package/dist/cjs/services/clients/azureSmsSender.client.js.map +1 -0
  20. package/dist/cjs/services/clients/twilioSmsSender.client.js +41 -0
  21. package/dist/cjs/services/clients/twilioSmsSender.client.js.map +1 -0
  22. package/dist/cjs/services/clients/vonageSmsSender.client.js +34 -0
  23. package/dist/cjs/services/clients/vonageSmsSender.client.js.map +1 -0
  24. package/dist/cjs/services/index.js +18 -0
  25. package/dist/cjs/services/index.js.map +1 -0
  26. package/dist/cjs/services/smsSender.service.js +17 -0
  27. package/dist/cjs/services/smsSender.service.js.map +1 -0
  28. package/dist/cjs/services/smsSenderFactory.service.js +45 -0
  29. package/dist/cjs/services/smsSenderFactory.service.js.map +1 -0
  30. package/dist/cjs/types/index.js +19 -0
  31. package/dist/cjs/types/index.js.map +1 -0
  32. package/dist/cjs/types/sendResult.type.js +3 -0
  33. package/dist/cjs/types/sendResult.type.js.map +1 -0
  34. package/dist/cjs/types/smsProviderModule.type.js +3 -0
  35. package/dist/cjs/types/smsProviderModule.type.js.map +1 -0
  36. package/dist/cjs/utils/constants.js +9 -0
  37. package/dist/cjs/utils/constants.js.map +1 -0
  38. package/dist/esm/awsSmsSender.client-DGZ2JYIG.mjs +41 -0
  39. package/dist/esm/awsSmsSender.client-DGZ2JYIG.mjs.map +1 -0
  40. package/dist/esm/azureSmsSender.client-K6MPTIU7.mjs +36 -0
  41. package/dist/esm/azureSmsSender.client-K6MPTIU7.mjs.map +1 -0
  42. package/dist/esm/chunk-NCQDKWNI.mjs +26 -0
  43. package/dist/esm/chunk-NCQDKWNI.mjs.map +1 -0
  44. package/dist/esm/index.d.mts +20 -0
  45. package/dist/esm/index.mjs +36 -0
  46. package/dist/esm/index.mjs.map +1 -0
  47. package/dist/esm/twilioSmsSender.client-2Z4R25US.mjs +42 -0
  48. package/dist/esm/twilioSmsSender.client-2Z4R25US.mjs.map +1 -0
  49. package/dist/esm/vonageSmsSender.client-23U65FBQ.mjs +38 -0
  50. package/dist/esm/vonageSmsSender.client-23U65FBQ.mjs.map +1 -0
  51. package/dist/types/index.d.ts +20 -0
  52. package/jest.config.ts +15 -0
  53. package/package.json +80 -0
  54. package/src/errors/index.test.ts +13 -0
  55. package/src/errors/index.ts +2 -0
  56. package/src/errors/notProvidedCredentials.error.ts +6 -0
  57. package/src/errors/unknownProvider.error.ts +6 -0
  58. package/src/index.test.ts +7 -0
  59. package/src/index.ts +2 -0
  60. package/src/interfaces/index.ts +1 -0
  61. package/src/interfaces/smsSender.interface.ts +5 -0
  62. package/src/services/clients/awsSmsSender.client.test.ts +39 -0
  63. package/src/services/clients/awsSmsSender.client.ts +57 -0
  64. package/src/services/clients/azureSmsSender.client.test.ts +46 -0
  65. package/src/services/clients/azureSmsSender.client.ts +38 -0
  66. package/src/services/clients/index.test.ts +50 -0
  67. package/src/services/clients/index.ts +4 -0
  68. package/src/services/clients/twilioSmsSender.client.test.ts +48 -0
  69. package/src/services/clients/twilioSmsSender.client.ts +52 -0
  70. package/src/services/clients/voangeSmsSender.client.test.ts +48 -0
  71. package/src/services/clients/vonageSmsSender.client.ts +49 -0
  72. package/src/services/index.test.ts +12 -0
  73. package/src/services/index.ts +1 -0
  74. package/src/services/smsSender.service.ts +16 -0
  75. package/src/services/smsSenderFactory.service.test.ts +16 -0
  76. package/src/services/smsSenderFactory.service.ts +24 -0
  77. package/src/types/index.ts +2 -0
  78. package/src/types/sendResult.type.ts +9 -0
  79. package/src/types/smsProviderModule.type.ts +5 -0
  80. package/src/utils/constants.ts +6 -0
  81. package/tsconfig.cjs.json +7 -0
  82. package/tsconfig.json +17 -0
  83. package/tsup.config.cjs.ts +9 -0
  84. package/tsup.config.esm.ts +10 -0
  85. package/tsup.config.types.ts +9 -0
@@ -0,0 +1,48 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import TwilioSMSSenderClient from './twilioSmsSender.client';
3
+ import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message';
4
+
5
+ jest.mock('twilio', () => {
6
+ const actualModule = jest.requireActual('twilio');
7
+ class Twilio {
8
+ messages = {
9
+ create: ({
10
+ from,
11
+ to,
12
+ body,
13
+ }: {
14
+ from: string;
15
+ to: string;
16
+ body: string;
17
+ }): Promise<MessageInstance> =>
18
+ Promise.resolve({ to } as MessageInstance),
19
+ };
20
+ }
21
+ return {
22
+ ...actualModule,
23
+ Twilio,
24
+ };
25
+ });
26
+
27
+ jest.mock('../../utils/constants', () => ({
28
+ USERNAME: 'qrvey',
29
+ PASSWORD: '123456',
30
+ }));
31
+
32
+ describe('TwilioSMSSender tests.', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ jest.resetAllMocks();
36
+ });
37
+
38
+ test('Should response with sent messages.', async () => {
39
+ const smsSender = new TwilioSMSSenderClient();
40
+ const result = await smsSender.send(
41
+ ['+1234567890', '+9876543210'],
42
+ 'Hello world',
43
+ );
44
+ expect(result).toMatchObject({
45
+ sent: [{ phone: '+1234567890' }, { phone: '+9876543210' }],
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,52 @@
1
+ import { ISMSSender } from '../../interfaces/smsSender.interface';
2
+ import { Twilio } from 'twilio';
3
+ import { USERNAME, PASSWORD, FROM } from '../../utils/constants';
4
+ import { MessageInstance } from 'twilio/lib/rest/api/v2010/account/message';
5
+ import { SendResult } from '../../types/sendResult.type';
6
+ import { NotProvidedCredentialsError } from '../../errors';
7
+
8
+ export default class TwilioSMSSenderClient implements ISMSSender {
9
+ private client: Twilio;
10
+ constructor() {
11
+ if (USERNAME && PASSWORD) this.client = new Twilio(USERNAME, PASSWORD);
12
+ else throw new NotProvidedCredentialsError();
13
+ }
14
+
15
+ public async send(
16
+ phones: string[] = [],
17
+ message: string,
18
+ ): Promise<SendResult> {
19
+ const sentMessages = await Promise.all(
20
+ phones.map((phone) => this._send(phone, message)),
21
+ );
22
+
23
+ const result: SendResult = {
24
+ sent: [],
25
+ failed: [],
26
+ };
27
+ sentMessages.forEach((message) => {
28
+ if (message.errorMessage) {
29
+ result.failed.push({
30
+ phone: message.to,
31
+ error: message.errorMessage,
32
+ });
33
+ } else {
34
+ result.sent.push({
35
+ phone: message.to,
36
+ });
37
+ }
38
+ });
39
+
40
+ return result;
41
+ }
42
+
43
+ private _send = (
44
+ phone: string,
45
+ message: string,
46
+ ): Promise<MessageInstance> =>
47
+ this.client.messages.create({
48
+ from: FROM,
49
+ to: phone,
50
+ body: message,
51
+ });
52
+ }
@@ -0,0 +1,48 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { SMSMessages } from '@vonage/sms/dist/lib/types/SMSMessages';
3
+ import VonageSMSSenderClient from './vonageSmsSender.client';
4
+
5
+ jest.mock('@vonage/server-sdk', () => {
6
+ const actualModule = jest.requireActual('@vonage/server-sdk');
7
+ class Vonage {
8
+ sms = {
9
+ send: ({
10
+ from,
11
+ to,
12
+ body,
13
+ }: {
14
+ from: string;
15
+ to: string;
16
+ body: string;
17
+ }): Promise<SMSMessages> => Promise.resolve({} as SMSMessages),
18
+ };
19
+ }
20
+ return {
21
+ ...actualModule,
22
+ Vonage,
23
+ };
24
+ });
25
+
26
+ jest.mock('../../utils/constants', () => ({
27
+ FROM: '+123456789',
28
+ USERNAME: 'qrvey',
29
+ PASSWORD: '123456',
30
+ }));
31
+
32
+ describe('AwsSMSSender tests.', () => {
33
+ beforeEach(() => {
34
+ jest.clearAllMocks();
35
+ jest.resetAllMocks();
36
+ });
37
+
38
+ test('Should call send method.', async () => {
39
+ const smsSender = new VonageSMSSenderClient();
40
+ const result = await smsSender.send(
41
+ ['+1234567890', '+9876543210'],
42
+ 'Hello world',
43
+ );
44
+ expect(result).toMatchObject({
45
+ sent: [{ phone: '+1234567890' }, { phone: '+9876543210' }],
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,49 @@
1
+ import { ISMSSender } from '../../interfaces';
2
+ import { SendResult } from '../../types';
3
+ import { Vonage } from '@vonage/server-sdk';
4
+ import { Auth } from '@vonage/auth';
5
+ import { USERNAME, PASSWORD, FROM } from '../../utils/constants';
6
+ import { NotProvidedCredentialsError } from '../../errors';
7
+ import { SMSMessages } from '@vonage/sms/dist/lib/types/SMSMessages';
8
+
9
+ export default class VonageSMSSenderClient implements ISMSSender {
10
+ private client: Vonage;
11
+ constructor() {
12
+ if (!(USERNAME && PASSWORD && FROM))
13
+ throw new NotProvidedCredentialsError();
14
+ const vonageAuth = new Auth({
15
+ apiKey: USERNAME,
16
+ apiSecret: PASSWORD,
17
+ });
18
+ this.client = new Vonage(vonageAuth);
19
+ }
20
+
21
+ public send = async (
22
+ phones: string[],
23
+ message: string,
24
+ ): Promise<SendResult> => {
25
+ const result: SendResult = {
26
+ sent: [],
27
+ failed: [],
28
+ };
29
+
30
+ await Promise.all(
31
+ phones.map((phone) =>
32
+ this._send(phone, message)
33
+ .then(() => result.sent.push({ phone }))
34
+ .catch((error) =>
35
+ result.failed.push({ phone, error: error.message }),
36
+ ),
37
+ ),
38
+ );
39
+
40
+ return result;
41
+ };
42
+
43
+ private _send = (phone: string, message: string): Promise<SMSMessages> =>
44
+ this.client.sms.send({
45
+ to: phone,
46
+ text: message,
47
+ from: FROM as string,
48
+ });
49
+ }
@@ -0,0 +1,12 @@
1
+ import { SMSSenderService } from './smsSender.service';
2
+ import { SMSSenderFactory } from './smsSenderFactory.service';
3
+
4
+ describe('Services tests.', () => {
5
+ test('SMSSender should be defined.', () => {
6
+ expect(SMSSenderService).toBeDefined();
7
+ });
8
+
9
+ test('SMSSenderFactory should be defined.', () => {
10
+ expect(SMSSenderFactory).toBeDefined();
11
+ });
12
+ });
@@ -0,0 +1 @@
1
+ export * from './smsSender.service';
@@ -0,0 +1,16 @@
1
+ import { SendResult } from '../types';
2
+ import { SMSSenderFactory } from './smsSenderFactory.service';
3
+
4
+ export class SMSSenderService {
5
+ /**
6
+ * Send a message to one or more phones.
7
+ * @param phones - phone numbers to send to
8
+ * @param message - message to send
9
+ * @returns {Promise<SendResult>} Send result.
10
+ */
11
+ public send = async (
12
+ phones: string[],
13
+ message: string,
14
+ ): Promise<SendResult> =>
15
+ (await SMSSenderFactory.getSMSSender()).send(phones, message);
16
+ }
@@ -0,0 +1,16 @@
1
+ import { SMSSenderFactory } from './smsSenderFactory.service';
2
+ import { UnknownProviderError } from '../errors';
3
+
4
+ jest.mock('../utils/constants', () => ({
5
+ FROM: '+123456789',
6
+ USERNAME: 'qrvey',
7
+ PASSWORD: '123456',
8
+ REGION: 'us-east-1',
9
+ }));
10
+ describe('SMSSenderFactory tests.', () => {
11
+ test('Should return AwsSMSSender.', async () => {
12
+ await expect(SMSSenderFactory.getSMSSender()).rejects.toThrow(
13
+ new UnknownProviderError(),
14
+ );
15
+ });
16
+ });
@@ -0,0 +1,24 @@
1
+ import { UnknownProviderError } from '../errors';
2
+ import { ISMSSender } from '../interfaces';
3
+ import { SMSProviderModule } from '../types';
4
+ import { SMS_PROVIDER } from '../utils/constants';
5
+
6
+ export class SMSSenderFactory {
7
+ private static providerModules: Record<
8
+ string,
9
+ () => Promise<SMSProviderModule>
10
+ > = {
11
+ twilio: () => import('./clients/twilioSmsSender.client'),
12
+ aws: () => import('./clients/awsSmsSender.client'),
13
+ azure: () => import('./clients/azureSmsSender.client'),
14
+ vonage: () => import('./clients/vonageSmsSender.client'),
15
+ };
16
+
17
+ static async getSMSSender(): Promise<ISMSSender> {
18
+ const provider = SMS_PROVIDER?.toLowerCase();
19
+ if (!provider) throw new UnknownProviderError();
20
+
21
+ const { default: SMSSender } = await this.providerModules[provider]();
22
+ return new SMSSender();
23
+ }
24
+ }
@@ -0,0 +1,2 @@
1
+ export * from './sendResult.type';
2
+ export * from './smsProviderModule.type';
@@ -0,0 +1,9 @@
1
+ export type SendResult = {
2
+ sent: SendMessageResult[];
3
+ failed: SendMessageResult[];
4
+ };
5
+
6
+ type SendMessageResult = {
7
+ phone: string;
8
+ error?: string;
9
+ };
@@ -0,0 +1,5 @@
1
+ import { ISMSSender } from '../interfaces';
2
+
3
+ export type SMSProviderModule = {
4
+ default: new () => ISMSSender;
5
+ };
@@ -0,0 +1,6 @@
1
+ export const SMS_PROVIDER = process.env.SMS_PROVIDER;
2
+
3
+ export const USERNAME = process.env.SMS_PROVIDER_USERNAME;
4
+ export const PASSWORD = process.env.SMS_PROVIDER_PASSWORD;
5
+ export const FROM = process.env.SMS_PROVIDER_FROM_NUMBER;
6
+ export const REGION = process.env.SMS_PROVIDER_REGION;
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist/cjs",
5
+ "module": "commonjs"
6
+ }
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "tsconfig/tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist"
5
+ },
6
+ "include": ["src/index.ts"],
7
+ "exclude": [
8
+ "dist",
9
+ "build",
10
+ "node_modules",
11
+ "coverage",
12
+ "**/*.test.ts",
13
+ "**/*.test.d.ts",
14
+ "**/*.spec.ts",
15
+ "**/__tests__",
16
+ ]
17
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entryPoints: ['./src/index.ts'],
5
+ format: ['cjs'],
6
+ treeshake: true,
7
+ sourcemap: true,
8
+ outDir: 'dist/cjs',
9
+ });
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entryPoints: ['./src/index.ts'],
5
+ format: ['esm'],
6
+ treeshake: true,
7
+ sourcemap: true,
8
+ dts: true,
9
+ outDir: 'dist/esm',
10
+ });
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entryPoints: ['./src/index.ts'],
5
+ dts: {
6
+ only: true,
7
+ },
8
+ outDir: 'dist/types',
9
+ });