Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IntentIq ID & Analytics Modules : support domainName parameter #12434

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
dd7399e
update intentIqAnalyticsAdapter.js && intentIqIdSystem.js
dLepetynskyiIntentiq Oct 2, 2024
9cf5ec0
fix lint issues
DimaIntentIQ Oct 2, 2024
b0baad3
fix tests
dLepetynskyiIntentiq Oct 2, 2024
bfe0b15
move info
dLepetynskyiIntentiq Oct 2, 2024
c2406da
resolve issues
dLepetynskyiIntentiq Oct 6, 2024
5124012
update storeFirstPartyData
dLepetynskyiIntentiq Oct 9, 2024
7d07e3b
remove unused code
DimaIntentIQ Oct 9, 2024
dace564
update defineEmptyDataAndFireCallback
dLepetynskyiIntentiq Oct 9, 2024
e6c7856
update fix lint
dLepetynskyiIntentiq Oct 9, 2024
1f1efb5
update reportExternalWin
dLepetynskyiIntentiq Oct 9, 2024
17fd8f9
small fixes
DimaIntentIQ Oct 9, 2024
c3e8da7
update test && add docs
dLepetynskyiIntentiq Oct 10, 2024
4210aab
Merge pull request #2 from DimaIntentIQ/AGT-356-prebid-manual-reporting
DimaIntentIQ Oct 10, 2024
fcbe1f0
Merge branch 'prebid:master' into master
DimaIntentIQ Oct 10, 2024
a298a00
Merge branch 'prebid:master' into master
DimaIntentIQ Oct 24, 2024
bca60e2
AGT-347: Support domain name
dmytro-po Oct 30, 2024
3876d76
AGT-347: Support domain name
dmytro-po Oct 30, 2024
8940970
AGT-374: Support domainName to vrref
dmytro-po Oct 30, 2024
f0252b5
AGT-374: tests in progress
dmytro-po Nov 1, 2024
d949b88
AGT-374: Remove duplicate encoded in getRelevantRefferer and fix tests
dmytro-po Nov 3, 2024
7eb4bcf
AGT-374: Add test domainName, changes in documentation
dmytro-po Nov 12, 2024
2d709b5
AGT-374: Change js version value
dmytro-po Nov 12, 2024
21c758b
AGT-374: Remove extra coma
dmytro-po Nov 12, 2024
c86bcd2
Remove unused method
dmytro-po Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libraries/intentIqConstants/intentIqConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ export const OPT_OUT = 'O';
export const BLACK_LIST = 'L';
export const CLIENT_HINTS_KEY = '_iiq_ch';
export const EMPTY = 'EMPTY'
export const VERSION = 0.21
export const VERSION = 0.22
64 changes: 64 additions & 0 deletions libraries/intentIqUtils/getRefferer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { getWindowTop, logError, getWindowLocation, getWindowSelf } from '../../src/utils.js';

/**
* Determines if the script is running inside an iframe and retrieves the URL.
* @return {string} The encoded vrref value representing the relevant URL.
*/
export function getReferrer() {
try {
if (getWindowSelf() === getWindowTop()) {
return encodeURIComponent(getWindowLocation().href);
} else {
return encodeURIComponent(getWindowTop().location.href);
}
} catch (error) {
logError(`Error accessing location: ${error}`);
return '';
}
}

/**
* Appends `vrref` and `fui` parameters to the provided URL.
* If the referrer URL is available, it appends `vrref` with the relevant referrer value based on the domain.
* Otherwise, it appends `fui=1`. If a domain name is provided, it may also append `vrref` with the domain.
* @param {string} url - The URL to append parameters to.
* @param {string} domainName - The domain name used to determine the relevant referrer.
* @return {string} The modified URL with appended `vrref` or `fui` parameters.
*/
export function appendVrrefAndFui(url, domainName) {
const fullUrl = getReferrer();
if (fullUrl) {
return (url += '&vrref=' + getRelevantRefferer(domainName, fullUrl));
}
url += '&fui=1'; // Full Url Issue
url += '&vrref=' + encodeURIComponent(domainName || '');
return url;
}

/**
* Get the relevant referrer based on full URL and domain
* @param {string} domainName The domain name to compare
* @param {string} fullUrl The full URL to analyze
* @return {string} The relevant referrer
*/
export function getRelevantRefferer(domainName, fullUrl) {
if (domainName && isDomainIncluded(fullUrl, domainName)) {
return fullUrl;
}
return domainName ? encodeURIComponent(domainName) : fullUrl;
}

