Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 53 additions & 6 deletions lib/odp/odp_manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ describe('DefaultOdpManager', () => {
expect(identifiers).toEqual(new Map([['fs_user_id', 'user'], ['vuid', 'vuid_a']]));
});

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

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

odpManager.identifyUser('user');
expect(mockSendEvents).toHaveBeenCalledOnce();
const { identifiers } = mockSendEvents.mock.calls[0][0];
expect(identifiers).toEqual(new Map([['fs_user_id', 'user']]));
expect(mockSendEvents).not.toHaveBeenCalled();
});

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

Expand All @@ -655,9 +653,58 @@ describe('DefaultOdpManager', () => {
await odpManager.onRunning();

odpManager.identifyUser('vuid_a');
expect(mockSendEvents).not.toHaveBeenCalled();
});

it('sends identified event when called with fs_user_id and vuid is set via setVuid', async () => {
const eventManager = getMockOdpEventManager();
eventManager.onRunning.mockReturnValue(Promise.resolve());

const mockSendEvents = vi.mocked(eventManager.sendEvent as OdpEventManager['sendEvent']);

const odpManager = new DefaultOdpManager({
segmentManager: getMockOdpSegmentManager(),
eventManager,
});

odpManager.start();
odpManager.updateConfig({ integrated: true, odpConfig: config });
await odpManager.onRunning();

// Set vuid first, then identify with fs_user_id
odpManager.setVuid('vuid_123');

// Clear the client_initialized event call
mockSendEvents.mockClear();

odpManager.identifyUser('user');
expect(mockSendEvents).toHaveBeenCalledOnce();
const { type, action, identifiers } = mockSendEvents.mock.calls[0][0];
expect(type).toEqual('fullstack');
expect(action).toEqual('identified');
// identifyUser passes {fs_user_id: 'user'}, and sendEvent augments with stored vuid
expect(identifiers).toEqual(new Map([['fs_user_id', 'user'], ['vuid', 'vuid_123']]));
});

it('sends identified event with three identifiers (fs_user_id, explicit vuid, and stored vuid)', async () => {
const eventManager = getMockOdpEventManager();
eventManager.onRunning.mockReturnValue(Promise.resolve());

const mockSendEvents = vi.mocked(eventManager.sendEvent as OdpEventManager['sendEvent']);

const odpManager = new DefaultOdpManager({
segmentManager: getMockOdpSegmentManager(),
eventManager,
});

odpManager.start();
odpManager.updateConfig({ integrated: true, odpConfig: config });
await odpManager.onRunning();

odpManager.identifyUser('user', 'vuid_a');
expect(mockSendEvents).toHaveBeenCalledOnce();
const { identifiers } = mockSendEvents.mock.calls[0][0];
expect(identifiers).toEqual(new Map([['vuid', 'vuid_a']]));
expect(identifiers).toEqual(new Map([['fs_user_id', 'user'], ['vuid', 'vuid_a']]));
});

it('should reject onRunning() if stopped in new state', async () => {
Expand Down
17 changes: 16 additions & 1 deletion lib/odp/odp_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export class DefaultOdpManager extends BaseService implements OdpManager {

identifyUser(userId: string, vuid?: string): void {
const identifiers = new Map<string, string>();

let finalUserId: Maybe<string> = userId;
let finalVuid: Maybe<string> = vuid;

Expand All @@ -229,6 +229,21 @@ export class DefaultOdpManager extends BaseService implements OdpManager {
identifiers.set(ODP_USER_KEY.FS_USER_ID, finalUserId);
}

// Include the stored vuid when counting identifiers to determine if there
// are enough to warrant sending an identify event. The vuid would be added
// later in sendEvent(), but we need to account for it here for the count.
const allIdentifiers = new Map(identifiers);
if (!allIdentifiers.has(ODP_USER_KEY.VUID) && this.vuid) {
allIdentifiers.set(ODP_USER_KEY.VUID, this.vuid);
}

// An identify event requires at least 2 identifiers to link (e.g., vuid + fs_user_id).
// A single identifier has no cross-reference value and would generate unnecessary traffic.
if (allIdentifiers.size < 2) {
this.logger?.debug('ODP identify event is not dispatched (only one identifier provided).');
return;
}

const event = new OdpEvent(ODP_DEFAULT_EVENT_TYPE, ODP_EVENT_ACTION.IDENTIFIED, identifiers);
this.sendEvent(event);
}
Expand Down
Loading