diff --git a/package.json b/package.json index 6b757c3..eb9eb61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "notificationapi-node-server-sdk", - "version": "0.17.2", + "version": "0.18.0", "description": "NotificationAPI server-side library for Node.js", "keywords": [ "notificationapi", diff --git a/src/__tests__/index.test.ts b/src/__tests__/index.test.ts index e11f23c..cfe9db0 100644 --- a/src/__tests__/index.test.ts +++ b/src/__tests__/index.test.ts @@ -7,10 +7,12 @@ import { Channels, CreateSubNotificationRequest, DeleteSubNotificationRequest, + PushProviders, SendRequest, SetUserPreferencesRequest, User } from '../interfaces'; +import { createHmac } from 'crypto'; const axiosMock = new MockAdapter(axios); const restoreConsole = mockConsole(); @@ -502,3 +504,57 @@ describe('setUserPreferences with subNotificationId', () => { ); }); }); +describe('Identify user', () => { + const userEndPointRegex = /.*\/users\/.*/; + const clientId = 'testClientId_identify_user'; + const clientSecret = 'testClientSecret_identify_user'; + const userId = 'testUserId_identify_user'; + const user: User = { + id: userId, + email: 'test+node_server_sdk@notificationapi.com', + number: '+15005550006', + pushTokens: [ + { + type: PushProviders.FCM, + token: 'samplePushToken', + device: { + app_id: 'sample_app_id', + ad_id: 'sample_ad_id', + device_id: 'sample_device_id', + platform: 'sample_platform', + manufacturer: 'sample_manufacturer', + model: 'sample_model' + } + } + ], + webPushTokens: [ + { + sub: { + endpoint: 'sample_endpoint', + keys: { + p256dh: 'sample_p256dh', + auth: 'sample_auth' + } + } + } + ] + }; + test('makes API calls with a correct request body', async () => { + axiosMock.onPost(userEndPointRegex).reply(200); + await notificationapi.init(clientId, clientSecret); + await notificationapi.identifyUser(user); + const { id, ...userData } = user; + expect(axiosMock.history.post).toHaveLength(1); + expect(axiosMock.history.post[0].data).toEqual(JSON.stringify(userData)); + expect(axiosMock.history.post[0].url).toEqual( + `https://api.notificationapi.com/${clientId}/users/${id}` + ); + expect(axiosMock.history.post[0].headers.Authorization).toEqual( + `Basic ${Buffer.from( + `${clientId}:${userId}:${createHmac('sha256', clientSecret) + .update(`${userId}`) + .digest('base64')}` + ).toString('base64')}` + ); + }); +}); diff --git a/src/index.ts b/src/index.ts index e620d22..254efc6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,9 +4,11 @@ import { InitConfiguration, RetractRequest, SendRequest, - SetUserPreferencesRequest + SetUserPreferencesRequest, + User } from './interfaces'; import axios, { AxiosResponse, Method } from 'axios'; +import { createHmac } from 'crypto'; const DEFAULT_BASE_URL = 'https://api.notificationapi.com'; @@ -37,6 +39,24 @@ class NotificationAPI { this.clientId = clientId; this.clientSecret = clientSecret; }; + /** used to identify your user */ + identifyUser = async (user: User): Promise => { + const { id, ...userData } = user; + const hashedUserId = `${createHmac('sha256', this.clientSecret as string) + .update(id) + .digest('base64')}`; + + const customAuthorization = + 'Basic ' + + Buffer.from(`${this.clientId}:${id}:${hashedUserId}`).toString('base64'); + + return this.request( + 'POST', + `users/${encodeURIComponent(id)}`, + userData, + customAuthorization + ); + }; /** Used to send a notification to the specified user. */ send = async (sendRequest: SendRequest): Promise => { return this.request('POST', 'sender', sendRequest); @@ -77,19 +97,20 @@ class NotificationAPI { request = async ( method: Method, uri: string, - data?: unknown + data?: unknown, + customAuthorization?: string ): Promise => { + const authorization: string = + customAuthorization ?? + 'Basic ' + + Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64'); try { const res = await axios.request({ method, url: `${this.baseURL}/${this.clientId}/${uri}`, data, headers: { - Authorization: - 'Basic ' + - Buffer.from(`${this.clientId}:${this.clientSecret}`).toString( - 'base64' - ) + Authorization: authorization } }); if (res.status === 202) { diff --git a/src/interfaces.ts b/src/interfaces.ts index d627bd7..f137bd6 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -5,6 +5,10 @@ export interface User { email?: string; /** Required for SMS/CALL notifications, otherwise optional. Valid format: +15005550006. Unformatted US/Canada numbers are also accepted, e.g., (415) 555-1212, 415-555-1212, or 4155551212.*/ number?: string; + /**Tokens which are required to send mobile push notifications, a user can have multiple devices and a push token is required for each device*/ + pushTokens?: PushToken[]; + /**Tokens which are required to send web push notification, a user can have multiple devices and a web push token is required for each device */ + webPushTokens?: WebPushToken[]; } export interface SendRequest { @@ -95,3 +99,53 @@ export interface InitConfiguration { /** To updated the based url. Optional.*/ baseURL?: string; } + +export interface PushToken { + /**[apn | fcm] The provider token is to be associated with. */ + type: PushProviders; + /**The full token string. */ + token: string; + /**Information about the device the token is associated with. */ + device: Device; +} + +export enum PushProviders { + /**firebase-fcm token provider */ + FCM = 'FCM', + /**APN token provider */ + APN = 'APN' +} + +export interface Device { + /**Id of the application the token is used for */ + app_id?: string; + /**Id of the advertising identifier */ + ad_id?: string; + /**Id of the device the token is associated with */ + device_id: string; + /**The device platform i.e. android, ios*/ + platform?: string; + /**The device manufacturer */ + manufacturer?: string; + /**The device model */ + model?: string; +} + +/** + * Configuration for a Push Subscription. This can be obtained on the frontend by calling + * serviceWorkerRegistration.pushManager.subscribe(). + * The expected format is the same output as JSON.stringify'ing a PushSubscription in the browser. + */ +export interface PushSubscription { + /**a string value containing the endpoint associated with the push subscription. */ + endpoint: string; + keys: { + /**An Elliptic curve Diffie–Hellman public key on the P-256 curve (that is, the NIST secp256r1 elliptic curve). The resulting key is an uncompressed point in ANSI X9.62 format. */ + p256dh: string; + /**An authentication secret, as described in Message Encryption for Web Push. */ + auth: string; + }; +} +export interface WebPushToken { + sub: PushSubscription; +}