From 1918bf17ab90e2253317b585d4e8dbf7f39a49fc Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Thu, 17 Apr 2025 17:37:01 -0400 Subject: [PATCH 1/8] feat: Add hashedAttributes method --- src/Rokt-Kit.js | 34 +++++++++++++- test/src/tests.js | 115 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index fc1436f..8d36551 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -30,6 +30,19 @@ var constructor = function () { self.filteredUser = {}; self.userAttributes = {}; + /** + * Passes attributes to the Rokt Web SDK for server-side hashing + * @param {Object} attributes - The attributes to be hashed + * @returns {Object} The hashed attributes from the launcher + */ + function hashedAttributes(attributes) { + if (!isInitialized()) { + console.error('Rokt Kit: Not initialized'); + return null; + } + return self.launcher.hashedAttributes(attributes); + } + function initForwarder( settings, _service, @@ -192,8 +205,6 @@ var constructor = function () { }); } - // Called by the mParticle Rokt Manager - this.selectPlacements = selectPlacements; // mParticle Kit Callback Methods function fetchOptimizely() { @@ -236,10 +247,29 @@ var constructor = function () { } return {}; } + + + // Called by the mParticle Rokt Manager + this.selectPlacements = selectPlacements; + this.hashedAttributes = hashedAttributes; + + // Kit Callback Methods this.init = initForwarder; this.setUserAttribute = setUserAttribute; this.onUserIdentified = onUserIdentified; this.removeUserAttribute = removeUserAttribute; + + /** + * Checks if the kit is properly initialized and ready for use. + * Both conditions must be true: + * 1. self.isInitialized - Set after successful initialization of the kit + * 2. self.launcher - The Rokt launcher instance must be available + * @returns {boolean} Whether the kit is fully initialized + */ + function isInitialized() { + return !!(self.isInitialized && self.launcher); + } + }; function getId() { diff --git a/test/src/tests.js b/test/src/tests.js index c616119..5d22db7 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -189,6 +189,121 @@ describe('Rokt Forwarder', () => { }); }); + describe('#hashedAttributes', () => { + beforeEach(() => { + window.Rokt = new MockRoktForwarder(); + 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.forwarder.launcher = { + hashedAttributes: function (attributes) { + window.mParticle.Rokt.hashedAttributesOptions = attributes; + window.mParticle.Rokt.hashedAttributesCalled = true; + + // Mocking the hashedAttributes method to show that + // the attributes will be transformed by the launcher's + // hashedAttributes method. + return Promise.resolve({ + 'test-attribute': 'hashed-value', + }); + }, + }; + }); + + it('should call launcher.hashedAttributes with passed through attributes when fully initialized', function () { + // Ensure both initialization conditions are met + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = { + hashedAttributes: function (attributes) { + window.mParticle.Rokt.hashedAttributesOptions = attributes; + window.mParticle.Rokt.hashedAttributesCalled = true; + return { + 'test-attribute': 'hashed-value', + }; + }, + }; + + var attributes = { + 'test-attribute': 'test-value', + }; + + window.mParticle.forwarder.hashedAttributes(attributes); + + window.Rokt.hashedAttributesCalled.should.equal(true); + window.Rokt.hashedAttributesOptions.should.deepEqual(attributes); + }); + + it('should return null when launcher exists but kit is not initialized', function () { + // Set launcher but ensure isInitialized is false + window.mParticle.forwarder.isInitialized = false; + window.mParticle.forwarder.launcher = { + hashedAttributes: function () {}, + }; + + var result = window.mParticle.forwarder.hashedAttributes({ + 'test-attribute': 'test-value', + }); + + (result === null).should.equal(true); + }); + + it('should log an error when called before initialization', function () { + var errorLogged = false; + var errorMessage = null; + window.console.error = function (message) { + errorLogged = true; + errorMessage = message; + }; + + // Ensure kit is not initialized + window.mParticle.forwarder.isInitialized = false; + window.mParticle.forwarder.launcher = null; + + window.mParticle.forwarder.hashedAttributes({ + 'test-attribute': 'test-value', + }); + + errorLogged.should.equal(true); + errorMessage.should.equal('Rokt Kit: Not initialized'); + }); + + it('should return null when kit is initialized but launcher is missing', function () { + // Mock isInitialized but remove launcher + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = null; + + var result = window.mParticle.forwarder.hashedAttributes({ + 'test-attribute': 'test-value', + }); + + (result === null).should.equal(true); + }); + + it('should return hashed attributes from launcher', async () => { + await window.mParticle.forwarder.init( + { + accountId: '123456', + }, + reportService.cb, + true, + null, + {} + ); + + const result = await window.mParticle.forwarder.hashedAttributes({ + 'test-attribute': 'test-value', + }); + + result.should.deepEqual({ + 'test-attribute': 'hashed-value', + }); + }); + }); + describe('#selectPlacements', () => { beforeEach(() => { window.Rokt = new MockRoktForwarder(); From 617445b53a040e818fd6a8da4c764aa063528fdf Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 18 Apr 2025 14:39:07 -0400 Subject: [PATCH 2/8] Rename hashedAttributes to hashAttributes --- src/Rokt-Kit.js | 9 ++++----- test/src/tests.js | 36 ++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 8d36551..779ad17 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -33,14 +33,14 @@ var constructor = function () { /** * Passes attributes to the Rokt Web SDK for server-side hashing * @param {Object} attributes - The attributes to be hashed - * @returns {Object} The hashed attributes from the launcher + * @returns {Object|null} The hashed attributes from the launcher, or `null` if the kit is not initialized */ - function hashedAttributes(attributes) { + function hashAttributes(attributes) { if (!isInitialized()) { console.error('Rokt Kit: Not initialized'); return null; } - return self.launcher.hashedAttributes(attributes); + return self.launcher.hashAttributes(attributes); } function initForwarder( @@ -248,10 +248,9 @@ var constructor = function () { return {}; } - // Called by the mParticle Rokt Manager this.selectPlacements = selectPlacements; - this.hashedAttributes = hashedAttributes; + this.hashAttributes = hashAttributes; // Kit Callback Methods this.init = initForwarder; diff --git a/test/src/tests.js b/test/src/tests.js index 5d22db7..9bc2b8e 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -189,7 +189,7 @@ describe('Rokt Forwarder', () => { }); }); - describe('#hashedAttributes', () => { + describe('#hashAttributes', () => { beforeEach(() => { window.Rokt = new MockRoktForwarder(); window.mParticle.Rokt = window.Rokt; @@ -200,13 +200,13 @@ describe('Rokt Forwarder', () => { Promise.resolve(); }; window.mParticle.forwarder.launcher = { - hashedAttributes: function (attributes) { - window.mParticle.Rokt.hashedAttributesOptions = attributes; - window.mParticle.Rokt.hashedAttributesCalled = true; + hashAttributes: function (attributes) { + window.mParticle.Rokt.hashAttributesOptions = attributes; + window.mParticle.Rokt.hashAttributesCalled = true; - // Mocking the hashedAttributes method to show that + // Mocking the hashAttributes method to show that // the attributes will be transformed by the launcher's - // hashedAttributes method. + // hashAttributes method. return Promise.resolve({ 'test-attribute': 'hashed-value', }); @@ -214,13 +214,13 @@ describe('Rokt Forwarder', () => { }; }); - it('should call launcher.hashedAttributes with passed through attributes when fully initialized', function () { + it('should call launcher.hashAttributes with passed through attributes when fully initialized', function () { // Ensure both initialization conditions are met window.mParticle.forwarder.isInitialized = true; window.mParticle.forwarder.launcher = { - hashedAttributes: function (attributes) { - window.mParticle.Rokt.hashedAttributesOptions = attributes; - window.mParticle.Rokt.hashedAttributesCalled = true; + hashAttributes: function (attributes) { + window.mParticle.Rokt.hashAttributesOptions = attributes; + window.mParticle.Rokt.hashAttributesCalled = true; return { 'test-attribute': 'hashed-value', }; @@ -231,20 +231,20 @@ describe('Rokt Forwarder', () => { 'test-attribute': 'test-value', }; - window.mParticle.forwarder.hashedAttributes(attributes); + window.mParticle.forwarder.hashAttributes(attributes); - window.Rokt.hashedAttributesCalled.should.equal(true); - window.Rokt.hashedAttributesOptions.should.deepEqual(attributes); + window.Rokt.hashAttributesCalled.should.equal(true); + window.Rokt.hashAttributesOptions.should.deepEqual(attributes); }); it('should return null when launcher exists but kit is not initialized', function () { // Set launcher but ensure isInitialized is false window.mParticle.forwarder.isInitialized = false; window.mParticle.forwarder.launcher = { - hashedAttributes: function () {}, + hashAttributes: function () {}, }; - var result = window.mParticle.forwarder.hashedAttributes({ + var result = window.mParticle.forwarder.hashAttributes({ 'test-attribute': 'test-value', }); @@ -263,7 +263,7 @@ describe('Rokt Forwarder', () => { window.mParticle.forwarder.isInitialized = false; window.mParticle.forwarder.launcher = null; - window.mParticle.forwarder.hashedAttributes({ + window.mParticle.forwarder.hashAttributes({ 'test-attribute': 'test-value', }); @@ -276,7 +276,7 @@ describe('Rokt Forwarder', () => { window.mParticle.forwarder.isInitialized = true; window.mParticle.forwarder.launcher = null; - var result = window.mParticle.forwarder.hashedAttributes({ + var result = window.mParticle.forwarder.hashAttributes({ 'test-attribute': 'test-value', }); @@ -294,7 +294,7 @@ describe('Rokt Forwarder', () => { {} ); - const result = await window.mParticle.forwarder.hashedAttributes({ + const result = await window.mParticle.forwarder.hashAttributes({ 'test-attribute': 'test-value', }); From 5443caa7191203ef78d0c4f1d0e653981dc9104f Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 18 Apr 2025 14:42:58 -0400 Subject: [PATCH 3/8] Remove lint --- src/Rokt-Kit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 779ad17..ebd12ea 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -205,7 +205,6 @@ var constructor = function () { }); } - // mParticle Kit Callback Methods function fetchOptimizely() { var forwarders = window.mParticle From 64b1cd7d38a2b0aaa24685cf6cf8fa853766c7c3 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Fri, 18 Apr 2025 14:44:11 -0400 Subject: [PATCH 4/8] Remove lint --- src/Rokt-Kit.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index ebd12ea..992896e 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -267,7 +267,6 @@ var constructor = function () { function isInitialized() { return !!(self.isInitialized && self.launcher); } - }; function getId() { From d4ebac02a4f0d20f2bdf371097ee1176787066c1 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 21 Apr 2025 10:16:22 -0400 Subject: [PATCH 5/8] fix: Update hashAttributes documentation to reflect Promise return type --- src/Rokt-Kit.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index 992896e..dbbf84d 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -33,7 +33,8 @@ var constructor = function () { /** * Passes attributes to the Rokt Web SDK for server-side hashing * @param {Object} attributes - The attributes to be hashed - * @returns {Object|null} The hashed attributes from the launcher, or `null` if the kit is not initialized + * @returns {Promise} A Promise resolving to the + * hashed attributes from the launcher, or `null` if the kit is not initialized */ function hashAttributes(attributes) { if (!isInitialized()) { From 432244e786b115a58a2d105513ccfbed70fdbc5d Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 21 Apr 2025 10:24:01 -0400 Subject: [PATCH 6/8] docs: Enhance documentation for hashAttributes and selectPlacements methods with relevant links and parameter details --- src/Rokt-Kit.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index dbbf84d..c9f9ae7 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -32,6 +32,7 @@ var constructor = function () { /** * Passes attributes to the Rokt Web SDK for server-side hashing + * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes * @param {Object} attributes - The attributes to be hashed * @returns {Promise} A Promise resolving to the * hashed attributes from the launcher, or `null` if the kit is not initialized @@ -95,12 +96,15 @@ var constructor = function () { } } + /** + * Selects placements for Rokt Web SDK with merged attributes, filters, and experimentation options + * @see https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ + * @param {Object} options - The options object for selecting placements containing: + * - identifier {string}: The placement identifier + * - attributes {Object}: Optional attributes to merge with existing attributes + * @returns {Promise} A Promise resolving to the Rokt launcher's selectPlacements method with processed attributes + */ function selectPlacements(options) { - // https://docs.rokt.com/developers/integration-guides/web/library/select-placements-options/ - // options should contain: - // - identifier - // - attributes - var attributes = (options && options.attributes) || {}; var placementAttributes = mergeObjects(self.userAttributes, attributes); From d9885686733b2724f5896e3b00290b0fe7096255 Mon Sep 17 00:00:00 2001 From: Alex S <49695018+alexs-mparticle@users.noreply.github.com> Date: Mon, 21 Apr 2025 14:48:31 -0400 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Robert Ing --- src/Rokt-Kit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rokt-Kit.js b/src/Rokt-Kit.js index c9f9ae7..17b12c4 100644 --- a/src/Rokt-Kit.js +++ b/src/Rokt-Kit.js @@ -31,7 +31,7 @@ var constructor = function () { self.userAttributes = {}; /** - * Passes attributes to the Rokt Web SDK for server-side hashing + * Passes attributes to the Rokt Web SDK for client-side hashing * @see https://docs.rokt.com/developers/integration-guides/web/library/integration-launcher#hash-attributes * @param {Object} attributes - The attributes to be hashed * @returns {Promise} A Promise resolving to the From 5ec12157d0bac595eadb417e808b518afe65c541 Mon Sep 17 00:00:00 2001 From: Alexander Sapountzis Date: Mon, 21 Apr 2025 15:00:47 -0400 Subject: [PATCH 8/8] Address PR Comments --- package.json | 2 +- test/src/tests.js | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 41e5faf..85060c6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "lint": "eslint src/ test/src/", "lint:fix": "eslint src/ test/src/ --fix", "watch": "rollup --config rollup.config.js -w", - "watch:test": "rollup --config rollup.test.config.js -w", + "watch:tests": "rollup --config rollup.test.config.js -w", "test": "npm run build && npm run build:test && karma start test/karma.config.js", "test:debug": "npm run build && npm run build:test && DEBUG=true karma start test/karma.config.js" }, diff --git a/test/src/tests.js b/test/src/tests.js index 9bc2b8e..7c52ee2 100644 --- a/test/src/tests.js +++ b/test/src/tests.js @@ -201,15 +201,20 @@ describe('Rokt Forwarder', () => { }; window.mParticle.forwarder.launcher = { hashAttributes: function (attributes) { - window.mParticle.Rokt.hashAttributesOptions = attributes; - window.mParticle.Rokt.hashAttributesCalled = true; - // Mocking the hashAttributes method to show that // the attributes will be transformed by the launcher's // hashAttributes method. - return Promise.resolve({ - 'test-attribute': 'hashed-value', - }); + const hashedAttributes = {}; + for (const key in attributes) { + if (attributes.hasOwnProperty(key)) { + hashedAttributes[key + '-hash'] = + 'hashed-' + attributes[key]; + } + } + window.mParticle.Rokt.hashedAttributes = hashedAttributes; + window.mParticle.Rokt.hashAttributesCalled = true; + + return Promise.resolve(hashedAttributes); }, }; }); @@ -283,6 +288,25 @@ describe('Rokt Forwarder', () => { (result === null).should.equal(true); }); + it('should log an error when kit is initialized but launcher is missing', function () { + var errorLogged = false; + var errorMessage = null; + window.console.error = function (message) { + errorLogged = true; + errorMessage = message; + }; + + window.mParticle.forwarder.isInitialized = true; + window.mParticle.forwarder.launcher = null; + + window.mParticle.forwarder.hashAttributes({ + 'test-attribute': 'test-value', + }); + + errorLogged.should.equal(true); + errorMessage.should.equal('Rokt Kit: Not initialized'); + }); + it('should return hashed attributes from launcher', async () => { await window.mParticle.forwarder.init( { @@ -299,7 +323,7 @@ describe('Rokt Forwarder', () => { }); result.should.deepEqual({ - 'test-attribute': 'hashed-value', + 'test-attribute-hash': 'hashed-test-value', }); }); });