Skip to content

Commit 14c9973

Browse files
committed
[AI-FSSDK] [FSSDK-12721] Skip ODP identify event for single-identifier users
1 parent a2b378c commit 14c9973

2 files changed

Lines changed: 89 additions & 8 deletions

File tree

lib/odp/odp_manager.spec.ts

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ describe('DefaultOdpManager', () => {
618618
expect(identifiers).toEqual(new Map([['fs_user_id', 'user'], ['vuid', 'vuid_a']]));
619619
});
620620

621-
it('sends identified event when called with just fs_user_id in first parameter', async () => {
621+
it('does not send identified event when called with just fs_user_id and no stored vuid', async () => {
622622
const eventManager = getMockOdpEventManager();
623623
eventManager.onRunning.mockReturnValue(Promise.resolve());
624624

@@ -634,12 +634,10 @@ describe('DefaultOdpManager', () => {
634634
await odpManager.onRunning();
635635

636636
odpManager.identifyUser('user');
637-
expect(mockSendEvents).toHaveBeenCalledOnce();
638-
const { identifiers } = mockSendEvents.mock.calls[0][0];
639-
expect(identifiers).toEqual(new Map([['fs_user_id', 'user']]));
637+
expect(mockSendEvents).not.toHaveBeenCalled();
640638
});
641639

642-
it('sends identified event when called with just vuid in first parameter', async () => {
640+
it('does not send identified event when called with just vuid in first parameter and no stored vuid', async () => {
643641
const eventManager = getMockOdpEventManager();
644642
eventManager.onRunning.mockReturnValue(Promise.resolve());
645643

@@ -655,9 +653,73 @@ describe('DefaultOdpManager', () => {
655653
await odpManager.onRunning();
656654

657655
odpManager.identifyUser('vuid_a');
656+
expect(mockSendEvents).not.toHaveBeenCalled();
657+
});
658+
659+
it('sends identified event when called with fs_user_id and stored vuid is available', async () => {
660+
const eventManager = getMockOdpEventManager();
661+
eventManager.onRunning.mockReturnValue(Promise.resolve());
662+
663+
const mockSendEvents = vi.mocked(eventManager.sendEvent as OdpEventManager['sendEvent']);
664+
665+
const odpManager = new DefaultOdpManager({
666+
segmentManager: getMockOdpSegmentManager(),
667+
eventManager,
668+
});
669+
670+
odpManager.start();
671+
odpManager.updateConfig({ integrated: true, odpConfig: config });
672+
await odpManager.onRunning();
673+
674+
// Set stored vuid first so identifyUser has 2 identifiers
675+
odpManager.setVuid('vuid_stored');
676+
677+
// Clear the sendEvent calls from setVuid's client_initialized event
678+
mockSendEvents.mockClear();
679+
680+
odpManager.identifyUser('user');
658681
expect(mockSendEvents).toHaveBeenCalledOnce();
659682
const { identifiers } = mockSendEvents.mock.calls[0][0];
660-
expect(identifiers).toEqual(new Map([['vuid', 'vuid_a']]));
683+
expect(identifiers).toEqual(new Map([['fs_user_id', 'user'], ['vuid', 'vuid_stored']]));
684+
});
685+
686+
it('does not count empty identifier values toward the minimum', async () => {
687+
const eventManager = getMockOdpEventManager();
688+
eventManager.onRunning.mockReturnValue(Promise.resolve());
689+
690+
const mockSendEvents = vi.mocked(eventManager.sendEvent as OdpEventManager['sendEvent']);
691+
692+
const odpManager = new DefaultOdpManager({
693+
segmentManager: getMockOdpSegmentManager(),
694+
eventManager,
695+
});
696+
697+
odpManager.start();
698+
odpManager.updateConfig({ integrated: true, odpConfig: config });
699+
await odpManager.onRunning();
700+
701+
// Call with fs_user_id and empty vuid - should not send (only 1 valid identifier)
702+
odpManager.identifyUser('user', '');
703+
expect(mockSendEvents).not.toHaveBeenCalled();
704+
});
705+
706+
it('logs debug message when skipping identify event due to single identifier', async () => {
707+
const eventManager = getMockOdpEventManager();
708+
eventManager.onRunning.mockReturnValue(Promise.resolve());
709+
710+
const logger = getMockLogger();
711+
const odpManager = new DefaultOdpManager({
712+
logger,
713+
segmentManager: getMockOdpSegmentManager(),
714+
eventManager,
715+
});
716+
717+
odpManager.start();
718+
odpManager.updateConfig({ integrated: true, odpConfig: config });
719+
await odpManager.onRunning();
720+
721+
odpManager.identifyUser('user');
722+
expect(logger.debug).toHaveBeenCalledWith('ODP identify event is not dispatched (only one identifier provided).');
661723
});
662724

663725
it('should reject onRunning() if stopped in new state', async () => {

lib/odp/odp_manager.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export class DefaultOdpManager extends BaseService implements OdpManager {
212212

213213
identifyUser(userId: string, vuid?: string): void {
214214
const identifiers = new Map<string, string>();
215-
215+
216216
let finalUserId: Maybe<string> = userId;
217217
let finalVuid: Maybe<string> = vuid;
218218

@@ -229,7 +229,26 @@ export class DefaultOdpManager extends BaseService implements OdpManager {
229229
identifiers.set(ODP_USER_KEY.FS_USER_ID, finalUserId);
230230
}
231231

232-
const event = new OdpEvent(ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION.IDENTIFIED, identifiers);
232+
// Include stored vuid if not already present to get accurate identifier count
233+
if (!identifiers.has(ODP_USER_KEY.VUID) && this.vuid) {
234+
identifiers.set(ODP_USER_KEY.VUID, this.vuid);
235+
}
236+
237+
// Filter out identifiers with empty/null values
238+
const validIdentifiers = new Map<string, string>();
239+
identifiers.forEach((value, key) => {
240+
if (value) {
241+
validIdentifiers.set(key, value);
242+
}
243+
});
244+
245+
// Only send identify event when there are 2+ valid identifiers to link
246+
if (validIdentifiers.size < 2) {
247+
this.logger?.debug('ODP identify event is not dispatched (only one identifier provided).');
248+
return;
249+
}
250+
251+
const event = new OdpEvent(ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION.IDENTIFIED, validIdentifiers);
233252
this.sendEvent(event);
234253
}
235254

0 commit comments

Comments
 (0)