diff --git a/libraries/intentIqConstants/intentIqConstants.js b/libraries/intentIqConstants/intentIqConstants.js index 05a0bfb0885..820c8364372 100644 --- a/libraries/intentIqConstants/intentIqConstants.js +++ b/libraries/intentIqConstants/intentIqConstants.js @@ -9,7 +9,7 @@ export const BLACK_LIST = 'L'; export const CLIENT_HINTS_KEY = '_iiq_ch'; export const EMPTY = 'EMPTY'; export const GVLID = '1323'; -export const VERSION = 0.29; +export const VERSION = 0.30; export const PREBID = 'pbjs'; export const HOURS_24 = 86400000; diff --git a/modules/intentIqIdSystem.js b/modules/intentIqIdSystem.js index 78f193eda11..90fe0573614 100644 --- a/modules/intentIqIdSystem.js +++ b/modules/intentIqIdSystem.js @@ -303,6 +303,20 @@ function storeCounters(storage, partnerData) { storeData(PARTNER_DATA_KEY, JSON.stringify(partnerData), storage, firstPartyData); } +export function setPPID(gamObjectReference, shouldSetPPID, eids) { + if (!shouldSetPPID || !isPlainObject(gamObjectReference) || !Array.isArray(eids)) return; + + const intentIqEid = eids.find(eid => eid.source === 'intentiq.com'); + if (!intentIqEid?.uids?.length) return; + + const ppuid = intentIqEid.uids.find(uid => uid.ext?.stype === 'ppuid')?.id; + if (!ppuid) return; + + gamObjectReference.cmd?.push(() => { + gamObjectReference.pubads().setPublisherProvidedId(ppuid); + }); +} + /** @type {Submodule} */ export const intentIqIdSubmodule = { /** @@ -444,6 +458,7 @@ export const intentIqIdSubmodule = { if (partnerData.data.length) { // encrypted data decryptedData = tryParse(decryptData(partnerData.data)); runtimeEids = decryptedData; + setPPID(gamObjectReference, configParams.shouldSetPPID, runtimeEids.eids); } } @@ -611,6 +626,7 @@ export const intentIqIdSubmodule = { if (respJson.data?.eids) { runtimeEids = respJson.data + setPPID(gamObjectReference, configParams.shouldSetPPID, runtimeEids.eids); callback(respJson.data.eids); firePartnerCallback() const encryptedData = encryptData(JSON.stringify(respJson.data)) diff --git a/modules/intentIqIdSystem.md b/modules/intentIqIdSystem.md index 644fe07fcd2..dec7fe8d6cd 100644 --- a/modules/intentIqIdSystem.md +++ b/modules/intentIqIdSystem.md @@ -59,6 +59,7 @@ Please find below list of parameters that could be used in configuring Intent IQ | params.additionalParameters [0].parameterName | Required | String | Name of the custom parameter. This will be sent as a query parameter. | `"abc"` | | params.additionalParameters [0].parameterValue | Required | String / Number | Value to assign to the parameter. | `123` | | params.additionalParameters [0].destination | Required | Array | Array of numbers either `1` or `0`. Controls where this parameter is sent `[sendWithSync, sendWithVr, winreport]`. | `[1, 0, 0]` | +| params.shouldSetPPID | Optional | Boolean | Enables the Intent IQ ID to be sent to Google Ad Manager as a Publisher Provided ID (PPID). Requires `gamObjectReference` to be provided in the configuration. | `true` | ### Configuration example @@ -81,6 +82,7 @@ pbjs.setConfig({ sourceMetaData: "123.123.123.123", // Optional parameter sourceMetaDataExternal: 123456, // Optional parameter reportMethod: "GET", // Optional parameter + shouldSetPPID: true, // Optional parameter additionalParameters: [ // Optional parameter { parameterName: "abc", diff --git a/test/spec/modules/intentIqIdSystem_spec.js b/test/spec/modules/intentIqIdSystem_spec.js index 49da8fa3267..01c36b69de1 100644 --- a/test/spec/modules/intentIqIdSystem_spec.js +++ b/test/spec/modules/intentIqIdSystem_spec.js @@ -58,7 +58,7 @@ export const testClientHints = { const testAPILink = 'https://new-test-api.intentiq.com' const syncTestAPILink = 'https://new-test-sync.intentiq.com' -const mockGAM = () => { +const mockGAM = (ppidSpy = sinon.spy()) => { const targetingObject = {}; return { cmd: [], @@ -71,7 +71,8 @@ const mockGAM = () => { }, getTargetingKeys: () => { return Object.keys(targetingObject); - } + }, + setPublisherProvidedId: ppidSpy }) }; }; @@ -302,21 +303,101 @@ describe('IntentIQ tests', function () { }); it('should use the provided gamParameterName from configParams', function () { - let callBackSpy = sinon.spy(); let mockGamObject = mockGAM(); let customParamName = 'custom_gam_param'; defaultConfigParams.params.gamObjectReference = mockGamObject; defaultConfigParams.params.gamParameterName = customParamName; - let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; - submoduleCallback(callBackSpy); + intentIqIdSubmodule.getId(defaultConfigParams) mockGamObject.cmd.forEach(cb => cb()); let targetingKeys = mockGamObject.pubads().getTargetingKeys(); expect(targetingKeys).to.include(customParamName); }); + it('should call setPublisherProvidedId if shouldSetPPID=true', function () { + const ppidSpy = sinon.spy(); + const gam = mockGAM(ppidSpy); + + defaultConfigParams.params.gamObjectReference = gam; + defaultConfigParams.params.shouldSetPPID = true; + + intentIqIdSubmodule.getId(defaultConfigParams).callback(() => {}); + + const request = server.requests[0]; + request.respond(200, responseHeader, JSON.stringify({ + data: { + eids: [ + { + source: 'intentiq.com', + uids: [ + { + id: 'test_id', + ext: { stype: 'ppuid' } + } + ] + } + ] + } + }) + ); + + gam.cmd.forEach(cb => cb()); + + expect(request.url).to.contain('at=39'); + expect(ppidSpy.calledOnce).to.be.true; + expect(ppidSpy.firstCall.args[0]).to.equal('test_id'); + }); + + it('should call setPublisherProvidedId if shouldSetPPID=true and ids are present in LS', function () { + const ppidSpy = sinon.spy(); + const gam = mockGAM(ppidSpy); + + defaultConfigParams.params.gamObjectReference = gam; + defaultConfigParams.params.shouldSetPPID = true; + + localStorage.setItem('_iiq_fdata_' + partner, JSON.stringify(testLSValueWithData)); + + intentIqIdSubmodule.getId(defaultConfigParams); + gam.cmd.forEach(fn => typeof fn === 'function' && fn()); + + const expectedEid = JSON.parse(decryptData(testLSValueWithData.data)).eids[0].uids[0].id; + expect(ppidSpy.calledOnce).to.be.true; + expect(ppidSpy.firstCall.args[0]).to.equal(expectedEid); + }); + + it('should NOT call setPublisherProvidedId if shouldSetPPID=false', function () { + const ppidSpy = sinon.spy(); + const gam = { + cmd: [], + pubads: () => ({ + setTargeting: sinon.spy(), + setPublisherProvidedId: ppidSpy + }) + }; + defaultConfigParams.params.gamObjectReference = gam; + defaultConfigParams.params.shouldSetPPID = false; + intentIqIdSubmodule.getId(defaultConfigParams).callback(() => {}); + + const request = server.requests[0]; + request.respond(200, responseHeader, JSON.stringify({ + data: { + eids: [{ + source: 'intentiq.com', + uids: [{ + id: 'test_id', + ext: { stype: 'ppuid' } + }] + }] + } + })); + + gam.cmd.forEach(cb => cb()); + expect(request.url).to.contain('at=39'); + expect(ppidSpy.called).to.be.false; + }); + it('should not throw Uncaught TypeError when IntentIQ endpoint returns empty response', function () { let callBackSpy = sinon.spy(); let submoduleCallback = intentIqIdSubmodule.getId(defaultConfigParams).callback; @@ -395,6 +476,7 @@ describe('IntentIQ tests', function () { it('return data stored in local storage ', function () { localStorage.setItem('_iiq_fdata_' + partner, JSON.stringify(testLSValueWithData)); + let returnedValue = intentIqIdSubmodule.getId(allConfigParams); expect(returnedValue.id).to.deep.equal(JSON.parse(decryptData(testLSValueWithData.data)).eids); });