Skip to content

Commit bf68a45

Browse files
committed
Support hashedEmailUserIdentityType
1 parent d17999d commit bf68a45

2 files changed

Lines changed: 98 additions & 38 deletions

File tree

src/roktManager.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import { IKitConfigs } from "./configAPIClient";
22
import { UserAttributeFilters } from "./forwarders.interfaces";
33
import { IMParticleUser } from "./identity-user-interfaces";
44
import KitFilterHelper from "./kitFilterHelper";
5-
import { Dictionary, parseSettingsString } from "./utils";
5+
import { Dictionary, isEmpty, parseSettingsString } from "./utils";
66
import { SDKIdentityApi } from "./identity.interfaces";
77
import { SDKLoggerApi } from "./sdkRuntimeModels";
8+
import { IdentityType } from "./types";
9+
import { UserIdentities } from "@mparticle/web-sdk";
810

911
// https://docs.rokt.com/developers/integration-guides/web/library/attributes
1012
export interface IRoktPartnerAttributes {
@@ -83,6 +85,7 @@ export default class RoktManager {
8385
private launcherOptions?: IRoktLauncherOptions;
8486
private logger: SDKLoggerApi;
8587
private domain?: string;
88+
private configSettings: Dictionary<string> = {};
8689
/**
8790
* Initializes the RoktManager with configuration settings and user data.
8891
*
@@ -103,6 +106,7 @@ export default class RoktManager {
103106
): void {
104107
const { userAttributeFilters, settings } = roktConfig || {};
105108
const { placementAttributesMapping } = settings || {};
109+
this.configSettings = settings;
106110

107111
try {
108112
this.placementAttributesMapping = parseSettingsString(placementAttributesMapping);
@@ -167,27 +171,49 @@ export default class RoktManager {
167171
const { attributes } = options;
168172
const sandboxValue = attributes?.sandbox || null;
169173
const mappedAttributes = this.mapPlacementAttributes(attributes, this.placementAttributesMapping);
174+
const { hashedEmailUserIdentityType = null } = this.configSettings || {};
175+
176+
const normalizedHashedEmailUserIdentityType = hashedEmailUserIdentityType?.toLowerCase() || null;
170177

171178
// Get current user identities
172179
this.currentUser = this.identityService.getCurrentUser();
173180
const currentUserIdentities = this.currentUser?.getUserIdentities()?.userIdentities || {};
181+
174182
const currentEmail = currentUserIdentities.email;
175183
const newEmail = mappedAttributes.email as string;
176184

185+
let currentHashedEmail: string | undefined;
186+
let newHashedEmail: string | undefined;
187+
188+
// Hashed email identity is valid if it is set to Other-Other10
189+
if(normalizedHashedEmailUserIdentityType && IdentityType.getIdentityType(normalizedHashedEmailUserIdentityType) !== false) {
190+
currentHashedEmail = currentUserIdentities[normalizedHashedEmailUserIdentityType];
191+
newHashedEmail = mappedAttributes['emailsha256'] as string || undefined;
192+
}
193+
194+
const emailChanged = !!(newEmail && (!currentEmail || currentEmail !== newEmail));
195+
const hashedEmailChanged = !!(newHashedEmail && (!currentHashedEmail || currentHashedEmail !== newHashedEmail));
177196
// https://go.mparticle.com/work/SQDSDKS-7338
178-
// Check if email exists and differs
179-
if (newEmail && (!currentEmail || currentEmail !== newEmail)) {
180-
if (currentEmail && currentEmail !== newEmail) {
197+
const newIdentities: UserIdentities = {};
198+
if (emailChanged) {
199+
newIdentities.email = newEmail;
200+
if (newEmail) {
181201
this.logger.warning(`Email mismatch detected. Current email, ${currentEmail} differs from email passed to selectPlacements call, ${newEmail}. Proceeding to call identify with ${newEmail}. Please verify your implementation.`);
182202
}
203+
}
183204

205+
if (hashedEmailChanged) {
206+
newIdentities[normalizedHashedEmailUserIdentityType] = newHashedEmail;
207+
this.logger.warning(`emailsha256 mismatch detected. Current mParticle ${normalizedHashedEmailUserIdentityType} identity, ${currentHashedEmail}, differs from from 'emailsha256' passed to selectPlacements call, ${newHashedEmail}. Proceeding to call identify with ${normalizedHashedEmailUserIdentityType} set to ${newHashedEmail}. Please verify your implementation`);
208+
}
209+
if (!isEmpty(newIdentities)) {
184210
// Call identify with the new user identities
185211
try {
186212
await new Promise<void>((resolve, reject) => {
187213
this.identityService.identify({
188214
userIdentities: {
189215
...currentUserIdentities,
190-
email: newEmail
216+
...newIdentities
191217
}
192218
}, () => {
193219
resolve();
@@ -198,32 +224,6 @@ export default class RoktManager {
198224
}
199225
}
200226

201-
// Handle emailsha256 mapping to 'other' identity
202-
const newEmailSha256 = options.attributes.emailsha256;
203-
const currentOther = currentUserIdentities.other;
204-
205-
if (newEmailSha256 && (!currentOther || currentOther !== newEmailSha256)) {
206-
if (currentOther && currentOther !== newEmailSha256) {
207-
this.logger.warning(`emailsha256 identity mismatch detected. Current mParticle 'other' identity, ${currentOther} differs from emailsha256 passed to selectPlacements call, ${newEmailSha256}. Proceeding to call identify with 'other' set to ${newEmailSha256}. Please verify your implementation.`);
208-
}
209-
210-
// Call identify with the new 'other' identity mapped from emailsha256
211-
try {
212-
await new Promise<void>((resolve, reject) => {
213-
this.identityService.identify({
214-
userIdentities: {
215-
...currentUserIdentities,
216-
other: newEmailSha256 as string
217-
}
218-
}, () => {
219-
resolve();
220-
});
221-
});
222-
} catch (error) {
223-
this.logger.error('Failed to identify user with new emailsha256 mapped to other identity: ' + JSON.stringify(error));
224-
}
225-
}
226-
227227
this.setUserAttributes(mappedAttributes);
228228

229229
const enrichedAttributes = {

test/jest/roktManager.spec.ts

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ describe('RoktManager', () => {
379379
describe('#selectPlacements', () => {
380380
beforeEach(() => {
381381
roktManager['currentUser'] = currentUser;
382+
jest.clearAllMocks();
382383
});
383384

384385
it('should call kit.selectPlacements with empty attributes', () => {
@@ -889,10 +890,55 @@ describe('RoktManager', () => {
889890
email: 'new@example.com'
890891
}
891892
}, expect.any(Function));
893+
expect(mockMPInstance.Logger.warning).toHaveBeenCalled();
894+
});
895+
896+
it('should not call identify when user has current email but no email is passed to selectPlacements', async () => {
897+
const kit: Partial<IRoktKit> = {
898+
launcher: {
899+
selectPlacements: jest.fn(),
900+
hashAttributes: jest.fn()
901+
},
902+
selectPlacements: jest.fn(),
903+
hashAttributes: jest.fn(),
904+
setExtensionData: jest.fn(),
905+
};
906+
907+
roktManager['placementAttributesMapping'] = [];
908+
roktManager['configSettings'] = {};
909+
roktManager.kit = kit as IRoktKit;
910+
911+
const mockIdentity = {
912+
getCurrentUser: jest.fn().mockReturnValue({
913+
getUserIdentities: () => ({
914+
userIdentities: {
915+
email: 'existing@example.com'
916+
}
917+
}),
918+
setUserAttributes: jest.fn()
919+
}),
920+
identify: jest.fn().mockImplementation((data, callback) => {
921+
// Call callback with no error to simulate success
922+
callback();
923+
})
924+
} as unknown as SDKIdentityApi;
925+
926+
roktManager['identityService'] = mockIdentity;
927+
928+
const options: IRoktSelectPlacementsOptions = {
929+
attributes: {
930+
// No email attribute passed
931+
// customAttribute: 'some-value'
932+
}
933+
};
934+
935+
await roktManager.selectPlacements(options);
936+
937+
expect(mockIdentity.identify).not.toHaveBeenCalled();
892938
expect(mockMPInstance.Logger.warning).not.toHaveBeenCalled();
893939
});
894940

895-
it('should call identify with emailsha256 mapped to other when it differs from current user other identity', async () => {
941+
it('should call identify with emailsha256 mapped to other5 when it differs from current user other5 identity', async () => {
896942
const kit: Partial<IRoktKit> = {
897943
launcher: {
898944
selectPlacements: jest.fn(),
@@ -904,13 +950,16 @@ describe('RoktManager', () => {
904950
};
905951

906952
roktManager.kit = kit as IRoktKit;
953+
roktManager['configSettings'] = {
954+
hashedEmailUserIdentityType: 'Other5'
955+
};
907956

908957
// Set up fresh mocks for this test
909958
const mockIdentity = {
910959
getCurrentUser: jest.fn().mockReturnValue({
911960
getUserIdentities: () => ({
912961
userIdentities: {
913-
other: 'old-other-value'
962+
other5: 'old-other-value'
914963
}
915964
}),
916965
setUserAttributes: jest.fn()
@@ -933,15 +982,15 @@ describe('RoktManager', () => {
933982

934983
expect(mockIdentity.identify).toHaveBeenCalledWith({
935984
userIdentities: {
936-
other: 'new-emailsha256-value'
985+
other5: 'new-emailsha256-value'
937986
}
938987
}, expect.any(Function));
939988
expect(mockMPInstance.Logger.warning).toHaveBeenCalledWith(
940-
'emailsha256 identity mismatch detected. Current mParticle \'other\' identity, old-other-value differs from emailsha256 passed to selectPlacements call, new-emailsha256-value. Proceeding to call identify with \'other\' set to new-emailsha256-value. Please verify your implementation.'
989+
"emailsha256 mismatch detected. Current mParticle other5 identity, old-other-value, differs from from 'emailsha256' passed to selectPlacements call, new-emailsha256-value. Proceeding to call identify with other5 set to new-emailsha256-value. Please verify your implementation"
941990
);
942991
});
943992

944-
it('should not call identify when emailsha256 matches current user other identity', () => {
993+
it('should not call identify when emailsha256 matches current user other5 identity', () => {
945994
// Reset mocks
946995
jest.clearAllMocks();
947996

@@ -956,13 +1005,16 @@ describe('RoktManager', () => {
9561005
};
9571006

9581007
roktManager.kit = kit as IRoktKit;
1008+
roktManager['configSettings'] = {
1009+
hashedEmailUserIdentityType: 'Other5'
1010+
};
9591011

9601012
// Set up fresh mocks for this test
9611013
const mockIdentity = {
9621014
getCurrentUser: jest.fn().mockReturnValue({
9631015
getUserIdentities: () => ({
9641016
userIdentities: {
965-
other: 'same-emailsha256-value'
1017+
other5: 'same-emailsha256-value'
9661018
}
9671019
}),
9681020
setUserAttributes: jest.fn()
@@ -996,6 +1048,9 @@ describe('RoktManager', () => {
9961048
};
9971049

9981050
roktManager.kit = kit as IRoktKit;
1051+
roktManager['configSettings'] = {
1052+
hashedEmailUserIdentityType: 'Other'
1053+
};
9991054

10001055
const mockIdentity = {
10011056
getCurrentUser: jest.fn().mockReturnValue({
@@ -1025,7 +1080,9 @@ describe('RoktManager', () => {
10251080
other: 'new-emailsha256-value'
10261081
}
10271082
}, expect.any(Function));
1028-
expect(mockMPInstance.Logger.warning).not.toHaveBeenCalled();
1083+
expect(mockMPInstance.Logger.warning).toHaveBeenCalledWith(
1084+
"emailsha256 mismatch detected. Current mParticle other identity, undefined, differs from from 'emailsha256' passed to selectPlacements call, new-emailsha256-value. Proceeding to call identify with other set to new-emailsha256-value. Please verify your implementation"
1085+
);
10291086
});
10301087

10311088
it('should not call identify when current user has other identity but emailsha256 is null', () => {
@@ -1043,6 +1100,9 @@ describe('RoktManager', () => {
10431100
};
10441101

10451102
roktManager.kit = kit as IRoktKit;
1103+
roktManager['configSettings'] = {
1104+
hashedEmailUserIdentityType: 'Other'
1105+
};
10461106

10471107
// Set up fresh mocks for this test
10481108
const mockIdentity = {

0 commit comments

Comments
 (0)