From 721530493d559ba89d2bfe1156a916a8885bb34c Mon Sep 17 00:00:00 2001 From: Utsab Chowdhury Date: Tue, 21 Jan 2025 17:25:09 +0530 Subject: [PATCH 1/3] fix: braze anonymousId tracking with alias details (#1994) --- .../integrations/Braze/browser.test.js | 170 +++++++++++++----- .../src/integrations/Braze/browser.js | 62 +++++-- .../src/integrations/Braze/utils.js | 4 +- 3 files changed, 171 insertions(+), 65 deletions(-) diff --git a/packages/analytics-js-integrations/__tests__/integrations/Braze/browser.test.js b/packages/analytics-js-integrations/__tests__/integrations/Braze/browser.test.js index caacf1273f..a247a77b1a 100644 --- a/packages/analytics-js-integrations/__tests__/integrations/Braze/browser.test.js +++ b/packages/analytics-js-integrations/__tests__/integrations/Braze/browser.test.js @@ -28,6 +28,7 @@ const mockBrazeSDK = () => { throw new Error('Braze SDK Error: changeUser requires a non-empty userId. (v4.2.1)'); } }), + addAlias: jest.fn(), openSession: jest.fn(), getUser: jest.fn().mockReturnThis(), setCountry: jest.fn(), @@ -141,15 +142,99 @@ describe('isLoaded', () => { }); }); -describe('isLoaded', () => { - it('should get false value with isReady', () => { - const config = {}; - const analytics = {}; - const destinationInfo = {}; +describe('setUserAlias', () => { + let braze; + let config; + let analytics; - const braze = new Braze(config, analytics, destinationInfo); - const isLoaded = braze.isReady(); - expect(isLoaded).toBe(false); + beforeEach(() => { + config = { + appKey: 'APP_KEY', + }; + analytics = { + getAnonymousId: jest.fn(), + }; + braze = new Braze(config, analytics, {}); + braze.init(); + mockBrazeSDK(); + }); + + it('should successfully set user alias', () => { + analytics.getAnonymousId.mockReturnValue('anon123'); + window.braze.getUser().addAlias.mockReturnValue(true); + + const result = braze.setUserAlias(); + expect(result).toBe(true); + expect(window.braze.getUser().addAlias).toHaveBeenCalledWith('anon123', 'rudder_id'); + }); + + it('should fail when anonymous ID is missing', () => { + analytics.getAnonymousId.mockReturnValue(null); + + const result = braze.setUserAlias(); + expect(result).toBe(false); + }); + + it('should fail when user object is not available', () => { + analytics.getAnonymousId.mockReturnValue('anon123'); + window.braze.getUser = jest.fn().mockReturnValue(null); + + const result = braze.setUserAlias(); + expect(result).toBe(false); + }); + + it('should fail when addAlias returns false', () => { + analytics.getAnonymousId.mockReturnValue('anon123'); + window.braze.getUser().addAlias.mockReturnValue(false); + + const result = braze.setUserAlias(); + expect(result).toBe(false); + }); + + it('should handle errors gracefully', () => { + analytics.getAnonymousId.mockImplementation(() => { + throw new Error('Test error'); + }); + + const result = braze.setUserAlias(); + expect(result).toBe(false); + }); +}); + + +describe('isReady', () => { + let braze; + let config; + let analytics; + + beforeEach(() => { + config = { appKey: 'APP_KEY' }; + analytics = { getAnonymousId: jest.fn() }; + braze = new Braze(config, analytics, {}); + }); + + it('should return false when not loaded', () => { + jest.spyOn(braze, 'isLoaded').mockReturnValue(false); + + const result = braze.isReady(); + expect(result).toBe(false); + expect(braze.isLoaded).toHaveBeenCalled(); + }); + + it('should return true when loaded and alias set successfully', () => { + jest.spyOn(braze, 'isLoaded').mockReturnValue(true); + jest.spyOn(braze, 'setUserAlias').mockReturnValue(true); + + const result = braze.isReady(); + expect(result).toBe(true); + }); + + it('should return false when loaded but alias setting fails', () => { + jest.spyOn(braze, 'isLoaded').mockReturnValue(true); + jest.spyOn(braze, 'setUserAlias').mockReturnValue(false); + + const result = braze.isReady(); + expect(result).toBe(false); }); }); @@ -561,11 +646,10 @@ describe('track', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Product Reviewed', { rating: 3, review_body: 'Good product.', @@ -622,11 +706,10 @@ describe('track', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logPurchase).toHaveBeenCalledTimes(1); expect(window.braze.logPurchase).toHaveBeenCalledWith('123454387', 15.99, 'USD', 1, {}); }); @@ -680,11 +763,10 @@ describe('track', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logPurchase).toHaveBeenCalledTimes(1); expect(window.braze.logPurchase).toHaveBeenCalledWith('123454387', 15.99, 'USD', 1, { rating: 5, }); @@ -734,11 +816,15 @@ describe('track', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('anon123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); + expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Product Reviewed', { + rating: 3, + review_body: 'Good product.', + review_id: '12345', + }); }); it('should call the necessary Braze methods for order completed event wit hreserved properties', () => { @@ -791,11 +877,10 @@ describe('track', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Product Reviewed', { products: [{ name: 'Game', price: 15.99, product_id: '123454387', quantity: 1 }], }); @@ -827,19 +912,18 @@ describe('page', () => { name: 'Home', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; - jest.spyOn(window.braze, 'changeUser'); braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Home', { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }); }); @@ -866,19 +950,18 @@ describe('page', () => { type: 'page', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; - jest.spyOn(window.braze, 'changeUser'); braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toHaveBeenCalledWith('user123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Page View', { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }); }); @@ -905,19 +988,18 @@ describe('page', () => { type: 'page', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; - jest.spyOn(window.braze, 'changeUser'); braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(0); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Page View', { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }); }); @@ -944,20 +1026,18 @@ describe('page', () => { type: 'page', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; - jest.spyOn(window.braze, 'changeUser'); braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(1); - expect(window.braze.changeUser).toHaveBeenCalledWith('anon123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Page View', { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }); }); @@ -984,7 +1064,7 @@ describe('page', () => { type: 'page', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', event_name: 'ABC', referer: 'index', currency: 'usd', @@ -992,15 +1072,13 @@ describe('page', () => { }, }; - jest.spyOn(window.braze, 'changeUser'); braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(1); - expect(window.braze.changeUser).toHaveBeenCalledWith('anon123'); + expect(window.braze.logCustomEvent).toHaveBeenCalledTimes(1); expect(window.braze.logCustomEvent).toHaveBeenCalledWith('Page View', { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', referer: 'index', }); }); @@ -1033,7 +1111,7 @@ describe('hybrid mode', () => { name: 'Home', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; @@ -1042,7 +1120,7 @@ describe('hybrid mode', () => { braze.page(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(0); + expect(window.braze.changeUser).toHaveBeenCalledTimes(0); }); it('should not call the necessary Braze methods for track call', () => { @@ -1071,7 +1149,7 @@ describe('hybrid mode', () => { name: 'Home', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; @@ -1080,7 +1158,7 @@ describe('hybrid mode', () => { braze.track(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(0); + expect(window.braze.changeUser).toHaveBeenCalledTimes(0); }); it('should call the necessary Braze methods for identify call', () => { @@ -1109,7 +1187,7 @@ describe('hybrid mode', () => { name: 'Home', properties: { title: 'Home | RudderStack', - url: 'http://www.rudderstack.com', + url: 'https://www.rudderstack.com', }, }, }; @@ -1118,6 +1196,6 @@ describe('hybrid mode', () => { braze.identify(rudderElement); // Expect the necessary Braze methods to be called with the correct values - expect(window.braze.changeUser).toBeCalledTimes(1); + expect(window.braze.changeUser).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/analytics-js-integrations/src/integrations/Braze/browser.js b/packages/analytics-js-integrations/src/integrations/Braze/browser.js index 0bd78dbfe9..480dc05fff 100644 --- a/packages/analytics-js-integrations/src/integrations/Braze/browser.js +++ b/packages/analytics-js-integrations/src/integrations/Braze/browser.js @@ -31,6 +31,10 @@ class Braze { if (!config.appKey) this.appKey = ''; this.endPoint = ''; this.isHybridModeEnabled = config.connectionMode === 'hybrid'; + this.isReadyStatus = { + hasLoggedErrorForAlias: false, + }; + if (config.dataCenter) { // ref: https://www.braze.com/docs/user_guide/administrative/access_braze/braze_instances const dataCenterArr = config.dataCenter.trim().split('-'); @@ -50,6 +54,13 @@ class Braze { } = destinationInfo ?? {}); } + logAliasError(message) { + if (!this.isReadyStatus.hasLoggedErrorForAlias) { + logger.error(message); + this.isReadyStatus.hasLoggedErrorForAlias = true; + } + } + init() { loadNativeSdk(); window.braze.initialize(this.appKey, { @@ -74,8 +85,38 @@ class Braze { return window.brazeQueue === null; } + setUserAlias() { + try { + const anonymousId = this.analytics.getAnonymousId(); + if (!anonymousId) { + this.logAliasError('Anonymous ID is not available'); + return false; + } + + const user = window.braze.getUser(); + if (!user) { + this.logAliasError('Braze user object is not available'); + return false; + } + + const aliasSet = user.addAlias(anonymousId, 'rudder_id'); + if (!aliasSet) { + this.logAliasError('Failed to set alias for braze'); + return false; + } + + return true; + } catch (error) { + this.logAliasError(`Error setting alias: ${stringifyWithoutCircularV1(error, true)}`); + return false; + } + } + isReady() { - return this.isLoaded(); + if (!this.isLoaded()) { + return false; + } + return this.setUserAlias(); } /** @@ -255,21 +296,17 @@ class Braze { if (this.isHybridModeEnabled) { return; } - const { userId } = rudderElement.message; const eventName = rudderElement.message.event; let { properties } = rudderElement.message; - const { anonymousId } = rudderElement.message; + + const { userId } = rudderElement.message; let canSendCustomEvent = false; - if (userId) { - window.braze.changeUser(userId); - canSendCustomEvent = true; - } else if (this.trackAnonymousUser) { - window.braze.changeUser(anonymousId); + if (userId || this.trackAnonymousUser) { canSendCustomEvent = true; } if (eventName && canSendCustomEvent) { if (eventName.toLowerCase() === 'order completed') { - handlePurchase(properties, userId); + handlePurchase(properties); } else { properties = handleReservedProperties(properties); window.braze.logCustomEvent(eventName, properties); @@ -281,15 +318,8 @@ class Braze { if (this.isHybridModeEnabled) { return; } - const { userId } = rudderElement.message; const eventName = rudderElement.message.name; let { properties } = rudderElement.message; - const { anonymousId } = rudderElement.message; - if (userId) { - window.braze.changeUser(userId); - } else if (this.trackAnonymousUser) { - window.braze.changeUser(anonymousId); - } properties = handleReservedProperties(properties); if (eventName) { window.braze.logCustomEvent(eventName, properties); diff --git a/packages/analytics-js-integrations/src/integrations/Braze/utils.js b/packages/analytics-js-integrations/src/integrations/Braze/utils.js index b2aac815de..b0dc2f00b7 100644 --- a/packages/analytics-js-integrations/src/integrations/Braze/utils.js +++ b/packages/analytics-js-integrations/src/integrations/Braze/utils.js @@ -26,12 +26,10 @@ const handleReservedProperties = props => { return props; }; -const handlePurchase = (properties, userId) => { +const handlePurchase = (properties) => { const { products, currency } = properties; const currencyCode = currency; - window.braze.changeUser(userId); - // del used properties del(properties, 'products'); del(properties, 'currency'); From 43faa17e60cc27218e41a99a2ecf6ae52511fd3d Mon Sep 17 00:00:00 2001 From: GitHub actions Date: Wed, 22 Jan 2025 05:18:23 +0000 Subject: [PATCH 2/3] chore(@rudderstack/analytics-js-integrations): release version 3.12.1 --- packages/analytics-js-integrations/CHANGELOG.md | 7 +++++++ packages/analytics-js-integrations/package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/analytics-js-integrations/CHANGELOG.md b/packages/analytics-js-integrations/CHANGELOG.md index 33ae1513e4..90a312af02 100644 --- a/packages/analytics-js-integrations/CHANGELOG.md +++ b/packages/analytics-js-integrations/CHANGELOG.md @@ -2,6 +2,13 @@ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver). +## [3.12.1](https://github.com/rudderlabs/rudder-sdk-js/compare/@rudderstack/analytics-js-integrations@3.12.0...@rudderstack/analytics-js-integrations@3.12.1) (2025-01-22) + + +### Bug Fixes + +* braze anonymousId tracking with alias details ([#1994](https://github.com/rudderlabs/rudder-sdk-js/issues/1994)) ([7215304](https://github.com/rudderlabs/rudder-sdk-js/commit/721530493d559ba89d2bfe1156a916a8885bb34c)) + ## [3.12.0](https://github.com/rudderlabs/rudder-sdk-js/compare/@rudderstack/analytics-js-integrations@3.11.15...@rudderstack/analytics-js-integrations@3.12.0) (2025-01-17) diff --git a/packages/analytics-js-integrations/package.json b/packages/analytics-js-integrations/package.json index ea78b17692..066aa63653 100644 --- a/packages/analytics-js-integrations/package.json +++ b/packages/analytics-js-integrations/package.json @@ -1,6 +1,6 @@ { "name": "@rudderstack/analytics-js-integrations", - "version": "3.12.0", + "version": "3.12.1", "private": true, "description": "RudderStack JavaScript SDK device mode integrations", "main": "dist/npm/modern/cjs/index.js", From 6ce233eb69c00db9f7ed64086ae26664bbc5acb8 Mon Sep 17 00:00:00 2001 From: GitHub actions Date: Wed, 22 Jan 2025 05:18:34 +0000 Subject: [PATCH 3/3] chore(monorepo): sync versions and generate release logs --- package-lock.json | 6 +++--- package.json | 2 +- packages/analytics-js-integrations/CHANGELOG_LATEST.md | 9 ++------- packages/analytics-js-integrations/project.json | 6 +++--- sonar-project.properties | 2 +- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index f6a71e792c..01a1cff3e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rudderstack/analytics-js-monorepo", - "version": "3.72.0", + "version": "3.73.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rudderstack/analytics-js-monorepo", - "version": "3.72.0", + "version": "3.73.0", "hasInstallScript": true, "license": "Elastic-2.0", "workspaces": [ @@ -25524,7 +25524,7 @@ }, "packages/analytics-js-integrations": { "name": "@rudderstack/analytics-js-integrations", - "version": "3.12.0", + "version": "3.12.1", "license": "Elastic-2.0", "dependencies": { "@lukeed/uuid": "2.0.1", diff --git a/package.json b/package.json index bd54aeb779..d05d569e75 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rudderstack/analytics-js-monorepo", - "version": "3.72.0", + "version": "3.73.0", "private": true, "description": "Monorepo for RudderStack Analytics JS SDK", "workspaces": [ diff --git a/packages/analytics-js-integrations/CHANGELOG_LATEST.md b/packages/analytics-js-integrations/CHANGELOG_LATEST.md index 8814c88d30..79d2c98349 100644 --- a/packages/analytics-js-integrations/CHANGELOG_LATEST.md +++ b/packages/analytics-js-integrations/CHANGELOG_LATEST.md @@ -1,12 +1,7 @@ -## [3.12.0](https://github.com/rudderlabs/rudder-sdk-js/compare/@rudderstack/analytics-js-integrations@3.11.15...@rudderstack/analytics-js-integrations@3.12.0) (2025-01-17) - - -### Features - -* braze sdk metadata ([#1949](https://github.com/rudderlabs/rudder-sdk-js/issues/1949)) ([81fe85a](https://github.com/rudderlabs/rudder-sdk-js/commit/81fe85a0eeb9405c0ec820dfe73a0ef5c0cc014b)) +## [3.12.1](https://github.com/rudderlabs/rudder-sdk-js/compare/@rudderstack/analytics-js-integrations@3.12.0...@rudderstack/analytics-js-integrations@3.12.1) (2025-01-22) ### Bug Fixes -* resolved minor bugsnag issue in googleAds ([#1993](https://github.com/rudderlabs/rudder-sdk-js/issues/1993)) ([0dfb893](https://github.com/rudderlabs/rudder-sdk-js/commit/0dfb893a78894d6d04b8fc0b4a0a3c8595f69e01)) +* braze anonymousId tracking with alias details ([#1994](https://github.com/rudderlabs/rudder-sdk-js/issues/1994)) ([7215304](https://github.com/rudderlabs/rudder-sdk-js/commit/721530493d559ba89d2bfe1156a916a8885bb34c)) diff --git a/packages/analytics-js-integrations/project.json b/packages/analytics-js-integrations/project.json index 6d737e26b5..afab101e6a 100644 --- a/packages/analytics-js-integrations/project.json +++ b/packages/analytics-js-integrations/project.json @@ -51,9 +51,9 @@ "github": { "executor": "@jscutlery/semver:github", "options": { - "tag": "@rudderstack/analytics-js-integrations@3.12.0", - "title": "@rudderstack/analytics-js-integrations@3.12.0", - "discussion-category": "@rudderstack/analytics-js-integrations@3.12.0", + "tag": "@rudderstack/analytics-js-integrations@3.12.1", + "title": "@rudderstack/analytics-js-integrations@3.12.1", + "discussion-category": "@rudderstack/analytics-js-integrations@3.12.1", "notesFile": "./packages/analytics-js-integrations/CHANGELOG_LATEST.md" } } diff --git a/sonar-project.properties b/sonar-project.properties index 798a687ae6..d6f829fda7 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -6,7 +6,7 @@ sonar.qualitygate.wait=false sonar.projectKey=rudderlabs_rudder-sdk-js sonar.organization=rudderlabs sonar.projectName=rudder-sdk-js -sonar.projectVersion=3.72.0 +sonar.projectVersion=3.73.0 # Meta-data for the project sonar.links.scm=https://github.com/rudderlabs/rudder-sdk-js