From 87c51390683057b0a3e8fa359eb9370a5b062f26 Mon Sep 17 00:00:00 2001 From: Shepherd Date: Thu, 29 Feb 2024 12:06:46 -0500 Subject: [PATCH 1/5] Add OneSignal-Subscription-Id header to create user Added OneSignal-Subscription-Id header and refresh_device_metadata --- src/core/requestService/RequestService.ts | 26 +++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/core/requestService/RequestService.ts b/src/core/requestService/RequestService.ts index c4ce0d972..442c3b5bb 100644 --- a/src/core/requestService/RequestService.ts +++ b/src/core/requestService/RequestService.ts @@ -28,12 +28,26 @@ export class RequestService { requestMetadata: RequestMetadata, requestBody: Partial, ): Promise { - const { appId } = requestMetadata; - return OneSignalApiBase.post( - `apps/${appId}/users`, - requestBody, - requestMetadata.jwtHeader, - ); + const { appId, subscriptionId } = requestMetadata; + + const subscriptionHeader = subscriptionId + ? { 'OneSignal-Subscription-Id': subscriptionId } + : undefined; + + let headers = {}; + + if (subscriptionHeader) { + headers = { ...headers, ...subscriptionHeader }; + } + + if (requestMetadata.jwtHeader) { + headers = { ...headers, ...requestMetadata.jwtHeader }; + } + + const refreshMetadata = { refresh_device_metadata: true }; + requestBody = { ...requestBody, ...refreshMetadata }; + + return OneSignalApiBase.post(`apps/${appId}/users`, requestBody, headers); } /** From 1616c9095b1187b3985b75c23e02ce3c43cf7767 Mon Sep 17 00:00:00 2001 From: Shepherd Date: Thu, 29 Feb 2024 12:11:37 -0500 Subject: [PATCH 2/5] Add subscriptionId to requests Pass up subscriptionId to use for OneSignal-Subscription-Id header in the create and update user calls --- .../requestService/UserPropertyRequests.ts | 19 ++++++++++++++++--- src/onesignal/UserDirector.ts | 14 +++++++++++++- src/page/managers/LoginManager.ts | 13 ++++++++++++- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/core/requestService/UserPropertyRequests.ts b/src/core/requestService/UserPropertyRequests.ts index 138d5ce99..5dc2ffb5b 100644 --- a/src/core/requestService/UserPropertyRequests.ts +++ b/src/core/requestService/UserPropertyRequests.ts @@ -13,6 +13,7 @@ import { RequestService } from './RequestService'; import MainHelper from '../../shared/helpers/MainHelper'; import OneSignalApiBaseResponse from '../../shared/api/OneSignalApiBaseResponse'; import Log from '../../shared/libraries/Log'; +import { isCompleteSubscriptionObject } from '../utils/typePredicates'; /** * This class contains logic for all the UserProperty model related requests that can be made to the OneSignal API @@ -44,9 +45,21 @@ export default class UserPropertyRequests { ); const appId = await MainHelper.getAppId(); - const response = await RequestService.updateUser({ appId }, aliasPair, { - properties, - }); + const pushSubscription = + await OneSignal.coreDirector.getPushSubscriptionModel(); + + let subscriptionId; + if (isCompleteSubscriptionObject(pushSubscription?.data)) { + subscriptionId = pushSubscription?.data.id; + } + + const response = await RequestService.updateUser( + { appId, subscriptionId }, + aliasPair, + { + properties, + }, + ); return UserPropertyRequests._processUserPropertyResponse(response); } diff --git a/src/onesignal/UserDirector.ts b/src/onesignal/UserDirector.ts index dc10c355e..4df8e56ef 100644 --- a/src/onesignal/UserDirector.ts +++ b/src/onesignal/UserDirector.ts @@ -9,6 +9,7 @@ import { logMethodCall } from '../shared/utils/utils'; import User from './User'; import { RequestService } from '../core/requestService/RequestService'; import { SupportedSubscription } from '../core/models/SubscriptionModels'; +import { isCompleteSubscriptionObject } from '../core/utils/typePredicates'; export default class UserDirector { static async initializeUser(isTemporary?: boolean): Promise { @@ -95,8 +96,19 @@ export default class UserDirector { try { const appId = await MainHelper.getAppId(); + const pushSubscription = + await OneSignal.coreDirector.getPushSubscriptionModel(); + + let subscriptionId; + if (isCompleteSubscriptionObject(pushSubscription?.data)) { + subscriptionId = pushSubscription?.data.id; + } + const userData = await UserDirector.getAllUserData(); - const response = await RequestService.createUser({ appId }, userData); + const response = await RequestService.createUser( + { appId, subscriptionId }, + userData, + ); user.isCreatingUser = false; return response.result as UserData; } catch (e) { diff --git a/src/page/managers/LoginManager.ts b/src/page/managers/LoginManager.ts index 441e2ba5e..cf4f91c88 100644 --- a/src/page/managers/LoginManager.ts +++ b/src/page/managers/LoginManager.ts @@ -205,12 +205,23 @@ export default class LoginManager { } const appId = await MainHelper.getAppId(); + const pushSubscription = + await OneSignal.coreDirector.getPushSubscriptionModel(); + + let subscriptionId; + if (isCompleteSubscriptionObject(pushSubscription?.data)) { + subscriptionId = pushSubscription?.data.id; + } + const userDataCopy = JSON.parse(JSON.stringify(userData)); // only accepts one alias, so remove other aliases only leaving external_id this.stripAliasesOtherThanExternalId(userData); - const response = await RequestService.createUser({ appId }, userData); + const response = await RequestService.createUser( + { appId, subscriptionId }, + userData, + ); const result = response?.result; const status = response?.status; From bf44683b9de18a4cc1011fb4a5cd525bc721b341 Mon Sep 17 00:00:00 2001 From: Shepherd Date: Mon, 4 Mar 2024 18:37:59 -0500 Subject: [PATCH 3/5] Add payload type for create user Makes it easier to make unit tests for create user with refresh_device_metadata --- src/core/requestService/CreateUserPayload.ts | 10 ++++++++++ src/core/requestService/RequestService.ts | 7 +++---- 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 src/core/requestService/CreateUserPayload.ts diff --git a/src/core/requestService/CreateUserPayload.ts b/src/core/requestService/CreateUserPayload.ts new file mode 100644 index 000000000..e12dea61e --- /dev/null +++ b/src/core/requestService/CreateUserPayload.ts @@ -0,0 +1,10 @@ +import { SupportedIdentity } from '../models/IdentityModel'; +import { SupportedSubscription } from '../models/SubscriptionModels'; +import { UserPropertiesModel } from '../models/UserPropertiesModel'; + +export type CreateUserPayload = { + properties?: UserPropertiesModel; + identity?: SupportedIdentity; + refresh_device_metadata?: boolean; + subscriptions?: SupportedSubscription[]; +}; diff --git a/src/core/requestService/RequestService.ts b/src/core/requestService/RequestService.ts index 442c3b5bb..896769e25 100644 --- a/src/core/requestService/RequestService.ts +++ b/src/core/requestService/RequestService.ts @@ -7,7 +7,7 @@ import { } from '../models/SubscriptionModels'; import AliasPair from './AliasPair'; import { UpdateUserPayload } from './UpdateUserPayload'; -import UserData from '../models/UserData'; +import { CreateUserPayload } from './CreateUserPayload'; import { RequestMetadata } from '../models/RequestMetadata'; import { encodeRFC3986URIComponent } from '../../shared/utils/Encoding'; import OneSignalUtils from '../../shared/utils/OneSignalUtils'; @@ -26,7 +26,7 @@ export class RequestService { */ static async createUser( requestMetadata: RequestMetadata, - requestBody: Partial, + requestBody: CreateUserPayload, ): Promise { const { appId, subscriptionId } = requestMetadata; @@ -44,8 +44,7 @@ export class RequestService { headers = { ...headers, ...requestMetadata.jwtHeader }; } - const refreshMetadata = { refresh_device_metadata: true }; - requestBody = { ...requestBody, ...refreshMetadata }; + requestBody['refresh_device_metadata'] = true; return OneSignalApiBase.post(`apps/${appId}/users`, requestBody, headers); } From 48777f20d06533cb47c1dd0d13d3c770fc02ecc8 Mon Sep 17 00:00:00 2001 From: Shepherd Date: Mon, 4 Mar 2024 19:08:32 -0500 Subject: [PATCH 4/5] Fix undefined subscription id Fixes the OneSignal-Subscription-Id header being undefined on login from an identified user to another identified user. The pushSubscription model at this point in time is undefined and has no id. Now the subscription id will be passed up from login in the same fashion as identify user. --- src/page/managers/LoginManager.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/page/managers/LoginManager.ts b/src/page/managers/LoginManager.ts index cf4f91c88..6c784ae0b 100644 --- a/src/page/managers/LoginManager.ts +++ b/src/page/managers/LoginManager.ts @@ -185,7 +185,7 @@ export default class LoginManager { if (isIdentified) { // if started off identified, upsert a user - result = await this.upsertUser(userData); + result = await this.upsertUser(userData, subscriptionId); } else { // promoting anonymous user to identified user // from user data, we only use identity (and we remove all aliases except external_id) @@ -196,23 +196,16 @@ export default class LoginManager { static async upsertUser( userData: Partial, + subscriptionId?: string, retry = 5, ): Promise { - logMethodCall('LoginManager.upsertUser', { userData }); + logMethodCall('LoginManager.upsertUser', { userData, subscriptionId }); if (retry === 0) { throw new OneSignalError('Login: upsertUser failed: max retries reached'); } const appId = await MainHelper.getAppId(); - const pushSubscription = - await OneSignal.coreDirector.getPushSubscriptionModel(); - - let subscriptionId; - if (isCompleteSubscriptionObject(pushSubscription?.data)) { - subscriptionId = pushSubscription?.data.id; - } - const userDataCopy = JSON.parse(JSON.stringify(userData)); // only accepts one alias, so remove other aliases only leaving external_id @@ -232,7 +225,7 @@ export default class LoginManager { } else if (status >= 500) { Log.error('Server error. Retrying...'); await awaitableTimeout(RETRY_BACKOFF[retry]); - return this.upsertUser(userDataCopy, retry - 1); + return this.upsertUser(userDataCopy, subscriptionId, retry - 1); } return result; From 0cbdc2dcc40de4e2210efab5cee10cc15f52cd5d Mon Sep 17 00:00:00 2001 From: Shepherd Date: Mon, 4 Mar 2024 19:12:44 -0500 Subject: [PATCH 5/5] Add OneSignal-Subscription-Id header unit tests Adds OneSignal-Subscription-Id header unit test for create and update user requests. Also adds refresh_device_metadata test for create user --- __test__/unit/http/sdkVersion.test.ts | 28 ++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/__test__/unit/http/sdkVersion.test.ts b/__test__/unit/http/sdkVersion.test.ts index d65aea8a1..9779919cc 100644 --- a/__test__/unit/http/sdkVersion.test.ts +++ b/__test__/unit/http/sdkVersion.test.ts @@ -1,6 +1,10 @@ import Environment from '../../../src/shared/helpers/Environment'; import { RequestService } from '../../../src/core/requestService/RequestService'; -import { APP_ID, DUMMY_EXTERNAL_ID } from '../../support/constants'; +import { + APP_ID, + DUMMY_EXTERNAL_ID, + DUMMY_SUBSCRIPTION_ID, +} from '../../support/constants'; import { expectHeaderToBeSent } from '../../support/helpers/sdkVersion'; import AliasPair from '../../../src/core/requestService/AliasPair'; import { getDummyPushSubscriptionOSModel } from '../../support/helpers/core'; @@ -46,6 +50,20 @@ describe('Sdk Version Header Tests', () => { RequestService.createUser({ appId: APP_ID }, {}); expectHeaderToBeSent(); }); + test('POST /users: header is sent', () => { + RequestService.createUser( + { appId: APP_ID }, + { refresh_device_metadata: true }, + ); + expectHeaderToBeSent(); + }); + test('POST /users: header is sent with subscription id', () => { + RequestService.createUser( + { appId: APP_ID, subscriptionId: DUMMY_SUBSCRIPTION_ID }, + {}, + ); + expectHeaderToBeSent(); + }); test('GET /users/by//: header is sent', () => { RequestService.getUser( { appId: APP_ID }, @@ -61,6 +79,14 @@ describe('Sdk Version Header Tests', () => { ); expectHeaderToBeSent(); }); + test('PATCH /users/by//: header is sent with subscription id', () => { + RequestService.updateUser( + { appId: APP_ID, subscriptionId: DUMMY_SUBSCRIPTION_ID }, + new AliasPair(AliasPair.EXTERNAL_ID, DUMMY_EXTERNAL_ID), + {}, + ); + expectHeaderToBeSent(); + }); test('DELETE /users/by//: header is sent', () => { RequestService.deleteUser( { appId: APP_ID },