From 9141369ded0d4af9494262e8aa0bb5af630e30b2 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Mon, 16 Jun 2025 12:18:35 -0700 Subject: [PATCH 1/3] feat: Add support to forward subscriptionGroupIds --- src/BrazeKit-dev.js | 68 ++++++++++++++++++++++++++++++++++++--------- test/tests.js | 51 +++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 14 deletions(-) diff --git a/src/BrazeKit-dev.js b/src/BrazeKit-dev.js index 6bed429..6bf6c5f 100644 --- a/src/BrazeKit-dev.js +++ b/src/BrazeKit-dev.js @@ -46,6 +46,7 @@ var constructor = function () { reportingService, hasConsentMappings, parsedConsentMappings, + parsedSubscriptionGroupMapping = {}, mpCustomFlags; self.name = name; @@ -571,6 +572,26 @@ var constructor = function () { return reportEvent; } + function setSubscriptionGroups(key, value) { + const subscriptionGroupId = parsedSubscriptionGroupMapping[key]; + if (!(typeof value === 'boolean')) { + kitLogger("Can't call setSubscriptionGroups on forwarder " + + name + + ', setSubscriptionGroups must set this value to a boolean'); + return; + } else { + if (!value) { + kitLogger('braze.getUser().removeFromSubscriptionGroup', subscriptionGroupId); + + braze.getUser().removeFromSubscriptionGroup(subscriptionGroupId); + } else { + kitLogger('braze.getUser().addToSubscriptionGroup', subscriptionGroupId); + + braze.getUser().addToSubscriptionGroup(subscriptionGroupId); + } + } + } + function removeUserAttribute(key) { if (!(key in DefaultAttributeMethods)) { var sanitizedKey = getSanitizedValueForBraze(key); @@ -589,21 +610,25 @@ var constructor = function () { function setUserAttribute(key, value) { if (!(key in DefaultAttributeMethods)) { - var sanitizedKey = getSanitizedValueForBraze(key); - var sanitizedValue = getSanitizedValueForBraze(value); - if (value != null && sanitizedValue == null) { - return 'Value did not pass validation for ' + key; - } + if (parsedSubscriptionGroupMapping[key]) { + setSubscriptionGroups(key, value) + } else { + var sanitizedKey = getSanitizedValueForBraze(key); + var sanitizedValue = getSanitizedValueForBraze(value); + if (value != null && sanitizedValue == null) { + return 'Value did not pass validation for ' + key; + } - kitLogger( - 'braze.getUser().setCustomUserAttribute', - sanitizedKey, - sanitizedValue - ); + kitLogger( + 'braze.getUser().setCustomUserAttribute', + sanitizedKey, + sanitizedValue + ); - braze - .getUser() - .setCustomUserAttribute(sanitizedKey, sanitizedValue); + braze + .getUser() + .setCustomUserAttribute(sanitizedKey, sanitizedValue); + } } else { return setDefaultAttribute(key, value); } @@ -887,6 +912,10 @@ var constructor = function () { } } + if (forwarderSettings.subscriptionGroupMapping) { + parsedSubscriptionGroupMapping = decodeSubscriptionGroupMappings(forwarderSettings.subscriptionGroupMapping) + } + var cluster = forwarderSettings.cluster || forwarderSettings.dataCenterLocation; @@ -965,6 +994,18 @@ var constructor = function () { } } + function decodeSubscriptionGroupMappings(subscriptionGroupSetting) { + let subscriptionGroupIds = {}; + const decodedSetting = subscriptionGroupSetting.replace(/"/g, '"'); + const parsedSetting = JSON.parse(decodedSetting) + for (let subscriptionGroupMap of parsedSetting) { + const key = subscriptionGroupMap.map + const value = subscriptionGroupMap.value + subscriptionGroupIds[key] = value + } + return subscriptionGroupIds + } + function getSanitizedStringForBraze(value) { if (typeof value === 'string') { if (value.substr(0, 1) === '$') { @@ -1029,6 +1070,7 @@ var constructor = function () { this.onUserIdentified = onUserIdentified; this.removeUserAttribute = removeUserAttribute; this.decodeClusterSetting = decodeClusterSetting; + this.decodeSubscriptionGroupMappings = decodeSubscriptionGroupMappings; /* An example output of this logger if we pass in a purchase event for 1 iPhone with a SKU of iphoneSku that cost $999 with a product attribute of diff --git a/test/tests.js b/test/tests.js index 05480a3..4d151d8 100644 --- a/test/tests.js +++ b/test/tests.js @@ -80,6 +80,7 @@ describe('Braze Forwarder', function() { this.monthOfBirth = null; this.dayOfBirth = null; this.customAttributes = {}; + this.subscriptionGroup = {}; this.customAttributeSet = false; @@ -137,6 +138,14 @@ describe('Braze Forwarder', function() { self.customAttributeSet = true; self.customAttributes[key] = value; }; + + this.addToSubscriptionGroup = function(key) { + self.subscriptionGroup[key] = true; + } + + this.removeFromSubscriptionGroup = function(key) { + self.subscriptionGroup[key] = false; + } }, MockBraze = function() { var self = this; @@ -175,6 +184,7 @@ describe('Braze Forwarder', function() { self.apiKey = apiKey; self.baseUrl = options.baseUrl || null; self.doNotLoadFontAwesome = options.doNotLoadFontAwesome; + self.parsedSubscriptionGroupMapping = {}; return true; }; @@ -1107,6 +1117,45 @@ describe('Braze Forwarder', function() { (window.braze.getUser().yearOfBirth === null).should.equal(true); }); + it('it should set subscription group for mapped attributes when value is boolean', function() { + // sample subscriptionGroupMapping from config + const subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; + + // initialize Braze kit with subscriptionGroupMappings + mParticle.forwarder.init( + { + apiKey: '123456', + subscriptionGroupMapping: subscriptionGroupMapping, + }, + reportService.cb, + true, + null + ); + + // get the decoded mapped subscriptionGroup + const parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); + + // set attribute subscriptionGroupTest1 with boolean value true should call Braze's addToSubscriptionGroup since it's mapped + mParticle.forwarder.setUserAttribute('subscriptionGroupTest1', true); + const mappedSubscriptionGroupId1 = parsedSubscriptionGroupMapping['subscriptionGroupTest1']; + window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId1].should.equal(true); + + // set attribute subscriptionGroupTest2 with boolean value false should call Braze's removeFromSubscriptionGroup since it's mapped + mParticle.forwarder.setUserAttribute('subscriptionGroupTest2', false); + const mappedSubscriptionGroupId2 = parsedSubscriptionGroupMapping['subscriptionGroupTest2']; + window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId2].should.equal(false); + + // should log error if mapped attribute value is not type boolean + mParticle.forwarder.logger = { + verbose: function(msg) { + mParticle.forwarder.msg = msg; + }, + }; + mParticle.forwarder.setUserAttribute('subscriptionGroupTest1', 'test'); + var expectedMessage = `mParticle - Braze Web Kit log:\nCan\'t call setSubscriptionGroups on forwarder Appboy, setSubscriptionGroups must set this value to a boolean:\n`; + mParticle.forwarder.msg.should.equal(expectedMessage) + }); + it('should not set default values if a string is not passed as the attribute', function() { mParticle.forwarder.setUserAttribute('first_name', 'John'); mParticle.forwarder.setUserAttribute('last_name', 'Doe'); @@ -1119,7 +1168,7 @@ describe('Braze Forwarder', function() { it('should set a custom user attribute', function() { mParticle.forwarder.setUserAttribute('test', 'result'); window.braze.getUser().should.have.property('customAttributeSet', true); - window.braze.getUser().customAttributes['test'].should.equal('result');; + window.braze.getUser().customAttributes['test'].should.equal('result'); }); it('should set a custom user attribute of diffferent types', function() { From 4364c63531d89fa4c281c6bc395e2dce9f1db336 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Fri, 20 Jun 2025 16:16:19 -0700 Subject: [PATCH 2/3] code nit and clean up --- src/BrazeKit-dev.js | 81 ++++++++++++++++++++++----------------------- test/tests.js | 77 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 108 insertions(+), 50 deletions(-) diff --git a/src/BrazeKit-dev.js b/src/BrazeKit-dev.js index 6bf6c5f..fd97f77 100644 --- a/src/BrazeKit-dev.js +++ b/src/BrazeKit-dev.js @@ -573,23 +573,18 @@ var constructor = function () { } function setSubscriptionGroups(key, value) { - const subscriptionGroupId = parsedSubscriptionGroupMapping[key]; - if (!(typeof value === 'boolean')) { + var subscriptionGroupId = parsedSubscriptionGroupMapping[key]; + + if (typeof value !== 'boolean') { kitLogger("Can't call setSubscriptionGroups on forwarder " + - name + - ', setSubscriptionGroups must set this value to a boolean'); + name + + ', setSubscriptionGroups must set this value to a boolean'); return; - } else { - if (!value) { - kitLogger('braze.getUser().removeFromSubscriptionGroup', subscriptionGroupId); - - braze.getUser().removeFromSubscriptionGroup(subscriptionGroupId); - } else { - kitLogger('braze.getUser().addToSubscriptionGroup', subscriptionGroupId); - - braze.getUser().addToSubscriptionGroup(subscriptionGroupId); - } } + + var action = value ? 'addToSubscriptionGroup' : 'removeFromSubscriptionGroup'; + kitLogger('braze.getUser().' + action, subscriptionGroupId); + braze.getUser()[action](subscriptionGroupId); } function removeUserAttribute(key) { @@ -609,29 +604,31 @@ var constructor = function () { } function setUserAttribute(key, value) { - if (!(key in DefaultAttributeMethods)) { - if (parsedSubscriptionGroupMapping[key]) { - setSubscriptionGroups(key, value) - } else { - var sanitizedKey = getSanitizedValueForBraze(key); - var sanitizedValue = getSanitizedValueForBraze(value); - if (value != null && sanitizedValue == null) { - return 'Value did not pass validation for ' + key; - } + if (key in DefaultAttributeMethods) { + return setDefaultAttribute(key, value); + } - kitLogger( - 'braze.getUser().setCustomUserAttribute', - sanitizedKey, - sanitizedValue - ); + if (parsedSubscriptionGroupMapping[key]) { + setSubscriptionGroups(key, value); + return; + } - braze - .getUser() - .setCustomUserAttribute(sanitizedKey, sanitizedValue); - } - } else { - return setDefaultAttribute(key, value); + var sanitizedKey = getSanitizedValueForBraze(key); + var sanitizedValue = getSanitizedValueForBraze(value); + + if (value != null && sanitizedValue == null) { + return 'Value did not pass validation for ' + key; } + + kitLogger( + 'braze.getUser().setCustomUserAttribute', + sanitizedKey, + sanitizedValue + ); + + braze + .getUser() + .setCustomUserAttribute(sanitizedKey, sanitizedValue); } function setUserIdentity(id, type) { @@ -913,7 +910,7 @@ var constructor = function () { } if (forwarderSettings.subscriptionGroupMapping) { - parsedSubscriptionGroupMapping = decodeSubscriptionGroupMappings(forwarderSettings.subscriptionGroupMapping) + parsedSubscriptionGroupMapping = decodeSubscriptionGroupMappings(forwarderSettings.subscriptionGroupMapping); } var cluster = @@ -995,15 +992,15 @@ var constructor = function () { } function decodeSubscriptionGroupMappings(subscriptionGroupSetting) { - let subscriptionGroupIds = {}; - const decodedSetting = subscriptionGroupSetting.replace(/"/g, '"'); - const parsedSetting = JSON.parse(decodedSetting) + var subscriptionGroupIds = {}; + var decodedSetting = subscriptionGroupSetting.replace(/"/g, '"'); + var parsedSetting = JSON.parse(decodedSetting); for (let subscriptionGroupMap of parsedSetting) { - const key = subscriptionGroupMap.map - const value = subscriptionGroupMap.value - subscriptionGroupIds[key] = value + var key = subscriptionGroupMap.map; + var value = subscriptionGroupMap.value; + subscriptionGroupIds[key] = value; } - return subscriptionGroupIds + return subscriptionGroupIds; } function getSanitizedStringForBraze(value) { diff --git a/test/tests.js b/test/tests.js index 4d151d8..23e5868 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1117,9 +1117,9 @@ describe('Braze Forwarder', function() { (window.braze.getUser().yearOfBirth === null).should.equal(true); }); - it('it should set subscription group for mapped attributes when value is boolean', function() { + it('decodeSubscriptionGroupMappings should return parsed subscriptionGroupIds map when proper setting is given', function () { // sample subscriptionGroupMapping from config - const subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; + var subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; // initialize Braze kit with subscriptionGroupMappings mParticle.forwarder.init( @@ -1133,17 +1133,77 @@ describe('Braze Forwarder', function() { ); // get the decoded mapped subscriptionGroup - const parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); + var parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); + var expectedResult = { + 'subscriptionGroupTest1': '00000000-0000-0000-0000-000000000000', + 'subscriptionGroupTest2': '00000000-0000-0000-0000-000000000001', + }; + + parsedSubscriptionGroupMapping.should.deepEqual(expectedResult); + }); + + it('should set subscription group for mapped attributes when value is true with type boolean', function() { + // sample subscriptionGroupMapping from config + var subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; + + // initialize Braze kit with subscriptionGroupMappings + mParticle.forwarder.init( + { + apiKey: '123456', + subscriptionGroupMapping: subscriptionGroupMapping, + }, + reportService.cb, + true, + null + ); + + // get the decoded mapped subscriptionGroup + var parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); // set attribute subscriptionGroupTest1 with boolean value true should call Braze's addToSubscriptionGroup since it's mapped mParticle.forwarder.setUserAttribute('subscriptionGroupTest1', true); - const mappedSubscriptionGroupId1 = parsedSubscriptionGroupMapping['subscriptionGroupTest1']; - window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId1].should.equal(true); + var mappedSubscriptionGroupId = parsedSubscriptionGroupMapping['subscriptionGroupTest1']; + window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId].should.equal(true); + }); + + it('should set subscription group for mapped attributes when value is false with type boolean', function() { + // sample subscriptionGroupMapping from config + var subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; + + // initialize Braze kit with subscriptionGroupMappings + mParticle.forwarder.init( + { + apiKey: '123456', + subscriptionGroupMapping: subscriptionGroupMapping, + }, + reportService.cb, + true, + null + ); + + // get the decoded mapped subscriptionGroup + var parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); // set attribute subscriptionGroupTest2 with boolean value false should call Braze's removeFromSubscriptionGroup since it's mapped mParticle.forwarder.setUserAttribute('subscriptionGroupTest2', false); - const mappedSubscriptionGroupId2 = parsedSubscriptionGroupMapping['subscriptionGroupTest2']; - window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId2].should.equal(false); + var mappedSubscriptionGroupId = parsedSubscriptionGroupMapping['subscriptionGroupTest2']; + window.braze.getUser().subscriptionGroup[mappedSubscriptionGroupId].should.equal(false); + }); + + it('should not set subscription group for mapped attributes when value type is not boolean', function() { + // sample subscriptionGroupMapping from config + var subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; + + // initialize Braze kit with subscriptionGroupMappings + mParticle.forwarder.init( + { + apiKey: '123456', + subscriptionGroupMapping: subscriptionGroupMapping, + }, + reportService.cb, + true, + null + ); // should log error if mapped attribute value is not type boolean mParticle.forwarder.logger = { @@ -1151,7 +1211,8 @@ describe('Braze Forwarder', function() { mParticle.forwarder.msg = msg; }, }; - mParticle.forwarder.setUserAttribute('subscriptionGroupTest1', 'test'); + + mParticle.forwarder.setUserAttribute('subscriptionGroupTest1', 'testStringValue'); var expectedMessage = `mParticle - Braze Web Kit log:\nCan\'t call setSubscriptionGroups on forwarder Appboy, setSubscriptionGroups must set this value to a boolean:\n`; mParticle.forwarder.msg.should.equal(expectedMessage) }); From eb7b3f38bd844894537894e88dcaa72328360d40 Mon Sep 17 00:00:00 2001 From: Mohammed Mustafa Date: Tue, 24 Jun 2025 10:22:23 -0700 Subject: [PATCH 3/3] remove forwarder init from unit test --- test/tests.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/test/tests.js b/test/tests.js index 23e5868..861d1c1 100644 --- a/test/tests.js +++ b/test/tests.js @@ -1121,17 +1121,6 @@ describe('Braze Forwarder', function() { // sample subscriptionGroupMapping from config var subscriptionGroupMapping = '[{"jsmap":null,"map":"subscriptionGroupTest1","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000000"},{"jsmap":null,"map":"subscriptionGroupTest2","maptype":"UserAttributeClass.Name","value":"00000000-0000-0000-0000-000000000001"}]'; - // initialize Braze kit with subscriptionGroupMappings - mParticle.forwarder.init( - { - apiKey: '123456', - subscriptionGroupMapping: subscriptionGroupMapping, - }, - reportService.cb, - true, - null - ); - // get the decoded mapped subscriptionGroup var parsedSubscriptionGroupMapping = mParticle.forwarder.decodeSubscriptionGroupMappings(subscriptionGroupMapping); var expectedResult = {