Skip to content

Commit 466431f

Browse files
Initial POC of Identity Failed implementation
1 parent 1bd7d52 commit 466431f

7 files changed

Lines changed: 163 additions & 10 deletions

File tree

src/identityApiClient.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ export default function IdentityAPIClient(
288288
}
289289

290290
mpInstance._Store.identityCallInFlight = false;
291+
mpInstance._Store.identityCallFailed = false;
291292

292293
verbose(message);
293294
parseIdentityResponse(
@@ -301,9 +302,14 @@ export default function IdentityAPIClient(
301302
);
302303
} catch (err) {
303304
mpInstance._Store.identityCallInFlight = false;
305+
306+
// Marking identity call failed - ready() will fire callbacks if
307+
// RoktManager is ready, allowing Rokt methods to execute even if identity fails
308+
mpInstance._Store.identityCallFailed = true;
304309

305310
const errorMessage = (err as Error).message || err.toString();
306311

312+
debugger;
307313
error('Error sending identity request to servers' + ' - ' + errorMessage);
308314
invokeCallback(
309315
callback,

src/mp-instance.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,9 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
130130
integrationDelays: {},
131131
forwarderConstructors: [],
132132
};
133-
this._RoktManager = new RoktManager();
133+
134+
// TODO: Refactor into a preinit class or ready queue manager
135+
this._RoktManager = new RoktManager(this._preInit);
134136

135137
// required for forwarders once they reference the mparticle instance
136138
this.IdentityType = IdentityType;
@@ -150,12 +152,17 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
150152

151153
if (typeof window !== 'undefined') {
152154
if (window.mParticle && window.mParticle.config) {
155+
// TODO: Set up test cases for this as well
153156
if (window.mParticle.config.hasOwnProperty('rq')) {
157+
console.warn('ready queue from config', window.mParticle.config.rq);
158+
console.warn('typeof ready queue from config', typeof window.mParticle.config.rq);
159+
// debugger;
154160
this._preInit.readyQueue = window.mParticle.config.rq;
155161
}
156162
}
157163
}
158164
this.init = function(apiKey, config) {
165+
console.warn('local dev');
159166
if (!config) {
160167
console.warn(
161168
'You did not pass a config object to init(). mParticle will not initialize properly'
@@ -251,7 +258,18 @@ export default function mParticleInstance(this: IMParticleWebSDKInstance, instan
251258
* @param {Function} function A function to be called after mParticle is initialized
252259
*/
253260
this.ready = function(f) {
254-
if (self.isInitialized() && typeof f === 'function') {
261+
// debugger;
262+
console.warn('ready', f);
263+
console.warn('typeof f', typeof f);
264+
console.warn('isInitialized', self.isInitialized());
265+
console.warn('identityCallFailed', self._Store?.identityCallFailed);
266+
const shouldExecute = typeof f === 'function' && (
267+
self._Store?.isInitialized ||
268+
(self._Store?.identityCallFailed && self._RoktManager.isReady())
269+
);
270+
271+
console.warn('shouldExecute', shouldExecute);
272+
if (shouldExecute) {
255273
f();
256274
} else {
257275
self._preInit.readyQueue.push(f);
@@ -1460,10 +1478,15 @@ function completeSDKInitialization(apiKey, config, mpInstance) {
14601478

14611479
// We will continue to clear out the ready queue as part of the initial init flow
14621480
// if an identify request is unnecessary, such as if there is an existing session
1481+
console.warn('Checking if store can be initialized', mpInstance._Store.mpid, mpInstance._Store.identifyCalled);
1482+
1483+
console.warn('ready queue on init complete', mpInstance._preInit.readyQueue);
1484+
14631485
if (
14641486
(mpInstance._Store.mpid && !mpInstance._Store.identifyCalled) ||
14651487
mpInstance._Store.webviewBridgeEnabled
14661488
) {
1489+
// TODO: We may need to extract this out of the identified check above
14671490
mpInstance._Store.isInitialized = true;
14681491

14691492
mpInstance._preInit.readyQueue = processReadyQueue(
@@ -1612,9 +1635,17 @@ function processIdentityCallback(
16121635
}
16131636

16141637
function queueIfNotInitialized(func, self) {
1615-
if (!self.isInitialized()) {
1616-
self.ready(function() {
1617-
func();
1638+
// Core SDK methods must wait for Store initialization
1639+
// We queue directly to readyQueue (not via ready()) because:
1640+
// - ready() fires when either Store OR RoktManager is ready
1641+
// - Core SDK methods specifically need Store to be initialized
1642+
if (!self._Store?.isInitialized) {
1643+
self._preInit.readyQueue.push(function() {
1644+
// Double-check Store before executing - prevents execution
1645+
// when only RoktManager is ready
1646+
if (self._Store?.isInitialized) {
1647+
func();
1648+
}
16181649
});
16191650
return true;
16201651
}

src/pre-init-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const processReadyQueue = (readyQueue): Function[] => {
1717
if (isFunction(readyQueueItem)) {
1818
readyQueueItem();
1919
} else if (Array.isArray(readyQueueItem)) {
20+
// TODO:L Can we add test coverage for this?
2021
processPreloadedItem(readyQueueItem);
2122
}
2223
});

src/roktManager.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { SDKLoggerApi } from "./sdkRuntimeModels";
1515
import { IStore, LocalSessionAttributes } from "./store";
1616
import { UserIdentities } from "@mparticle/web-sdk";
1717
import { IdentityType } from "./types";
18+
import { IPreInit, processReadyQueue } from "./pre-init-utils";
1819

1920
// https://docs.rokt.com/developers/integration-guides/web/library/attributes
2021
export interface IRoktPartnerAttributes {
@@ -100,6 +101,12 @@ export default class RoktManager {
100101
private logger: SDKLoggerApi;
101102
private domain?: string;
102103
private mappedEmailShaIdentityType?: string | null;
104+
105+
// TODO: Refactor into a preinit class or ready queue manager
106+
constructor(private _preInit: IPreInit) {
107+
this._preInit = _preInit;
108+
}
109+
103110
/**
104111
* Initializes the RoktManager with configuration settings and user data.
105112
*
@@ -155,8 +162,15 @@ export default class RoktManager {
155162
}
156163

157164
public attachKit(kit: IRoktKit): void {
165+
console.warn('RoktManager: attaching kit');
158166
this.kit = kit;
159167
this.processMessageQueue();
168+
169+
// Process ready queue only if identity call failed
170+
// This ensures queued SDK methods execute even when identity initialization fails
171+
if (this.store.identityCallFailed) {
172+
this._preInit.readyQueue = processReadyQueue(this._preInit.readyQueue);
173+
}
160174
}
161175

162176
/**
@@ -180,6 +194,7 @@ export default class RoktManager {
180194
}
181195

182196
try {
197+
console.warn('RoktManager: selectPlacements');
183198
const { attributes } = options;
184199
const sandboxValue = attributes?.sandbox || null;
185200
const mappedAttributes = this.mapPlacementAttributes(attributes, this.placementAttributesMapping);
@@ -357,7 +372,7 @@ export default class RoktManager {
357372
this.store.setLocalSessionAttribute(key, value);
358373
}
359374

360-
private isReady(): boolean {
375+
public isReady(): boolean {
361376
// The Rokt Manager is ready when a kit is attached and has a launcher
362377
return Boolean(this.kit && this.kit.launcher);
363378
}
@@ -430,6 +445,7 @@ export default class RoktManager {
430445
}
431446

432447
private deferredCall<T>(methodName: string, payload: any): Promise<T> {
448+
console.warn('deferredCall', methodName, payload);
433449
return new Promise<T>((resolve, reject) => {
434450
const messageId = `${methodName}_${generateUniqueId()}`;
435451

src/store.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export interface IStore {
182182
context: Context | null;
183183
configurationLoaded: boolean;
184184
identityCallInFlight: boolean;
185+
identityCallFailed: boolean;
185186
SDKConfig: Partial<SDKConfig>;
186187
nonCurrentUserMPIDs: Record<MPID, Dictionary>;
187188
identifyCalled: boolean;
@@ -266,6 +267,7 @@ export default function Store(
266267
context: null,
267268
configurationLoaded: false,
268269
identityCallInFlight: false,
270+
identityCallFailed: false,
269271
SDKConfig: {},
270272
nonCurrentUserMPIDs: {},
271273
identifyCalled: false,

test/jest/roktManager.spec.ts

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { IKitConfigs } from "../../src/configAPIClient";
22
import { IMParticleUser } from "../../src/identity-user-interfaces";
33
import { SDKIdentityApi } from "../../src/identity.interfaces";
44
import { IMParticleWebSDKInstance } from "../../src/mp-instance";
5+
import { IPreInit } from "../../src/pre-init-utils";
56
import RoktManager, { IRoktKit, IRoktSelectPlacementsOptions } from "../../src/roktManager";
67
import { testMPID } from '../src/config/constants';
78

@@ -10,6 +11,7 @@ const resolvePromise = () => new Promise(resolve => setTimeout(resolve, 0));
1011
describe('RoktManager', () => {
1112
let roktManager: RoktManager;
1213
let currentUser: IMParticleUser;
14+
let preInit: IPreInit;
1315

1416
const mockMPInstance = ({
1517
Identity: {
@@ -42,7 +44,12 @@ describe('RoktManager', () => {
4244
} as unknown) as IMParticleWebSDKInstance;
4345

4446
beforeEach(() => {
45-
roktManager = new RoktManager();
47+
preInit = {
48+
readyQueue: [],
49+
integrationDelays: {},
50+
forwarderConstructors: [],
51+
};
52+
roktManager = new RoktManager(preInit);
4653
currentUser = {
4754
setUserAttributes: jest.fn(),
4855
getUserIdentities: jest.fn().mockReturnValue({
@@ -440,8 +447,10 @@ describe('RoktManager', () => {
440447
});
441448

442449
describe('#attachKit', () => {
443-
it('should attach a kit', () => {
444-
const kit: IRoktKit = {
450+
let kit: IRoktKit;
451+
452+
beforeEach(() => {
453+
kit = {
445454
launcher: {
446455
selectPlacements: jest.fn(),
447456
hashAttributes: jest.fn(),
@@ -455,10 +464,77 @@ describe('RoktManager', () => {
455464
setExtensionData: jest.fn(),
456465
use: jest.fn(),
457466
};
467+
});
458468

469+
it('should attach a kit', () => {
459470
roktManager.attachKit(kit);
460471
expect(roktManager['kit']).not.toBeNull();
461472
});
473+
474+
it('should call processMessageQueue when kit is attached', () => {
475+
const processMessageQueueSpy = jest.spyOn(roktManager as any, 'processMessageQueue');
476+
477+
roktManager.attachKit(kit);
478+
479+
expect(processMessageQueueSpy).toHaveBeenCalledTimes(1);
480+
processMessageQueueSpy.mockRestore();
481+
});
482+
483+
it('should process ready queue when kit is attached and identity call failed', () => {
484+
const readyQueueItem1 = jest.fn();
485+
const readyQueueItem2 = jest.fn();
486+
preInit.readyQueue = [readyQueueItem1, readyQueueItem2];
487+
488+
// Set identityCallFailed to true
489+
roktManager['store'].identityCallFailed = true;
490+
491+
roktManager.attachKit(kit);
492+
493+
expect(readyQueueItem1).toHaveBeenCalledTimes(1);
494+
expect(readyQueueItem2).toHaveBeenCalledTimes(1);
495+
expect(preInit.readyQueue).toEqual([]);
496+
});
497+
498+
it('should NOT process ready queue when identity call did not fail', () => {
499+
const readyQueueItem1 = jest.fn();
500+
const readyQueueItem2 = jest.fn();
501+
preInit.readyQueue = [readyQueueItem1, readyQueueItem2];
502+
503+
// Set identityCallFailed to false
504+
roktManager['store'].identityCallFailed = false;
505+
506+
roktManager.attachKit(kit);
507+
508+
// Ready queue items should NOT be called
509+
expect(readyQueueItem1).not.toHaveBeenCalled();
510+
expect(readyQueueItem2).not.toHaveBeenCalled();
511+
// Ready queue should remain unchanged
512+
expect(preInit.readyQueue).toEqual([readyQueueItem1, readyQueueItem2]);
513+
});
514+
515+
it('should process message queue before processing ready queue when identity call failed', () => {
516+
const processMessageQueueSpy = jest.spyOn(roktManager as any, 'processMessageQueue');
517+
const processReadyQueueSpy = jest.spyOn(require('../../src/pre-init-utils'), 'processReadyQueue');
518+
519+
// Set identityCallFailed to true so ready queue is processed
520+
roktManager['store'].identityCallFailed = true;
521+
522+
roktManager.attachKit(kit);
523+
524+
// Verify both were called
525+
expect(processMessageQueueSpy).toHaveBeenCalled();
526+
expect(processReadyQueueSpy).toHaveBeenCalled();
527+
528+
// Verify processMessageQueue was called before processReadyQueue
529+
// by checking the order of calls
530+
const messageQueueCallIndex = processMessageQueueSpy.mock.invocationCallOrder[0];
531+
const readyQueueCallIndex = processReadyQueueSpy.mock.invocationCallOrder[0];
532+
533+
expect(messageQueueCallIndex).toBeLessThan(readyQueueCallIndex);
534+
535+
processMessageQueueSpy.mockRestore();
536+
processReadyQueueSpy.mockRestore();
537+
});
462538
});
463539

464540
describe('#processMessageQueue', () => {

0 commit comments

Comments
 (0)