diff --git a/packages/analytics-core/src/analytics.js b/packages/analytics-core/src/analytics.js index 056e0d8e..7b923572 100644 --- a/packages/analytics-core/src/analytics.js +++ b/packages/analytics-core/src/analytics.js @@ -31,6 +31,11 @@ export default class AvAnalytics { this.isPageTracking = false; this.hasInit = false; + this.hasLogPlugin = this.plugins.some((plugin) => { + const apiName = plugin.AvLogMessages?.defaultConfig?.name; + return apiName === 'appl/analytics/log' || apiName === 'spc/analytics/log'; + }); + if (autoTrack) { this.startAutoTrack(); } @@ -102,9 +107,7 @@ export default class AvAnalytics { isModifiedEvent(event) || (event.type === 'click' && !isLeftClickEvent(event)) || !isValidEventTypeOnTarget(event); getAnalyticAttrs = (elem) => { - if (!elem.attributes) { - return {}; - } + if (!elem.attributes) return {}; const attrs = elem.attributes; const analyticAttrs = {}; @@ -117,6 +120,19 @@ export default class AvAnalytics { analyticAttrs[camelName] = elem.getAttribute(name); } } + + if (this.hasLogPlugin) { + const overridesKeys = Object.keys(analyticAttrs).filter(key => key.startsWith('overrides')); + if (overridesKeys.length > 0) { + analyticAttrs.overrides = {}; + for (const key of overridesKeys) { + const nestedKey = key.slice(9); // Remove 'overrides' + const finalKey = nestedKey.charAt(0).toLowerCase() + nestedKey.slice(1); + analyticAttrs.overrides[finalKey] = analyticAttrs[key]; + delete analyticAttrs[key]; + } + } + } } return analyticAttrs; }; diff --git a/packages/analytics-core/src/tests/analytics.test.ts b/packages/analytics-core/src/tests/analytics.test.ts index b814636d..e7cef760 100644 --- a/packages/analytics-core/src/tests/analytics.test.ts +++ b/packages/analytics-core/src/tests/analytics.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable unicorn/prefer-dom-node-dataset */ import { AvAnalytics } from '..'; type MockPlugin = { @@ -184,4 +185,135 @@ describe('AvAnalytics', () => { expect(plugins[1].trackPageView).toHaveBeenCalledWith(mockUrl); }); }); + + describe('getAnalyticAttrs with nested overrides', () => { + let logPlugin: MockPlugin & { AvLogMessages?: { defaultConfig: { name: string } } }; + let mockApiV2: { defaultConfig: { name: string } }; + let mockApiV3: { defaultConfig: { name: string } }; + + beforeEach(() => { + logPlugin = makePlugin(); + mockApiV2 = { defaultConfig: { name: 'spc/analytics/log' } }; + mockApiV3 = { defaultConfig: { name: 'appl/analytics/log' } }; + }); + + test('should extract nested overrides when AvLogMessagesApiV3 plugin is registered', () => { + logPlugin.AvLogMessages = mockApiV3; + mockAvAnalytics = new AvAnalytics([logPlugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-category', 'error'); + elem.setAttribute('data-analytics-overrides-endpoint', '/custom/log'); + elem.setAttribute('data-analytics-overrides-timeout', '5000'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + category: 'error', + overrides: { + endpoint: '/custom/log', + timeout: '5000', + }, + }); + }); + + test('should extract nested overrides when AvLogMessagesApiV2 plugin is registered', () => { + logPlugin.AvLogMessages = mockApiV2; + mockAvAnalytics = new AvAnalytics([logPlugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-overrides-retry', 'true'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + overrides: { + retry: 'true', + }, + }); + }); + + test('should NOT extract nested overrides when no log plugin is registered', () => { + const googlePlugin = makePlugin(); + mockAvAnalytics = new AvAnalytics([googlePlugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-category', 'button'); + elem.setAttribute('data-analytics-overrides-endpoint', '/custom/log'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + category: 'button', + overridesEndpoint: '/custom/log', + }); + }); + + test('should NOT extract nested overrides when plugin has no AvLogMessages', () => { + const plugin = makePlugin(); + mockAvAnalytics = new AvAnalytics([plugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-category', 'button'); + elem.setAttribute('data-analytics-overrides-endpoint', '/custom/log'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + category: 'button', + overridesEndpoint: '/custom/log', + }); + }); + + test('should handle log plugin without overrides attributes', () => { + logPlugin.AvLogMessages = mockApiV3; + mockAvAnalytics = new AvAnalytics([logPlugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-category', 'error'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + category: 'error', + }); + }); + + test('should use custom attributePrefix with log plugin', () => { + logPlugin.AvLogMessages = mockApiV3; + mockAvAnalytics = new AvAnalytics([logPlugin], Promise, false, false, { + attributePrefix: 'custom-attr', + }); + + const elem = document.createElement('button'); + elem.setAttribute('custom-attr-overrides-endpoint', '/test'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + overrides: { + endpoint: '/test', + }, + }); + }); + + test('should extract nested overrides when multiple plugins including log plugin', () => { + logPlugin.AvLogMessages = mockApiV3; + const googlePlugin = makePlugin(); + mockAvAnalytics = new AvAnalytics([googlePlugin, logPlugin], Promise, false, false); + + const elem = document.createElement('button'); + elem.setAttribute('data-analytics-category', 'error'); + elem.setAttribute('data-analytics-overrides-endpoint', '/custom/log'); + + const attrs = mockAvAnalytics.getAnalyticAttrs(elem); + + expect(attrs).toEqual({ + category: 'error', + overrides: { + endpoint: '/custom/log', + }, + }); + }); + }); }); diff --git a/packages/api-axios/src/resources/dma-cloud.js b/packages/api-axios/src/resources/dma-cloud.js index 66024fd2..3ce1ac14 100644 --- a/packages/api-axios/src/resources/dma-cloud.js +++ b/packages/api-axios/src/resources/dma-cloud.js @@ -12,7 +12,9 @@ export default class AvLogMessagesApiV3 extends AvMicroserviceApi { send(level, entries) { delete entries.level; - const payload = { level, entries }; + const { overrides } = entries; + delete entries.overrides; + const payload = { level, entries, overrides }; const flattened = flattenObject(payload); flattened.X_Client_ID = this.clientId; diff --git a/packages/api-axios/src/resources/dma.js b/packages/api-axios/src/resources/dma.js index 61539e98..6dc77ad4 100644 --- a/packages/api-axios/src/resources/dma.js +++ b/packages/api-axios/src/resources/dma.js @@ -11,7 +11,9 @@ export default class AvLogMessagesApiV2 extends AvMicroserviceApi { send(level, entries) { delete entries.level; - const payload = { level, entries }; + const { overrides } = entries; + delete entries.overrides; + const payload = { level, entries, overrides }; const flattened = flattenObject(payload); flattened.X_Client_ID = this.clientId; diff --git a/packages/api-axios/src/resources/tests/dma-cloud.test.js b/packages/api-axios/src/resources/tests/dma-cloud.test.js index 0a1df0e3..d762b5fb 100644 --- a/packages/api-axios/src/resources/tests/dma-cloud.test.js +++ b/packages/api-axios/src/resources/tests/dma-cloud.test.js @@ -17,4 +17,9 @@ describe('AvLogMessagesApiV3', () => { const fields = api.send('info', { testField1: 'test1', testField2: 'test2'}); expect(fields).toContain('level=info&entries.testField1=test1&entries.testField2=test2'); }); + + test('send should generate optional overrides fields correctly', () => { + const fields = api.send('info', { testField1: 'test1', testField2: 'test2', overrides: { akaName: 'override1', transactionId: 'override2' } }); + expect(fields).toContain('level=info&entries.testField1=test1&entries.testField2=test2&overrides.akaName=override1&overrides.transactionId=override2'); + }); }); diff --git a/packages/api-axios/src/resources/tests/dma.test.js b/packages/api-axios/src/resources/tests/dma.test.js index 4c8214aa..2781e867 100644 --- a/packages/api-axios/src/resources/tests/dma.test.js +++ b/packages/api-axios/src/resources/tests/dma.test.js @@ -17,4 +17,9 @@ describe('AvLogMessagesApiV2', () => { const fields = api.send('info', { testField1: 'test1', testField2: 'test2'}); expect(fields).toContain('level=info&entries.testField1=test1&entries.testField2=test2'); }); + + test('send should generate optional overrides fields correctly', () => { + const fields = api.send('info', { testField1: 'test1', testField2: 'test2', overrides: { akaName: 'override1', transactionId: 'override2' } }); + expect(fields).toContain('level=info&entries.testField1=test1&entries.testField2=test2&overrides.akaName=override1&overrides.transactionId=override2'); + }); }); diff --git a/yarn.lock b/yarn.lock index 1b24c92b..f01b4167 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20510,15 +20510,15 @@ __metadata: linkType: hard "tar@npm:^7.5.4": - version: 7.5.9 - resolution: "tar@npm:7.5.9" + version: 7.5.11 + resolution: "tar@npm:7.5.11" dependencies: "@isaacs/fs-minipass": "npm:^4.0.0" chownr: "npm:^3.0.0" minipass: "npm:^7.1.2" minizlib: "npm:^3.1.0" yallist: "npm:^5.0.0" - checksum: 10/1213cdde9c22d6acf8809ba5d2a025212ce3517bc99c4a4c6981b7dc0489bf3b164db9c826c9517680889194c9ba57448c8ff0da35eca9a60bb7689bf0b3897d + checksum: 10/fb2e77ee858a73936c68e066f4a602d428d6f812e6da0cc1e14a41f99498e4f7fd3535e355fa15157240a5538aa416026cfa6306bb0d1d1c1abf314b1f878e9a languageName: node linkType: hard