Skip to content

Commit babd33c

Browse files
rmi22186claude
andcommitted
fix: Mirror Apple SDK sha256 alias semantics (split slots, null pass-through)
Aligns the email_sha256/mobile_sha256 aliases with the final Apple SDK behavior after mparticle-apple-sdk #756 + #761: - Map mobile_sha256 to other2 (was: other) so the two aliases use distinct canonical slots and no longer clobber each other. - Pass null through to the canonical slot instead of dropping it, so a Modify call with `<alias>: null` actually clears the slot — matching the null=clear contract honored by removeFalsyIdentityValues and validators. - Hoist the alias-key array to a module-level frozen constant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent afa7f8b commit babd33c

4 files changed

Lines changed: 46 additions & 19 deletions

File tree

src/identity-utils.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -289,26 +289,32 @@ const getExpireTimestamp = (maxAge: number = ONE_DAY_IN_SECONDS): number =>
289289
const parseIdentityResponse = (responseText: string): IdentityResultBody =>
290290
responseText ? JSON.parse(responseText) : ({} as IdentityResultBody);
291291

292-
// Maps convenience identity key names to their canonical server-side names.
293-
// Mirrors the emailSha256/mobileSha256 helpers added in the Apple SDK (PR #756).
294-
const SHA256_IDENTITY_ALIASES: Readonly<Record<string, string>> = {
292+
// Maps convenience identity key names to their canonical server-side slots.
293+
// Mirrors the Apple SDK helpers (PRs #756 and #761): email_sha256 → other,
294+
// mobile_sha256 → other2. Null on an alias clears the canonical slot, matching
295+
// the existing null=clear contract honored by removeFalsyIdentityValues.
296+
const SHA256_IDENTITY_ALIASES = Object.freeze({
295297
email_sha256: 'other',
296-
mobile_sha256: 'other',
297-
};
298+
mobile_sha256: 'other2',
299+
} as const);
300+
301+
const SHA256_ALIAS_KEYS = Object.freeze(
302+
Object.keys(SHA256_IDENTITY_ALIASES) as Array<
303+
keyof typeof SHA256_IDENTITY_ALIASES
304+
>
305+
);
298306

299307
export const normalizeUserIdentityKeys = (
300308
userIdentities: UserIdentities
301309
): UserIdentities => {
302310
const normalized: Record<string, string | null | undefined> = {
303311
...(userIdentities as Record<string, string | null | undefined>),
304312
};
305-
for (const alias of Object.keys(SHA256_IDENTITY_ALIASES)) {
313+
for (const alias of SHA256_ALIAS_KEYS) {
306314
if (alias in normalized) {
307315
const value = normalized[alias];
308316
delete normalized[alias];
309-
if (value !== null) {
310-
normalized[SHA256_IDENTITY_ALIASES[alias]] = value;
311-
}
317+
normalized[SHA256_IDENTITY_ALIASES[alias]] = value;
312318
}
313319
}
314320
return normalized as UserIdentities;

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ export const IdentityType = {
208208
case 'email_sha256':
209209
return IdentityType.Other;
210210
case 'mobile_sha256':
211-
return IdentityType.Other;
211+
return IdentityType.Other2;
212212
default:
213213
return false;
214214
}

test/jest/identity-utils.spec.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,22 @@ describe('normalizeUserIdentityKeys', () => {
66
expect(result).toEqual({ other: 'abc123hash' });
77
});
88

9-
it('maps mobile_sha256 to other', () => {
10-
const result = normalizeUserIdentityKeys({ mobile_sha256: 'mobilehash456' });
11-
expect(result).toEqual({ other: 'mobilehash456' });
9+
it('maps mobile_sha256 to other2', () => {
10+
const result = normalizeUserIdentityKeys({
11+
mobile_sha256: 'mobilehash456',
12+
});
13+
expect(result).toEqual({ other2: 'mobilehash456' });
1214
});
1315

14-
it('last sha256 alias wins when both are set (same slot)', () => {
16+
it('maps both aliases to distinct canonical slots', () => {
1517
const result = normalizeUserIdentityKeys({
1618
email_sha256: 'emailhash',
1719
mobile_sha256: 'mobilehash',
1820
});
19-
expect(result).toEqual({ other: 'mobilehash' });
21+
expect(result).toEqual({
22+
other: 'emailhash',
23+
other2: 'mobilehash',
24+
});
2025
});
2126

2227
it('preserves other canonical identity keys alongside sha256 aliases', () => {
@@ -35,12 +40,28 @@ describe('normalizeUserIdentityKeys', () => {
3540
it('does not modify identities without sha256 aliases', () => {
3641
const input = { email: 'user@example.com', customerid: 'cust123' };
3742
const result = normalizeUserIdentityKeys(input);
38-
expect(result).toEqual({ email: 'user@example.com', customerid: 'cust123' });
43+
expect(result).toEqual({
44+
email: 'user@example.com',
45+
customerid: 'cust123',
46+
});
3947
});
4048

41-
it('drops null values for sha256 aliases', () => {
49+
it('passes null on email_sha256 through to other (clears canonical slot)', () => {
4250
const result = normalizeUserIdentityKeys({ email_sha256: null });
43-
expect(result).toEqual({});
51+
expect(result).toEqual({ other: null });
52+
});
53+
54+
it('passes null on mobile_sha256 through to other2 (clears canonical slot)', () => {
55+
const result = normalizeUserIdentityKeys({ mobile_sha256: null });
56+
expect(result).toEqual({ other2: null });
57+
});
58+
59+
it('alias overwrites a same-slot canonical value (silent last-write-wins)', () => {
60+
const result = normalizeUserIdentityKeys({
61+
other: 'preexisting',
62+
email_sha256: 'aliasvalue',
63+
});
64+
expect(result).toEqual({ other: 'aliasvalue' });
4465
});
4566

4667
it('does not mutate the original object', () => {

test/jest/types.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ describe('IdentityType', () => {
440440
expect(getIdentityType('phone_number_2')).toBe(PhoneNumber2);
441441
expect(getIdentityType('phone_number_3')).toBe(PhoneNumber3);
442442
expect(getIdentityType('email_sha256')).toBe(Other);
443-
expect(getIdentityType('mobile_sha256')).toBe(Other);
443+
expect(getIdentityType('mobile_sha256')).toBe(Other2);
444444
});
445445

446446
it('returns false if the identity name is not found', () => {

0 commit comments

Comments
 (0)