diff --git a/__test__/unit/http/sdkVersion.test.ts b/__test__/unit/http/sdkVersion.test.ts index 9779919cc..c1d43d780 100644 --- a/__test__/unit/http/sdkVersion.test.ts +++ b/__test__/unit/http/sdkVersion.test.ts @@ -120,11 +120,17 @@ describe('Sdk Version Header Tests', () => { expectHeaderToBeSent(); }); test('PATCH /subscriptions/: header is sent', () => { - RequestService.updateSubscription({ appId: APP_ID }, DUMMY_EXTERNAL_ID, {}); + RequestService.updateSubscription( + { appId: APP_ID, subscriptionId: DUMMY_SUBSCRIPTION_ID }, + {}, + ); expectHeaderToBeSent(); }); test('DELETE /subscriptions/: header is sent', () => { - RequestService.deleteSubscription({ appId: APP_ID }, DUMMY_EXTERNAL_ID); + RequestService.updateSubscription( + { appId: APP_ID, subscriptionId: DUMMY_SUBSCRIPTION_ID }, + {}, + ); expectHeaderToBeSent(); }); }); diff --git a/src/core/models/RequestMetadata.ts b/src/core/models/RequestMetadata.ts index 78e67d49c..de30e2514 100644 --- a/src/core/models/RequestMetadata.ts +++ b/src/core/models/RequestMetadata.ts @@ -1,7 +1,7 @@ -import { APIHeaders } from '../../shared/models/APIHeaders'; - export type RequestMetadata = { + // path parameters appId: string; subscriptionId?: string; - jwtHeader?: APIHeaders; + // sent as header + jwtToken?: string; }; diff --git a/src/core/requestService/RequestService.ts b/src/core/requestService/RequestService.ts index 896769e25..1e83f3f6f 100644 --- a/src/core/requestService/RequestService.ts +++ b/src/core/requestService/RequestService.ts @@ -15,34 +15,25 @@ import { SdkInitError, SdkInitErrorKind, } from '../../shared/errors/SdkInitError'; +import { addJwtHeader, addOneSignalSubscriptionIdHeader } from './helpers'; export class RequestService { /* U S E R O P E R A T I O N S */ /** * Creates a new user - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, subscriptionId, jwtToken } * @param requestBody - The user's properties, identity, and subscriptions */ static async createUser( requestMetadata: RequestMetadata, requestBody: CreateUserPayload, ): Promise { - const { appId, subscriptionId } = requestMetadata; - - const subscriptionHeader = subscriptionId - ? { 'OneSignal-Subscription-Id': subscriptionId } - : undefined; - - let headers = {}; + const { appId, subscriptionId, jwtToken } = requestMetadata; - if (subscriptionHeader) { - headers = { ...headers, ...subscriptionHeader }; - } - - if (requestMetadata.jwtHeader) { - headers = { ...headers, ...requestMetadata.jwtHeader }; - } + const headers = new Headers(); + addOneSignalSubscriptionIdHeader(headers, subscriptionId); + addJwtHeader(headers, jwtToken); requestBody['refresh_device_metadata'] = true; @@ -51,7 +42,7 @@ export class RequestService { /** * Returns the user's properties, aliases, and subscriptions - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - The user's alias * @returns - A promise that resolves with the user's properties, identity, and subscriptions */ @@ -59,44 +50,38 @@ export class RequestService { requestMetadata: RequestMetadata, alias: AliasPair, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.get( `apps/${appId}/users/by/${alias.label}/${alias.id}`, - requestMetadata.jwtHeader, + headers, ); } /** * Updates an existing user's properties * - Aliases and subscriptions are managed via other endpoints - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, subscriptionId, jwtToken } * @param alias - alias label & id - * @param payload - update user payload + * @param requestBody - update user payload * @returns no body */ static async updateUser( requestMetadata: RequestMetadata, alias: AliasPair, - payload: UpdateUserPayload, + requestBody: UpdateUserPayload, ): Promise { - const { appId, subscriptionId } = requestMetadata; + const { appId, subscriptionId, jwtToken } = requestMetadata; if (!OneSignalUtils.isValidUuid(appId)) { throw new SdkInitError(SdkInitErrorKind.InvalidAppId); } - const subscriptionHeader = subscriptionId - ? { 'OneSignal-Subscription-Id': subscriptionId } - : undefined; - - let headers = {}; - - if (subscriptionHeader) { - headers = { ...headers, ...subscriptionHeader }; - } - - if (requestMetadata.jwtHeader) { - headers = { ...headers, ...requestMetadata.jwtHeader }; - } + const headers = new Headers(); + addOneSignalSubscriptionIdHeader(headers, subscriptionId); + addJwtHeader(headers, jwtToken); const sanitizedAlias = { label: encodeRFC3986URIComponent(alias.label), @@ -105,24 +90,28 @@ export class RequestService { return OneSignalApiBase.patch( `apps/${appId}/users/by/${sanitizedAlias.label}/${sanitizedAlias.id}`, - payload, + requestBody, headers, ); } /** * Removes the user identified by the given alias pair, and all subscriptions and aliases - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - alias label & id */ static async deleteUser( requestMetadata: RequestMetadata, alias: AliasPair, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.delete( `apps/${appId}/users/by/${alias.label}/${alias.id}`, - requestMetadata.jwtHeader, + headers, ); } @@ -130,7 +119,7 @@ export class RequestService { /** * Upserts one or more aliases for the user identified by the given alias pair - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - alias label & id * @param identity - identity label & id */ @@ -139,33 +128,41 @@ export class RequestService { alias: AliasPair, identity: SupportedIdentity, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.patch( `apps/${appId}/users/by/${alias.label}/${alias.id}/identity`, { identity }, - requestMetadata.jwtHeader, + headers, ); } /** * Lists all aliases for the user identified by the given alias pair - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - alias label & id */ static async getUserIdentity( requestMetadata: RequestMetadata, alias: AliasPair, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.get( `apps/${appId}/users/by/${alias.label}/${alias.id}/identity`, - requestMetadata.jwtHeader, + headers, ); } /** * Deletes an alias for the user identified by the given alias pair - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - alias label & id * @param labelToRemove - label of identity to remove */ @@ -174,10 +171,14 @@ export class RequestService { alias: AliasPair, labelToRemove: string, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.delete( `apps/${appId}/users/by/${alias.label}/${alias.id}/identity/${labelToRemove}`, - requestMetadata.jwtHeader, + headers, ); } @@ -186,7 +187,7 @@ export class RequestService { /** * Creates a new subscription for the user identified by the given alias pair * Useful to add email or SMS subscriptions to a user - * @param requestMetadata - { appId } + * @param requestMetadata - { appId, jwtToken } * @param alias - alias label & id * @param subscription - subscription label & id */ @@ -195,26 +196,29 @@ export class RequestService { alias: AliasPair, subscription: { subscription: FutureSubscriptionModel }, ): Promise { - const { appId } = requestMetadata; + const { appId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.post( `apps/${appId}/users/by/${alias.label}/${alias.id}/subscriptions`, subscription, - requestMetadata.jwtHeader, + headers, ); } /** * Updates an existing Subscription’s properties. - * @param requestMetadata - { appId } - * @param subscriptionId - subscription id + * @param requestMetadata - { appId, subscriptionId } * @param subscription - subscription object */ static async updateSubscription( requestMetadata: RequestMetadata, - subscriptionId: string, subscription: Partial, ): Promise { - const { appId } = requestMetadata; + const { appId, subscriptionId } = requestMetadata; + return OneSignalApiBase.patch( `apps/${appId}/subscriptions/${subscriptionId}`, { subscription }, @@ -224,29 +228,31 @@ export class RequestService { /** * Deletes the subscription. * Creates an "orphan" user record if the user has no other subscriptions. - * @param requestMetadata - { appId } - * @param subscriptionId - subscription id + * @param requestMetadata - { appId, subscriptionId, jwtToken } */ static async deleteSubscription( requestMetadata: RequestMetadata, - subscriptionId: string, ): Promise { - const { appId } = requestMetadata; + const { appId, subscriptionId, jwtToken } = requestMetadata; + + const headers = new Headers(); + addJwtHeader(headers, jwtToken); + return OneSignalApiBase.delete( `apps/${appId}/subscriptions/${subscriptionId}`, + headers, ); } /** * Lists all aliases for the user identified by the given subscription id - * @param requestMetadata - { appId } - * @param subscriptionId - subscription id + * @param requestMetadata - { appId, subscriptionId } */ static async fetchAliasesForSubscription( requestMetadata: RequestMetadata, - subscriptionId: string, ): Promise { - const { appId } = requestMetadata; + const { appId, subscriptionId } = requestMetadata; + return OneSignalApiBase.get( `apps/${appId}/subscriptions/${subscriptionId}/identity`, ); @@ -254,27 +260,24 @@ export class RequestService { /** * Upserts one or more aliases for the user identified by the given subscription id - * @param requestMetadata - { appId } - * @param subscriptionId - subscription id + * @param requestMetadata - { appId, subscriptionId } * @param identity - identity label & id */ static async identifyUserForSubscription( requestMetadata: RequestMetadata, - subscriptionId: string, identity: IdentityModel, ): Promise { - const { appId } = requestMetadata; + const { appId, subscriptionId } = requestMetadata; + return OneSignalApiBase.patch( `apps/${appId}/users/by/subscriptions/${subscriptionId}/identity`, { identity }, - requestMetadata.jwtHeader, ); } /** * Transfers this Subscription to the User identified by the identity in the payload. - * @param requestMetadata - { appId } - * @param subscriptionId - subscription id + * @param requestMetadata - { appId, subscriptionId } * @param identity - identity label & id * @param retainPreviousOwner - if true *AND* subscription is last subscription for the previous * user, an orphan user will be created. Otherwise, the previous user will be deleted. Useful when going @@ -283,18 +286,17 @@ export class RequestService { */ static async transferSubscription( requestMetadata: RequestMetadata, - subscriptionId: string, identity: SupportedIdentity, retainPreviousOwner: boolean, ): Promise { - const { appId } = requestMetadata; + const { appId, subscriptionId } = requestMetadata; + return OneSignalApiBase.patch( `apps/${appId}/subscriptions/${subscriptionId}/owner`, { identity: { ...identity }, retain_previous_owner: retainPreviousOwner, }, - requestMetadata.jwtHeader, ); } } diff --git a/src/core/requestService/SubscriptionRequests.ts b/src/core/requestService/SubscriptionRequests.ts index 4d6ff13a0..ed514a54d 100644 --- a/src/core/requestService/SubscriptionRequests.ts +++ b/src/core/requestService/SubscriptionRequests.ts @@ -56,10 +56,10 @@ export default class SubscriptionRequests { } const appId = await MainHelper.getAppId(); - const response = await RequestService.deleteSubscription( - { appId }, + const response = await RequestService.deleteSubscription({ + appId, subscriptionId, - ); + }); return SubscriptionRequests._processSubscriptionResponse(response); } @@ -83,8 +83,7 @@ export default class SubscriptionRequests { const appId = await MainHelper.getAppId(); const response = await RequestService.updateSubscription( - { appId }, - subscriptionId, + { appId, subscriptionId }, payload, ); return SubscriptionRequests._processSubscriptionResponse(response); diff --git a/src/core/requestService/helpers.ts b/src/core/requestService/helpers.ts index 4269c9b28..3d1d1a85e 100644 --- a/src/core/requestService/helpers.ts +++ b/src/core/requestService/helpers.ts @@ -9,8 +9,6 @@ import { isCompleteSubscriptionObject, } from '../utils/typePredicates'; import AliasPair from './AliasPair'; -import { APIHeaders } from '../../shared/models/APIHeaders'; - export function processSubscriptionOperation( operation: Operation, ): { @@ -95,3 +93,18 @@ export function processIdentityOperation(operation: Operation): { aliasPair: new AliasPair(AliasPair.ONESIGNAL_ID, onesignalId), }; } + +export function addJwtHeader(header: Headers, jwtToken?: string) { + if (jwtToken) { + header.append('Authorization', `Bearer ${jwtToken}`); + } +} + +export function addOneSignalSubscriptionIdHeader( + header: Headers, + subscriptionId?: string, +) { + if (subscriptionId) { + header.append('OneSignal-Subscription-Id', subscriptionId); + } +} diff --git a/src/page/managers/LoginManager.ts b/src/page/managers/LoginManager.ts index e95c966ef..6e08a50d3 100644 --- a/src/page/managers/LoginManager.ts +++ b/src/page/managers/LoginManager.ts @@ -407,8 +407,7 @@ export default class LoginManager { const retainPreviousOwner = false; const transferResponse = await RequestService.transferSubscription( - { appId }, - pushSubscriptionId, + { appId, subscriptionId: pushSubscriptionId }, identity, retainPreviousOwner, ); diff --git a/src/shared/api/OneSignalApiBase.ts b/src/shared/api/OneSignalApiBase.ts index 544813cf9..f3e7b5b7a 100644 --- a/src/shared/api/OneSignalApiBase.ts +++ b/src/shared/api/OneSignalApiBase.ts @@ -5,91 +5,93 @@ import { import OneSignalError from '../errors/OneSignalError'; import Environment from '../helpers/Environment'; import Log from '../libraries/Log'; +import EventHelper from '../helpers/EventHelper'; import SdkEnvironment from '../managers/SdkEnvironment'; -import { APIHeaders } from '../models/APIHeaders'; import { awaitableTimeout } from '../utils/AwaitableTimeout'; import { isValidUuid } from '../utils/utils'; import OneSignalApiBaseResponse from './OneSignalApiBaseResponse'; import { RETRY_BACKOFF } from './RetryBackoff'; type SupportedMethods = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; +const OS_API_VERSION = '1'; export class OneSignalApiBase { static get( - action: string, - data?: any, - headers?: APIHeaders | undefined, + url: string, + headers?: Headers, ): Promise { - return OneSignalApiBase.call('GET', action, data, headers); + return OneSignalApiBase.makeRequest('GET', url, undefined, headers); } static post( - action: string, - data?: any, - headers?: APIHeaders | undefined, + url: string, + body?: any, + headers?: Headers, ): Promise { - return OneSignalApiBase.call('POST', action, data, headers); + return OneSignalApiBase.makeRequest('POST', url, body, headers); } static put( - action: string, - data?: any, - headers?: APIHeaders | undefined, + url: string, + body?: any, + headers?: Headers, ): Promise { - return OneSignalApiBase.call('PUT', action, data, headers); + return OneSignalApiBase.makeRequest('PUT', url, body, headers); } static delete( - action: string, - data?: any, - headers?: APIHeaders | undefined, + url: string, + headers?: Headers, ): Promise { - return OneSignalApiBase.call('DELETE', action, data, headers); + return OneSignalApiBase.makeRequest('DELETE', url, undefined, headers); } static patch( - action: string, - data?: any, - headers?: APIHeaders | undefined, + url: string, + body?: any, + headers?: Headers, ): Promise { - return OneSignalApiBase.call('PATCH', action, data, headers); + return OneSignalApiBase.makeRequest('PATCH', url, body, headers); } - private static call( + private static async makeRequest( method: SupportedMethods, - action: string, - data: any, - headers: APIHeaders | undefined, + url: string, + body?: any, + headers?: Headers, ): Promise { - if (!this.requestHasAppId(action, data)) { + if (!this.requestHasAppId(url, body)) { return Promise.reject( new OneSignalApiError(OneSignalApiErrorKind.MissingAppId), ); } - const callHeaders = new Headers(); - callHeaders.append('Origin', SdkEnvironment.getOrigin()); - callHeaders.append('SDK-Version', `onesignal/web/${Environment.version()}`); - callHeaders.append('Content-Type', 'application/json;charset=UTF-8'); + const requestHeaders = new Headers({ + Origin: SdkEnvironment.getOrigin(), + 'SDK-Version': `onesignal/web/${Environment.version()}`, + 'Content-Type': 'application/json;charset=UTF-8', + }); + if (headers) { - for (const key of Object.keys(headers)) { - callHeaders.append(key, headers[key]); - } + headers.forEach((value, key) => { + requestHeaders.append(key, value); + }); } const contents: RequestInit = { method: method || 'NO_METHOD_SPECIFIED', - headers: callHeaders, + headers: requestHeaders, cache: 'no-cache', }; - if (data) contents.body = JSON.stringify(data); - const url = `${SdkEnvironment.getOneSignalApiUrl( + if (body) contents.body = JSON.stringify(body); + + const requestUrl = `${SdkEnvironment.getOneSignalApiUrl( undefined, - action, - ).toString()}/${action}`; + url, + ).toString()}/${url}`; - return OneSignalApiBase.executeFetch(url, contents); + return OneSignalApiBase.executeFetch(requestUrl, contents); } private static async executeFetch( diff --git a/src/shared/api/OneSignalApiSW.ts b/src/shared/api/OneSignalApiSW.ts index f72b1e8e0..4e5a7ef95 100644 --- a/src/shared/api/OneSignalApiSW.ts +++ b/src/shared/api/OneSignalApiSW.ts @@ -17,7 +17,7 @@ export class OneSignalApiSW { appId: string, ): Promise { Utils.enforceAppId(appId); - const response = await OneSignalApiBase.get(`sync/${appId}/web`, null); + const response = await OneSignalApiBase.get(`sync/${appId}/web`); return response?.result; } diff --git a/src/shared/models/APIHeaders.ts b/src/shared/models/APIHeaders.ts deleted file mode 100644 index ab97bdcc5..000000000 --- a/src/shared/models/APIHeaders.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type APIHeaders = { - Authorization?: string; - [key: string]: any; -};