diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index a1a4326..845a82e 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -29,6 +29,7 @@ var constructor = function () { self.filters = {}; self.userAttributes = {}; self.testHelpers = null; + self.placementEventMappingLookup = {}; /** * Generates the Rokt launcher script URL with optional domain override and extensions @@ -74,8 +75,16 @@ var constructor = function () { ) { var accountId = settings.accountId; var roktExtensions = extractRoktExtensions(settings.roktExtensions); - self.userAttributes = filteredUserAttributes; + self.userAttributes = filteredUserAttributes || {}; self.onboardingExpProvider = settings.onboardingExpProvider; + + var placementEventMapping = parseSettingsString( + settings.placementEventMapping + ); + self.placementEventMappingLookup = generateMappedEventLookup( + placementEventMapping + ); + var domain = window.mParticle.Rokt.domain; var launcherOptions = mergeObjects( {}, @@ -89,6 +98,9 @@ var constructor = function () { self.testHelpers = { generateLauncherScript: generateLauncherScript, extractRoktExtensions: extractRoktExtensions, + hashEventMessage: hashEventMessage, + parseSettingsString: parseSettingsString, + generateMappedEventLookup: generateMappedEventLookup, }; attachLauncher(accountId, launcherOptions); return; @@ -199,10 +211,20 @@ var constructor = function () { var filteredUserIdentities = returnUserIdentities(filteredUser); + var localSessionAttributes = {}; + + try { + localSessionAttributes = + window.mParticle.Rokt.getLocalSessionAttributes(); + } catch (error) { + console.error('Error getting local session attributes:', error); + } + var selectPlacementsAttributes = mergeObjects( filteredUserIdentities, replaceOtherWithEmailsha256(filteredAttributes), optimizelyAttributes, + localSessionAttributes, { mpid: mpid, } @@ -231,6 +253,26 @@ var constructor = function () { window.Rokt.setExtensionData(partnerExtensionData); } + function processEvent(event) { + if ( + !isKitReady() || + typeof window.mParticle.Rokt.setLocalSessionAttribute !== 'function' + ) { + return; + } + + var hashedEvent = hashEventMessage( + event.EventDataType, + event.EventCategory, + event.EventName + ); + + if (self.placementEventMappingLookup[hashedEvent]) { + var mappedValue = self.placementEventMappingLookup[hashedEvent]; + window.mParticle.Rokt.setLocalSessionAttribute(mappedValue, true); + } + } + function onUserIdentified(filteredUser) { self.filters.filteredUser = filteredUser; self.userAttributes = filteredUser.getAllUserAttributes(); @@ -330,6 +372,7 @@ var constructor = function () { // Kit Callback Methods this.init = initForwarder; + this.process = processEvent; this.setExtensionData = setExtensionData; this.setUserAttribute = setUserAttribute; this.onUserIdentified = onUserIdentified; @@ -429,6 +472,25 @@ function extractRoktExtensions(settingsString) { return roktExtensions; } +function generateMappedEventLookup(placementEventMapping) { + if (!placementEventMapping) { + return {}; + } + + var mappedEvents = {}; + for (var i = 0; i < placementEventMapping.length; i++) { + var mapping = placementEventMapping[i]; + mappedEvents[mapping.jsmap] = mapping.value; + } + return mappedEvents; +} + +function hashEventMessage(messageType, eventType, eventName) { + return window.mParticle.generateHash( + [messageType, eventType, eventName].join('') + ); +} + if (window && window.mParticle && window.mParticle.addForwarder) { window.mParticle.addForwarder({ name: name, diff --git a/test/src/tests.js b/test/src/tests.js index 85d1dce..73b4c71 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -22,6 +22,27 @@ const waitForCondition = async (conditionFn, timeout = 200, interval = 10) => { }; describe('Rokt Forwarder', () => { + var EventType = { + Unknown: 0, + Navigation: 1, + Location: 2, + Search: 3, + Transaction: 4, + UserContent: 5, + UserPreference: 6, + Social: 7, + Other: 8, + Media: 9, + }; + var MessageType = { + SessionStart: 1, + SessionEnd: 2, + PageView: 3, + PageEvent: 4, + CrashReport: 5, + OptOut: 6, + Commerce: 16, + }; var ReportingService = function () { var self = this; @@ -58,9 +79,15 @@ describe('Rokt Forwarder', () => { }; }, }; + mParticle._Store = { + localSessionAttributes: {}, + }; mParticle._getActiveForwarders = function () { return []; }; + mParticle.generateHash = function (input) { + return 'hashed-<' + input + '>-value'; + }; // -------------------START EDITING BELOW:----------------------- var MockRoktForwarder = function () { var self = this; @@ -303,6 +330,42 @@ describe('Rokt Forwarder', () => { const expectedProcessedName = `${sdkVersion}_${kitVersion}_${originalIntegrationName}`; window.Rokt.integrationName.should.equal(expectedProcessedName); }); + + it('should initialize the kit with placement event mapping lookup from a config', async () => { + await mParticle.forwarder.init( + { + accountId: '123456', + placementEventMapping: JSON.stringify([ + { + jsmap: '-1484452948', + map: '-5208850776883573773', + maptype: 'EventClass.Id', + value: 'foo-mapped-flag', + }, + { + jsmap: '1838502119', + map: '1324617889422969328', + maptype: 'EventClass.Id', + value: 'ad_viewed_test', + }, + ]), + }, + reportService.cb, + true, + null, + {} + ); + + // Wait for initialization to complete (after launcher is created) + await waitForCondition(() => window.mParticle.Rokt.isInitialized); + + window.mParticle.forwarder.placementEventMappingLookup.should.deepEqual( + { + '-1484452948': 'foo-mapped-flag', + 1838502119: 'ad_viewed_test', + } + ); + }); }); describe('#hashAttributes', () => { @@ -559,6 +622,17 @@ describe('Rokt Forwarder', () => { window.mParticle.Rokt.attachKitCalled = true; return Promise.resolve(); }; + window.mParticle.Rokt.setLocalSessionAttribute = function ( + key, + value + ) { + mParticle._Store.localSessionAttributes[key] = value; + }; + window.mParticle.Rokt.getLocalSessionAttributes = function () { + return mParticle._Store.localSessionAttributes; + }; + window.mParticle.Rokt.store = window.mParticle._Store; + window.mParticle.Rokt.store.localSessionAttributes = {}; window.mParticle.forwarder.launcher = { selectPlacements: function (options) { window.mParticle.Rokt.selectPlacementsOptions = options; @@ -636,6 +710,39 @@ describe('Rokt Forwarder', () => { }, }); }); + + it('should collect local session attributes and send to launcher.selectPlacements', async () => { + window.mParticle.Rokt.store.localSessionAttributes = { + 'custom-local-attribute': true, + 'secondary-local-attribute': true, + }; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true + ); + + await window.mParticle.forwarder.selectPlacements({ + identifier: 'test-placement', + attributes: { + 'test-attribute': 'test-value', + }, + }); + + window.Rokt.selectPlacementsCalled.should.equal(true); + window.Rokt.selectPlacementsOptions.should.deepEqual({ + identifier: 'test-placement', + attributes: { + mpid: '123', + 'test-attribute': 'test-value', + 'custom-local-attribute': true, + 'secondary-local-attribute': true, + }, + }); + }); }); describe('User Attributes', () => { @@ -744,6 +851,15 @@ describe('Rokt Forwarder', () => { window.mParticle.Rokt.kit = kit; Promise.resolve(); }; + window.mParticle.Rokt.setLocalSessionAttribute = function ( + key, + value + ) { + mParticle._Store.localSessionAttributes[key] = value; + }; + window.mParticle.Rokt.getLocalSessionAttributes = function () { + return mParticle._Store.localSessionAttributes; + }; window.mParticle.forwarder.launcher = { selectPlacements: function (options) { window.mParticle.Rokt.selectPlacementsOptions = options; @@ -1337,6 +1453,15 @@ describe('Rokt Forwarder', () => { window.mParticle.Rokt.kit = kit; Promise.resolve(); }; + window.mParticle.Rokt.setLocalSessionAttribute = function ( + key, + value + ) { + mParticle._Store.localSessionAttributes[key] = value; + }; + window.mParticle.Rokt.getLocalSessionAttributes = function () { + return mParticle._Store.localSessionAttributes; + }; window.mParticle.forwarder.launcher = { selectPlacements: function (options) { window.mParticle.Rokt.selectPlacementsOptions = options; @@ -1593,4 +1718,222 @@ describe('Rokt Forwarder', () => { .should.deepEqual([]); }); }); + + describe('#generateMappedEventLookup', () => { + beforeEach(async () => { + window.Rokt = new MockRoktForwarder(); + window.mParticle.Rokt = window.Rokt; + + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true + ); + }); + + it('should generate a lookup table from a placement event mapping', () => { + const placementEventMapping = [ + { + jsmap: '-1484452948', + map: '-5208850776883573773', + maptype: 'EventClass.Id', + value: 'foo-mapped-flag', + }, + { + jsmap: '1838502119', + map: '1324617889422969328', + maptype: 'EventClass.Id', + value: 'ad_viewed_test', + }, + ]; + + window.mParticle.forwarder.testHelpers + .generateMappedEventLookup(placementEventMapping) + .should.deepEqual({ + '-1484452948': 'foo-mapped-flag', + 1838502119: 'ad_viewed_test', + }); + }); + + it('should return an empty object if the placement event mapping is null', () => { + window.mParticle.forwarder.testHelpers + .generateMappedEventLookup(null) + .should.deepEqual({}); + }); + }); + + describe('#processEvent', () => { + beforeEach(() => { + window.Rokt = new MockRoktForwarder(); + window.Rokt.createLauncher = async function () { + return Promise.resolve({ + selectPlacements: function (options) { + window.mParticle.Rokt.selectPlacementsOptions = options; + window.mParticle.Rokt.selectPlacementsCalled = true; + }, + }); + }; + window.mParticle.Rokt = window.Rokt; + window.mParticle.Rokt.attachKitCalled = false; + window.mParticle.Rokt.attachKit = async (kit) => { + window.mParticle.Rokt.attachKitCalled = true; + window.mParticle.Rokt.kit = kit; + Promise.resolve(); + }; + window.mParticle.Rokt.setLocalSessionAttribute = function ( + key, + value + ) { + window.mParticle._Store.localSessionAttributes[key] = value; + }; + window.mParticle.Rokt.getLocalSessionAttributes = function () { + return window.mParticle._Store.localSessionAttributes; + }; + window.mParticle.forwarder.launcher = { + selectPlacements: function (options) { + window.mParticle.Rokt.selectPlacementsOptions = options; + window.mParticle.Rokt.selectPlacementsCalled = true; + }, + }; + window.mParticle.Rokt.filters = { + userAttributesFilters: [], + filterUserAttributes: function (attributes) { + return attributes; + }, + filteredUser: { + getMPID: function () { + return '123'; + }, + }, + }; + }); + + it('set a local session selection attribute if the event is a mapped placement event', async () => { + // Mocks hashed values for testing + const placementEventMapping = JSON.stringify([ + { + jsmap: 'hashed-<48Video Watched>-value', + map: '123466', + maptype: 'EventClass.Id', + value: 'foo-mapped-flag', + }, + { + jsmap: 'hashed-<29Other Value>-value', + map: '1279898989', + maptype: 'EventClass.Id', + value: 'ad_viewed_test', + }, + ]); + + await window.mParticle.forwarder.init( + { + accountId: '123456', + placementEventMapping, + }, + reportService.cb, + true, + null, + {} + ); + + await waitForCondition(() => window.mParticle.Rokt.attachKitCalled); + + window.mParticle.forwarder.process({ + EventName: 'Video Watched', + EventCategory: EventType.Other, + EventDataType: MessageType.PageEvent, + }); + + window.mParticle._Store.localSessionAttributes.should.deepEqual({ + 'foo-mapped-flag': true, + }); + }); + }); + + describe('#parseSettingsString', () => { + it('should parse null values in a settings string appropriately', () => { + const settingsString = + '[{"jsmap":null,"map":"f.name","maptype":"UserAttributeClass.Name","value":"firstname"},{"jsmap":null,"map":"last_name","maptype":"UserAttributeClass.Name","value":"lastname"}]'; + + window.mParticle.forwarder.testHelpers + .parseSettingsString(settingsString) + .should.deepEqual([ + { + jsmap: null, + map: 'f.name', + maptype: 'UserAttributeClass.Name', + value: 'firstname', + }, + { + jsmap: null, + map: 'last_name', + maptype: 'UserAttributeClass.Name', + value: 'lastname', + }, + ]); + }); + + it('should convert jmap and map number values to stringified numbers when parsed', () => { + const settingsString = + '[{"jsmap":"-1484452948","map":"-5208850776883573773","maptype":"EventClass.Id","value":"abc"},{"jsmap":"1838502119","map":"1324617889422969328","maptype":"EventClass.Id","value":"bcd"},{"jsmap":"-355458063","map":"5878452521714063084","maptype":"EventClass.Id","value":"card_viewed_test"}]'; + + window.mParticle.forwarder.testHelpers + .parseSettingsString(settingsString) + .should.deepEqual([ + { + jsmap: '-1484452948', + map: '-5208850776883573773', + maptype: 'EventClass.Id', + value: 'abc', + }, + { + jsmap: '1838502119', + map: '1324617889422969328', + maptype: 'EventClass.Id', + value: 'bcd', + }, + { + jsmap: '-355458063', + map: '5878452521714063084', + maptype: 'EventClass.Id', + value: 'card_viewed_test', + }, + ]); + }); + + it('returns an empty array if the settings string is empty', () => { + const settingsString = ''; + + window.mParticle.forwarder.testHelpers + .parseSettingsString(settingsString) + .should.deepEqual([]); + }); + + it('returns an empty array if the settings string is not a valid JSON', () => { + const settingsString = 'not a valid JSON'; + + window.mParticle.forwarder.testHelpers + .parseSettingsString(settingsString) + .should.deepEqual([]); + }); + }); + + describe('#hashEventMessage', () => { + it('should hash event message using generateHash in the proper order', () => { + const eventName = 'Test Event'; + const eventType = EventType.Other; + const messageType = MessageType.PageEvent; + const resultHash = + window.mParticle.forwarder.testHelpers.hashEventMessage( + messageType, + eventType, + eventName + ); + + // Order should be messageType (4), eventType (8), eventName (Test Event) + resultHash.should.equal('hashed-<48Test Event>-value'); + }); + }); });