/**
* Checks if the provided domain name is included in the full URL.
* @param {string} fullUrl - The full URL to check.
* @param {string} domainName - The domain name to search for within the URL.
* @return {boolean} `True` if the domain name is found in the URL, `false` otherwise.
*/
export function isDomainIncluded(fullUrl, domainName) {
try {
return fullUrl.includes(domainName);
} catch (error) {
logError(`Invalid URL provided: ${error}`);
return false;
}
}
40 changes: 16 additions & 24 deletions modules/intentIqAnalyticsAdapter.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {getWindowLocation, getWindowSelf, getWindowTop, logError, logInfo} from '../src/utils.js';
import {logError, logInfo} from '../src/utils.js';
import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
import {ajax} from '../src/ajax.js';
import {getStorageManager} from '../src/storageManager.js';
import {config} from '../src/config.js';
import {EVENTS} from '../src/constants.js';
import {MODULE_TYPE_ANALYTICS} from '../src/activities/modules.js';
import {detectBrowser} from '../libraries/detectBrowserUtils/detectBrowserUtils.js';
import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js';
import {appendVrrefAndFui, getReferrer} from '../libraries/intentIqUtils/getRefferer.js';
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, VERSION} from '../libraries/intentIqConstants/intentIqConstants.js';

const MODULE_NAME = 'iiqAnalytics'
Expand Down Expand Up @@ -61,7 +62,8 @@ let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl, analyticsT
dataInLs: null,
eidl: null,
lsIdsInitialized: false,
manualWinReportEnabled: false
manualWinReportEnabled: false,
domainName: null
},
track({eventType, args}) {
switch (eventType) {
Expand Down Expand Up @@ -114,7 +116,8 @@ function initLsValues() {
iiqAnalyticsAnalyticsAdapter.initOptions.partner = iiqArr[0].params.partner;
}
iiqAnalyticsAnalyticsAdapter.initOptions.browserBlackList = typeof iiqArr[0].params.browserBlackList === 'string' ? iiqArr[0].params.browserBlackList.toLowerCase() : '';
iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = iiqArr[0].params.manualWinReportEnabled || false
iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = iiqArr[0].params.manualWinReportEnabled || false;
iiqAnalyticsAnalyticsAdapter.initOptions.domainName = iiqArr[0].params.domainName || '';
}
}

Expand Down Expand Up @@ -258,32 +261,21 @@ function getDefaultDataObject() {
}

