Skip to content

Commit 3a0643b

Browse files
authored
fix: SDKE-317 Call deferred methods on Rokt Manager instead of kit (#1076)
1 parent 8187d72 commit 3a0643b

File tree

2 files changed

+137
-5
lines changed

2 files changed

+137
-5
lines changed

src/roktManager.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,22 +319,25 @@ export default class RoktManager {
319319
this.logger?.verbose(`RoktManager: Processing ${this.messageQueue.size} queued messages`);
320320

321321
this.messageQueue.forEach((message) => {
322-
if(!(message.methodName in this.kit) || !isFunction(this.kit[message.methodName])) {
323-
this.logger?.error(`RoktManager: Method ${message.methodName} not found in kit`);
322+
if(!(message.methodName in this) || !isFunction(this[message.methodName])) {
323+
this.logger?.error(`RoktManager: Method ${message.methodName} not found`);
324324
return;
325325
}
326326

327327
this.logger?.verbose(`RoktManager: Processing queued message: ${message.methodName} with payload: ${JSON.stringify(message.payload)}`);
328328

329329
try {
330-
const result = (this.kit[message.methodName] as Function)(message.payload);
330+
const result = (this[message.methodName] as Function)(message.payload);
331331
this.completePendingPromise(message.messageId, result);
332332
} catch (error) {
333333
const errorMessage = error instanceof Error ? error.message : String(error);
334334
this.logger?.error(`RoktManager: Error processing message '${message.methodName}': ${errorMessage}`);
335335
this.completePendingPromise(message.messageId, Promise.reject(error));
336336
}
337337
});
338+
339+
// Clear the queue after processing all messages
340+
this.messageQueue.clear();
338341
}
339342

340343
private queueMessage(message: IRoktMessage): void {

test/jest/roktManager.spec.ts

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,10 @@ describe('RoktManager', () => {
389389
});
390390

391391
describe('#processMessageQueue', () => {
392-
it('should process the message queue if a launcher and kit are attached', () => {
393-
const kit: IRoktKit = {
392+
let kit: IRoktKit;
393+
394+
beforeEach(() => {
395+
kit = {
394396
launcher: {
395397
selectPlacements: jest.fn(),
396398
hashAttributes: jest.fn(),
@@ -404,6 +406,9 @@ describe('RoktManager', () => {
404406
setExtensionData: jest.fn(),
405407
use: jest.fn(),
406408
};
409+
});
410+
411+
it('should process the message queue if a launcher and kit are attached', () => {
407412

408413

409414
roktManager.selectPlacements({} as IRoktSelectPlacementsOptions);
@@ -420,6 +425,130 @@ describe('RoktManager', () => {
420425
expect(roktManager['messageQueue'].size).toBe(0);
421426
expect(kit.selectPlacements).toHaveBeenCalledTimes(3);
422427
});
428+
429+
it('should call RoktManager methods (not kit methods directly) when processing queue', () => {
430+
// Queue some calls before kit is ready (these will be deferred)
431+
const selectOptions = { attributes: { test: 'value' } } as IRoktSelectPlacementsOptions;
432+
const hashAttrs = { email: '[email protected]' };
433+
const extensionData = { 'test-ext': { config: true } };
434+
const useName = 'TestExtension';
435+
436+
roktManager.selectPlacements(selectOptions);
437+
roktManager.hashAttributes(hashAttrs);
438+
roktManager.setExtensionData(extensionData);
439+
roktManager.use(useName);
440+
441+
// Verify calls were queued
442+
expect(roktManager['messageQueue'].size).toBe(4);
443+
expect(kit.selectPlacements).not.toHaveBeenCalled(); // Kit methods not called yet
444+
expect(kit.hashAttributes).not.toHaveBeenCalled(); // Kit methods not called yet
445+
expect(kit.setExtensionData).not.toHaveBeenCalled(); // Kit methods not called yet
446+
expect(kit.use).not.toHaveBeenCalled(); // Kit methods not called yet
447+
448+
// Spy on RoktManager methods AFTER initial calls to track queue processing
449+
const selectPlacementsSpy = jest.spyOn(roktManager, 'selectPlacements');
450+
const hashAttributesSpy = jest.spyOn(roktManager, 'hashAttributes');
451+
const setExtensionDataSpy = jest.spyOn(roktManager, 'setExtensionData');
452+
const useSpy = jest.spyOn(roktManager, 'use');
453+
454+
// Attach kit (triggers processMessageQueue)
455+
roktManager.attachKit(kit);
456+
457+
// Verify RoktManager methods were called during queue processing
458+
expect(selectPlacementsSpy).toHaveBeenCalledTimes(1);
459+
expect(selectPlacementsSpy).toHaveBeenCalledWith(selectOptions);
460+
461+
expect(hashAttributesSpy).toHaveBeenCalledTimes(1);
462+
expect(hashAttributesSpy).toHaveBeenCalledWith(hashAttrs);
463+
464+
expect(setExtensionDataSpy).toHaveBeenCalledTimes(1);
465+
expect(setExtensionDataSpy).toHaveBeenCalledWith(extensionData);
466+
467+
expect(useSpy).toHaveBeenCalledTimes(1);
468+
expect(useSpy).toHaveBeenCalledWith(useName);
469+
470+
// Verify queue was cleared
471+
expect(roktManager['messageQueue'].size).toBe(0);
472+
473+
// Clean up spies
474+
selectPlacementsSpy.mockRestore();
475+
hashAttributesSpy.mockRestore();
476+
setExtensionDataSpy.mockRestore();
477+
useSpy.mockRestore();
478+
});
479+
480+
it('should preserve RoktManager preprocessing logic when processing deferred selectPlacements calls', () => {
481+
// Set up placement attributes mapping to test preprocessing
482+
roktManager['placementAttributesMapping'] = [
483+
{
484+
jsmap: null,
485+
map: 'original_key',
486+
maptype: 'UserAttributeClass.Name',
487+
value: 'mapped_key'
488+
}
489+
];
490+
491+
// Set up current user mock
492+
roktManager['currentUser'] = {
493+
setUserAttributes: jest.fn(),
494+
getUserIdentities: jest.fn().mockReturnValue({
495+
userIdentities: {}
496+
})
497+
} as unknown as IMParticleUser;
498+
499+
// Queue a selectPlacements call with attributes that need mapping
500+
const originalOptions: IRoktSelectPlacementsOptions = {
501+
attributes: {
502+
original_key: 'test_value',
503+
other_attr: 'other_value'
504+
}
505+
};
506+
507+
roktManager.selectPlacements(originalOptions);
508+
expect(roktManager['messageQueue'].size).toBe(1);
509+
510+
// Attach kit (triggers processMessageQueue)
511+
roktManager.attachKit(kit);
512+
513+
// Verify the kit method was called with MAPPED attributes (proving preprocessing occurred)
514+
const expectedMappedOptions = {
515+
attributes: {
516+
mapped_key: 'test_value', // This key should be mapped
517+
other_attr: 'other_value' // This key should remain unchanged
518+
}
519+
};
520+
521+
expect(kit.selectPlacements).toHaveBeenCalledWith(expectedMappedOptions);
522+
expect(roktManager['currentUser'].setUserAttributes).toHaveBeenCalledWith({
523+
mapped_key: 'test_value',
524+
other_attr: 'other_value'
525+
});
526+
});
527+
528+
529+
it('should skip processing if method does not exist on RoktManager', () => {
530+
// Manually add a message with a non-existent method name
531+
roktManager['queueMessage']({
532+
messageId: 'test_123',
533+
methodName: 'nonExistentMethod',
534+
payload: { test: 'data' },
535+
resolve: jest.fn(),
536+
reject: jest.fn()
537+
});
538+
539+
expect(roktManager['messageQueue'].size).toBe(1);
540+
541+
// Attach kit (triggers processMessageQueue)
542+
roktManager.attachKit(kit);
543+
544+
// Verify error was logged for non-existent method
545+
expect(mockMPInstance.Logger.error).toHaveBeenCalledWith(
546+
'RoktManager: Method nonExistentMethod not found'
547+
);
548+
549+
// Verify message was removed from queue even though method didn't exist
550+
expect(roktManager['messageQueue'].size).toBe(0);
551+
});
423552
});
424553

425554
describe('#selectPlacements', () => {

0 commit comments

Comments
 (0)