Skip to content

Commit 4c3c927

Browse files
authored
IntentIq ID & Analytics Modules: GAM reporting (#12785)
* AGT-399: GAM reporting integration * AGT-399: Description for new parameters * AGT-399: Some fixes after review, gamParameterName test * AGT-399: Change version * AGT-399: Fix linter
1 parent fd9e031 commit 4c3c927

File tree

5 files changed

+109
-5
lines changed

5 files changed

+109
-5
lines changed

libraries/intentIqConstants/intentIqConstants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ export const OPT_OUT = 'O';
77
export const BLACK_LIST = 'L';
88
export const CLIENT_HINTS_KEY = '_iiq_ch';
99
export const EMPTY = 'EMPTY'
10-
export const VERSION = 0.25
10+
export const VERSION = 0.26

libraries/intentIqUtils/detectBrowserUtils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function detectBrowserFromUserAgent(userAgent) {
6464

6565
/**
6666
* Detects the browser from the NavigatorUAData object
67-
* @param {NavigatorUAData} userAgentData - The user agent data object from the browser
67+
* @param {Object} userAgentData - The user agent data object from the browser
6868
* @return {string} The name of the detected browser or 'unknown' if unable to detect
6969
*/
7070
export function detectBrowserFromUserAgentData(userAgentData) {

modules/intentIqIdSystem.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @requires module:modules/userId
66
*/
77

8-
import {logError, logInfo} from '../src/utils.js';
8+
import {logError, logInfo, isPlainObject} from '../src/utils.js';
99
import {ajax} from '../src/ajax.js';
1010
import {submodule} from '../src/hook.js'
1111
import {getStorageManager} from '../src/storageManager.js';
@@ -159,6 +159,23 @@ function tryParse(data) {
159159
}
160160
}
161161

162+
/**
163+
* Configures and updates A/B testing group in Google Ad Manager (GAM).
164+
*
165+
* @param {object} gamObjectReference - Reference to the GAM object, expected to have a `cmd` queue and `pubads()` API.
166+
* @param {string} gamParameterName - The name of the GAM targeting parameter where the group value will be stored.
167+
* @param {string} userGroup - The A/B testing group assigned to the user (e.g., 'A', 'B', or a custom value).
168+
*/
169+
export function setGamReporting(gamObjectReference, gamParameterName, userGroup) {
170+
if (isPlainObject(gamObjectReference) && Array.isArray(gamObjectReference.cmd)) {
171+
gamObjectReference.cmd.push(() => {
172+
gamObjectReference
173+
.pubads()
174+
.setTargeting(gamParameterName, userGroup || NOT_YET_DEFINED);
175+
});
176+
}
177+
}
178+
162179
/**
163180
* Processes raw client hints data into a structured format.
164181
* @param {object} clientHints - Raw client hints data
@@ -218,11 +235,14 @@ export const intentIqIdSubmodule = {
218235
let decryptedData, callbackTimeoutID;
219236
let callbackFired = false;
220237
let runtimeEids = { eids: [] };
238+
let gamObjectReference = isPlainObject(configParams.gamObjectReference) ? configParams.gamObjectReference : undefined;
239+
let gamParameterName = configParams.gamParameterName ? configParams.gamParameterName : 'intent_iq_group';
221240

222241
const allowedStorage = defineStorageType(config.enabledStorageTypes);
223242

224243
let firstPartyData = tryParse(readData(FIRST_PARTY_KEY, allowedStorage));
225244
const isGroupB = firstPartyData?.group === WITHOUT_IIQ;
245+
setGamReporting(gamObjectReference, gamParameterName, firstPartyData?.group)
226246

227247
const firePartnerCallback = () => {
228248
if (configParams.callback && !callbackFired) {
@@ -403,9 +423,11 @@ export const intentIqIdSubmodule = {
403423
firstPartyData.group = WITHOUT_IIQ;
404424
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage);
405425
defineEmptyDataAndFireCallback();
426+
if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
406427
return
407428
} else {
408429
firstPartyData.group = WITH_IIQ;
430+
if (gamObjectReference) setGamReporting(gamObjectReference, gamParameterName, firstPartyData.group);
409431
}
410432
}
411433
if ('isOptedOut' in respJson) {

modules/intentIqIdSystem.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Please find below list of paramters that could be used in configuring Intent IQ
4343
| params.browserBlackList | Optional |  String | This is the name of a browser that can be added to a blacklist. | `"chrome"` |
4444
| params.manualWinReportEnabled | Optional | Boolean | This variable determines whether the bidWon event is triggered automatically. If set to false, the event will occur automatically, and manual reporting with reportExternalWin will be disabled. If set to true, the event will not occur automatically, allowing manual reporting through reportExternalWin. The default value is false. | `true`|
4545
| params.domainName | Optional | String | Specifies the domain of the page in which the IntentIQ object is currently running and serving the impression. This domain will be used later in the revenue reporting breakdown by domain. For example, cnn.com. It identifies the primary source of requests to the IntentIQ servers, even within nested web pages. | `"currentDomain.com"` |
46+
| params.gamObjectReference | Optional | Object | This is a reference to the Google Ad Manager (GAM) object, which will be used to set targeting. If this parameter is not provided, the group reporting will not be configured. | `googletag` |
47+
| params.gamParameterName | Optional | String | The name of the targeting parameter that will be used to pass the group. If not specified, the default value is `intent_iq_group`. | `"intent_iq_group"` |
4648

4749
### Configuration example
4850

test/spec/modules/intentIqIdSystem_spec.js

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { expect } from 'chai';
22
import { intentIqIdSubmodule, storage } from 'modules/intentIqIdSystem.js';
33
import * as utils from 'src/utils.js';
44
import { server } from 'test/mocks/xhr.js';
5-
import { decryptData, handleClientHints, readData } from '../../../modules/intentIqIdSystem';
5+
import { decryptData, handleClientHints, readData, setGamReporting } from '../../../modules/intentIqIdSystem';
66
import {getGppValue} from '../../../libraries/intentIqUtils/getGppValue.js';
77
import { gppDataHandler, uspDataHandler } from '../../../src/consentHandler';
88
import { clearAllCookies } from '../../helpers/cookies';
99
import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/intentIqUtils/detectBrowserUtils';
10-
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY} from '../../../libraries/intentIqConstants/intentIqConstants.js';
10+
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, NOT_YET_DEFINED, WITH_IIQ} from '../../../libraries/intentIqConstants/intentIqConstants.js';
1111

1212
const partner = 10;
1313
const pai = '11';
@@ -48,6 +48,24 @@ export const testClientHints = {
4848
wow64: false
4949
};
5050

51+
const mockGAM = () => {
52+
const targetingObject = {};
53+
return {
54+
cmd: [],
55+
pubads: () => ({
56+
setTargeting: (key, value) => {
57+
targetingObject[key] = value;
58+
},
59+
getTargeting: (key) => {
60+
return [targetingObject[key]];
61+
},
62+
getTargetingKeys: () => {
63+
return Object.keys(targetingObject);
64+
}
65+
})
66+
};
67+
};
68+
5169
describe('IntentIQ tests', function () {
5270
let logErrorStub;
5371
let testLSValue = {
@@ -199,6 +217,68 @@ describe('IntentIQ tests', function () {
199217
expect(callBackSpy.calledOnce).to.be.true;
200218
});
201219

220+
it('should set GAM targeting to U initially and update to A after server response', function () {
221+
let callBackSpy = sinon.spy();
222+
let mockGamObject = mockGAM();
223+
let expectedGamParameterName = 'intent_iq_group';
224+
225+
const originalPubads = mockGamObject.pubads;
226+
let setTargetingSpy = sinon.spy();
227+
mockGamObject.pubads = function () {
228+
const obj = { ...originalPubads.apply(this, arguments) };
229+
const originalSetTargeting = obj.setTargeting;
230+
obj.setTargeting = function (...args) {
231+
setTargetingSpy(...args);
232+
return originalSetTargeting.apply(this, args);
233+
};
234+
return obj;
235+
};
236+
237+
defaultConfigParams.params.gamObjectReference = mockGamObject;
238+
239+
let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback;
240+
241+
submoduleCallback(callBackSpy);
242+
let request = server.requests[0];
243+
244+
mockGamObject.cmd.forEach(cb => cb());
245+
mockGamObject.cmd = []
246+
247+
let groupBeforeResponse = mockGamObject.pubads().getTargeting(expectedGamParameterName);
248+
249+
request.respond(
250+
200,
251+
responseHeader,
252+
JSON.stringify({ group: 'A', tc: 20 })
253+
);
254+
255+
mockGamObject.cmd.forEach(item => item());
256+
257+
let groupAfterResponse = mockGamObject.pubads().getTargeting(expectedGamParameterName);
258+
259+
expect(request.url).to.contain('https://api.intentiq.com/profiles_engine/ProfilesEngineServlet?at=39');
260+
expect(groupBeforeResponse).to.deep.equal([NOT_YET_DEFINED]);
261+
expect(groupAfterResponse).to.deep.equal([WITH_IIQ]);
262+
263+
expect(setTargetingSpy.calledTwice).to.be.true;
264+
});
265+
266+
it('should use the provided gamParameterName from configParams', function () {
267+
let callBackSpy = sinon.spy();
268+
let mockGamObject = mockGAM();
269+
let customParamName = 'custom_gam_param';
270+
271+
defaultConfigParams.params.gamObjectReference = mockGamObject;
272+
defaultConfigParams.params.gamParameterName = customParamName;
273+
274+
let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback;
275+
submoduleCallback(callBackSpy);
276+
mockGamObject.cmd.forEach(cb => cb());
277+
let targetingKeys = mockGamObject.pubads().getTargetingKeys();
278+
279+
expect(targetingKeys).to.include(customParamName);
280+
});
281+
202282
it('should not throw Uncaught TypeError when IntentIQ endpoint returns empty response', function () {
203283
let callBackSpy = sinon.spy();
204284
let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback;

0 commit comments

Comments
 (0)