function constructFullUrl(data) {
let report = []
data = btoa(JSON.stringify(data))
report.push(data)
return defaultUrl + '?pid=' + iiqAnalyticsAnalyticsAdapter.initOptions.partner +
let report = [];
data = btoa(JSON.stringify(data));
report.push(data);

let url = defaultUrl + '?pid=' + iiqAnalyticsAnalyticsAdapter.initOptions.partner +
'&mct=1' +
((iiqAnalyticsAnalyticsAdapter.initOptions && iiqAnalyticsAnalyticsAdapter.initOptions.fpid)
((iiqAnalyticsAnalyticsAdapter.initOptions?.fpid)
? '&iiqid=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid) : '') +
'&agid=' + REPORTER_ID +
'&jsver=' + VERSION +
'&vrref=' + getReferrer() +
'&source=pbjs' +
'&payload=' + JSON.stringify(report) +
'&uh=' + iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints
}

export function getReferrer() {
try {
if (getWindowSelf() === getWindowTop()) {
return encodeURIComponent(getWindowLocation().href);
} else {
return encodeURIComponent(getWindowTop().location.href);
}
} catch (error) {
logError(`Error accessing location: ${error}`);
return '';
}
'&uh=' + iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints;
url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName);
return url;
}

iiqAnalyticsAnalyticsAdapter.originEnableAnalytics = iiqAnalyticsAnalyticsAdapter.enableAnalytics;
Expand Down
6 changes: 5 additions & 1 deletion modules/intentIqIdSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {MODULE_TYPE_UID} from '../src/activities/modules.js';
import {gppDataHandler, uspDataHandler} from '../src/consentHandler.js';
import AES from 'crypto-js/aes.js';
import Utf8 from 'crypto-js/enc-utf8.js';
import {detectBrowser} from '../libraries/detectBrowserUtils/detectBrowserUtils.js';
import {detectBrowser} from '../libraries/intentIqUtils/detectBrowserUtils.js';
import {appendVrrefAndFui} from '../libraries/intentIqUtils/getRefferer.js';
import {
FIRST_PARTY_KEY,
WITH_IIQ, WITHOUT_IIQ,
Expand Down Expand Up @@ -383,6 +384,9 @@ export const intentIqIdSubmodule = {
url += VERSION ? '&jsver=' + VERSION : '';
url += firstPartyData?.group ? '&testGroup=' + encodeURIComponent(firstPartyData.group) : '';

// Add vrref and fui to the URL
url = appendVrrefAndFui(url, configParams.domainName);

const storeFirstPartyData = () => {
partnerData.eidl = runtimeEids?.eids?.length || -1
storeData(FIRST_PARTY_KEY, JSON.stringify(firstPartyData), allowedStorage);
Expand Down
5 changes: 3 additions & 2 deletions modules/intentIqIdSystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Please find below list of paramters that could be used in configuring Intent IQ
| params.timeoutInMillis | Optional | Number | This is the timeout in milliseconds, which defines the maximum duration before the callback is triggered. The default value is 500. | `450` |
| params.browserBlackList | Optional |  String | This is the name of a browser that can be added to a blacklist. | `"chrome"` |
| 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`|

| 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"` |

### Configuration example

Expand All @@ -56,7 +56,8 @@ pbjs.setConfig({
timeoutInMillis: 500,
browserBlackList: "chrome",
callback: (data, group) => window.pbjs.requestBids(),
manualWinReportEnabled: true
manualWinReportEnabled: true,
domainName: "currentDomain.com"
},
storage: {
type: "html5",
Expand Down
1 change: 0 additions & 1 deletion src/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ const _public = (function () {
return eventsFired.toArray().map(val => Object.assign({}, val))
};

window.prebidEvents = _public
return _public;
}());

Expand Down
86 changes: 78 additions & 8 deletions test/spec/modules/intentIqAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { expect } from 'chai';
import iiqAnalyticsAnalyticsAdapter from 'modules/intentIqAnalyticsAdapter.js';
import * as utils from 'src/utils.js';
import * as detectBrowserUtils from '../../../libraries/detectBrowserUtils/detectBrowserUtils';
import { server } from 'test/mocks/xhr.js';
import { config } from 'src/config.js';
import { EVENTS } from 'src/constants.js';
import * as events from 'src/events.js';
import { getStorageManager } from 'src/storageManager.js';
import sinon from 'sinon';
import { REPORTER_ID, getReferrer, preparePayload } from '../../../modules/intentIqAnalyticsAdapter';
import { REPORTER_ID, preparePayload } from '../../../modules/intentIqAnalyticsAdapter';
import {FIRST_PARTY_KEY, VERSION} from '../../../libraries/intentIqConstants/intentIqConstants.js';
import * as detectBrowserUtils from '../../../libraries/intentIqUtils/detectBrowserUtils.js';
import {getReferrer, appendVrrefAndFui} from '../../../libraries/intentIqUtils/getRefferer.js';

const partner = 10;
const defaultData = '{"pcid":"f961ffb1-a0e1-4696-a9d2-a21d815bd344", "group": "A"}';
Expand Down Expand Up @@ -89,7 +90,8 @@ describe('IntentIQ tests all', function () {
dataInLs: null,
eidl: null,
lsIdsInitialized: false,
manualWinReportEnabled: false
manualWinReportEnabled: false,
domainName: null
};
if (iiqAnalyticsAnalyticsAdapter.track.restore) {
iiqAnalyticsAnalyticsAdapter.track.restore();
Expand All @@ -115,13 +117,16 @@ describe('IntentIQ tests all', function () {

it('IIQ Analytical Adapter bid win report', function () {
localStorage.setItem(FIRST_PARTY_KEY, defaultData);
getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns({href: 'http://localhost:9876/'});
const expectedVrref = encodeURIComponent(getWindowLocationStub().href);

events.emit(EVENTS.BID_WON, wonRequest);

expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain('https://reports.intentiq.com/report?pid=' + partner + '&mct=1');
expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
expect(request.url).to.contain(`&jsver=${version}`);
expect(request.url).to.contain(`&vrref=${expectedVrref}`);
expect(request.url).to.contain('&payload=');
expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
});
Expand All @@ -132,13 +137,15 @@ describe('IntentIQ tests all', function () {

it('should handle BID_WON event with group configuration from local storage', function () {
localStorage.setItem(FIRST_PARTY_KEY, '{"pcid":"testpcid", "group": "B"}');
const expectedVrref = encodeURIComponent('http://localhost:9876/');

events.emit(EVENTS.BID_WON, wonRequest);

expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain('https://reports.intentiq.com/report?pid=' + partner + '&mct=1');
expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
expect(request.url).to.contain(`&jsver=${version}`);
expect(request.url).to.contain(`&vrref=${expectedVrref}`);
expect(request.url).to.contain('iiqid=testpcid');
});

Expand All @@ -153,9 +160,11 @@ describe('IntentIQ tests all', function () {
const dataToSend = preparePayload(wonRequest);
const base64String = btoa(JSON.stringify(dataToSend));
const payload = `[%22${base64String}%22]`;
expect(request.url).to.equal(
`https://reports.intentiq.com/report?pid=${partner}&mct=1&iiqid=${defaultDataObj.pcid}&agid=${REPORTER_ID}&jsver=${version}&vrref=${getReferrer()}&source=pbjs&payload=${payload}&uh=`
const expectedUrl = appendVrrefAndFui(
`https://reports.intentiq.com/report?pid=${partner}&mct=1&iiqid=${defaultDataObj.pcid}&agid=${REPORTER_ID}&jsver=${version}&source=pbjs&payload=${payload}&uh=`,
iiqAnalyticsAnalyticsAdapter.initOptions.domainName
);
expect(request.url).to.equal(expectedUrl);
expect(dataToSend.pcid).to.equal(defaultDataObj.pcid)
});

Expand Down Expand Up @@ -250,8 +259,69 @@ describe('IntentIQ tests all', function () {
expect(server.requests.length).to.be.above(0);
const request = server.requests[0];
expect(request.url).to.contain(`https://reports.intentiq.com/report?pid=${partner}&mct=1`);
expect(request.url).to.contain(`&jsver=${version}&vrref=${encodeURIComponent('http://localhost:9876/')}`);
expect(request.url).to.contain(`&jsver=${version}`);
expect(request.url).to.contain(`&vrref=${encodeURIComponent('http://localhost:9876/')}`);
expect(request.url).to.contain('&payload=');
expect(request.url).to.contain('iiqid=f961ffb1-a0e1-4696-a9d2-a21d815bd344');
});

const testCasesVrref = [
{
description: 'domainName matches window.top.location.href',
getWindowSelf: {},
getWindowTop: { location: { href: 'http://example.com/page' } },
getWindowLocation: { href: 'http://example.com/page' },
domainName: 'example.com',
expectedVrref: encodeURIComponent('http://example.com/page'),
shouldContainFui: false
},
{
description: 'domainName does not match window.top.location.href',
getWindowSelf: {},
getWindowTop: { location: { href: 'http://anotherdomain.com/page' } },
getWindowLocation: { href: 'http://anotherdomain.com/page' },
domainName: 'example.com',
expectedVrref: encodeURIComponent('example.com'),
shouldContainFui: false
},
{
description: 'domainName is missing, only fui=1 is returned',
getWindowSelf: {},
getWindowTop: { location: { href: '' } },
getWindowLocation: { href: '' },
domainName: null,
expectedVrref: '',
shouldContainFui: true
},
{
description: 'domainName is missing',
getWindowSelf: {},
getWindowTop: { location: { href: 'http://example.com/page' } },
getWindowLocation: { href: 'http://example.com/page' },
domainName: null,
expectedVrref: encodeURIComponent('http://example.com/page'),
shouldContainFui: false
},
];

testCasesVrref.forEach(({ description, getWindowSelf, getWindowTop, getWindowLocation, domainName, expectedVrref, shouldContainFui }) => {
it(`should append correct vrref when ${description}`, function () {
getWindowSelfStub = sinon.stub(utils, 'getWindowSelf').returns(getWindowSelf);
getWindowTopStub = sinon.stub(utils, 'getWindowTop').returns(getWindowTop);
getWindowLocationStub = sinon.stub(utils, 'getWindowLocation').returns(getWindowLocation);

const url = 'https://reports.intentiq.com/report?pid=10';
const modifiedUrl = appendVrrefAndFui(url, domainName);
const urlObj = new URL(modifiedUrl);

const vrref = encodeURIComponent(urlObj.searchParams.get('vrref') || '');
const fui = urlObj.searchParams.get('fui');

expect(vrref).to.equal(expectedVrref);
expect(urlObj.searchParams.has('fui')).to.equal(shouldContainFui);
if (shouldContainFui) {
expect(fui).to.equal('1');
}
});
});
});
2 changes: 1 addition & 1 deletion test/spec/modules/intentIqIdSystem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { server } from 'test/mocks/xhr.js';
import { decryptData, handleClientHints, handleGPPData, readData } from '../../../modules/intentIqIdSystem';
import { gppDataHandler, uspDataHandler } from '../../../src/consentHandler';
import { clearAllCookies } from '../../helpers/cookies';
import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/detectBrowserUtils/detectBrowserUtils';
import { detectBrowserFromUserAgent, detectBrowserFromUserAgentData } from '../../../libraries/intentIqUtils/detectBrowserUtils';
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY} from '../../../libraries/intentIqConstants/intentIqConstants.js';

const partner = 10;
Expand Down