Skip to content

IntentIq ID & Analytics Modules: Supports additional configuration parameters #13122

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

Merged
merged 6 commits into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 14 additions & 5 deletions libraries/intentIqConstants/intentIqConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ export const BLACK_LIST = 'L';
export const CLIENT_HINTS_KEY = '_iiq_ch';
export const EMPTY = 'EMPTY';
export const GVLID = '1323';
export const VERSION = 0.28;
export const VERSION = 0.29;
export const PREBID = 'pbjs';
export const HOURS_24 = 86400000;

export const VR_ENDPOINT = 'https://api.intentiq.com';
export const GDPR_ENDPOINT = 'https://api-gdpr.intentiq.com';
export const INVALID_ID = 'INVALID_ID';

export const SYNC_ENDPOINT = 'https://sync.intentiq.com'
export const GDPR_SYNC_ENDPOINT = 'https://sync-gdpr.intentiq.com'
export const SCREEN_PARAMS = {
0: 'windowInnerHeight',
1: 'windowInnerWidth',
Expand All @@ -27,3 +25,14 @@ export const SCREEN_PARAMS = {
};

export const SYNC_REFRESH_MILL = 3600000;
export const META_DATA_CONSTANT = 256;

export const MAX_REQUEST_LENGTH = {
// https://www.geeksforgeeks.org/maximum-length-of-a-url-in-different-browsers/
chrome: 2097152,
safari: 80000,
opera: 2097152,
edge: 2048,
firefox: 65536,
ie: 2048
};
44 changes: 44 additions & 0 deletions libraries/intentIqUtils/handleAdditionalParams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MAX_REQUEST_LENGTH } from "../intentIqConstants/intentIqConstants.js";

/**
* Appends additional parameters to a URL if they are valid and applicable for the given request destination.
*
* @param {string} browser - The name of the current browser; used to look up the maximum URL length.
* @param {string} url - The base URL to which additional parameters may be appended.
* @param {(string|number)} requestTo - The destination identifier; used as an index to check if a parameter applies.
* @param {Array<Object>} additionalParams - An array of parameter objects to append.
* Each parameter object should have the following properties:
* - `parameterName` {string}: The name of the parameter.
* - `parameterValue` {*}: The value of the parameter.
* - `destination` {Object|Array}: An object or array indicating the applicable destinations. Sync = 0, VR = 1, reporting = 2
*
* @return {string} The resulting URL with additional parameters appended if valid; otherwise, the original URL.
*/
export function handleAdditionalParams(browser, url, requestTo, additionalParams) {
let queryString = '';

if (!Array.isArray(additionalParams)) return url;

for (let i = 0; i < additionalParams.length; i++) {
const param = additionalParams[i];

if (
typeof param !== 'object' ||
!param.parameterName ||
!param.parameterValue ||
!param.destination ||
!Array.isArray(param.destination)
) {
continue;
}

if (param.destination[requestTo]) {
queryString += `&agp_${encodeURIComponent(param.parameterName)}=${param.parameterValue}`;
}
}

const maxLength = MAX_REQUEST_LENGTH[browser] ?? 2048;
if ((url.length + queryString.length) > maxLength) return url;

return url + queryString;
}
3 changes: 3 additions & 0 deletions libraries/intentIqUtils/intentIqConfig.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions libraries/intentIqUtils/storageUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,16 @@ export function defineStorageType(params) {
const filteredArr = params.filter(item => SUPPORTED_TYPES.includes(item));
return filteredArr.length ? filteredArr : ['html5'];
}

/**
* Parse json if possible, else return null
* @param data
*/
export function tryParse(data) {
try {
return JSON.parse(data);
} catch (err) {
logError(err);
return null;
}
}
5 changes: 5 additions & 0 deletions libraries/intentIqUtils/urlUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function appendSPData (url, firstPartyData) {
const spdParam = firstPartyData?.spd ? encodeURIComponent(typeof firstPartyData.spd === 'object' ? JSON.stringify(firstPartyData.spd) : firstPartyData.spd) : '';
url += spdParam ? '&spd=' + spdParam : '';
return url
};
87 changes: 67 additions & 20 deletions modules/intentIqAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ 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/intentIqUtils/detectBrowserUtils.js';
import {appendSPData} from '../libraries/intentIqUtils/urlUtils.js';
import {appendVrrefAndFui, getReferrer} from '../libraries/intentIqUtils/getRefferer.js';
import {getCmpData} from '../libraries/intentIqUtils/getCmpData.js'
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, VERSION} from '../libraries/intentIqConstants/intentIqConstants.js';
import {CLIENT_HINTS_KEY, FIRST_PARTY_KEY, VERSION, PREBID} from '../libraries/intentIqConstants/intentIqConstants.js';
import {readData, defineStorageType} from '../libraries/intentIqUtils/storageUtils.js';
import {reportingServerAddress} from '../libraries/intentIqUtils/intentIqConfig.js';
import { handleAdditionalParams } from '../libraries/intentIqUtils/handleAdditionalParams.js';

const MODULE_NAME = 'iiqAnalytics'
const analyticsType = 'endpoint';
const REPORT_ENDPOINT = 'https://reports.intentiq.com/report';
const REPORT_ENDPOINT_GDPR = 'https://reports-gdpr.intentiq.com/report';
const storage = getStorageManager({moduleType: MODULE_TYPE_ANALYTICS, moduleName: MODULE_NAME});
const prebidVersion = '$prebid.version$';
export const REPORTER_ID = Date.now() + '_' + getRandom(0, 1000);
Expand Down Expand Up @@ -59,7 +60,21 @@ const PARAMS_NAMES = {
adType: 'adType'
};

let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl: REPORT_ENDPOINT, analyticsType}), {
function getIntentIqConfig() {
return config.getConfig('userSync.userIds')?.find(m => m.name === 'intentIqId');
}

const DEFAULT_URL = 'https://reports.intentiq.com/report'

const getDataForDefineURL = () => {
const iiqConfig = getIntentIqConfig()
const cmpData = getCmpData();
const gdprDetected = cmpData.gdprString;

return [iiqConfig, gdprDetected]
}

let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({url: DEFAULT_URL, analyticsType}), {
initOptions: {
lsValueInitialized: false,
partner: null,
Expand All @@ -69,7 +84,10 @@ let iiqAnalyticsAnalyticsAdapter = Object.assign(adapter({defaultUrl: REPORT_END
eidl: null,
lsIdsInitialized: false,
manualWinReportEnabled: false,
domainName: null
domainName: null,
siloEnabled: false,
reportMethod: null,
additionalParams: null
},
track({eventType, args}) {
switch (eventType) {
Expand All @@ -91,11 +109,7 @@ const {
BID_REQUESTED
} = EVENTS;

function getIntentIqConfig() {
return config.getConfig('userSync.userIds')?.find(m => m.name === 'intentIqId');
}

function initLsValues() {
function initAdapterConfig() {
if (iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized) return;
let iiqConfig = getIntentIqConfig()

Expand All @@ -108,16 +122,24 @@ function initLsValues() {
typeof iiqConfig.params?.browserBlackList === 'string' ? iiqConfig.params.browserBlackList.toLowerCase() : '';
iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled = iiqConfig.params?.manualWinReportEnabled || false;
iiqAnalyticsAnalyticsAdapter.initOptions.domainName = iiqConfig.params?.domainName || '';
iiqAnalyticsAnalyticsAdapter.initOptions.siloEnabled =
typeof iiqConfig.params?.siloEnabled === 'boolean' ? iiqConfig.params.siloEnabled : false;
iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod = parseReportingMethod(iiqConfig.params?.reportMethod);
iiqAnalyticsAnalyticsAdapter.initOptions.additionalParams = iiqConfig.params?.additionalParams || null;
} else {
iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized = false;
iiqAnalyticsAnalyticsAdapter.initOptions.partner = -1;
iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod = 'GET';
}
}

function initReadLsIds() {
try {
iiqAnalyticsAnalyticsAdapter.initOptions.dataInLs = null;
iiqAnalyticsAnalyticsAdapter.initOptions.fpid = JSON.parse(readData(FIRST_PARTY_KEY, allowedStorage, storage));
iiqAnalyticsAnalyticsAdapter.initOptions.fpid = JSON.parse(readData(
`${FIRST_PARTY_KEY}${iiqAnalyticsAnalyticsAdapter.initOptions.siloEnabled ? '_p_' + iiqAnalyticsAnalyticsAdapter.initOptions.partner : ''}`,
allowedStorage, storage
));
if (iiqAnalyticsAnalyticsAdapter.initOptions.fpid) {
iiqAnalyticsAnalyticsAdapter.initOptions.currentGroup = iiqAnalyticsAnalyticsAdapter.initOptions.fpid.group;
}
Expand All @@ -144,7 +166,7 @@ function initReadLsIds() {

function bidWon(args, isReportExternal) {
if (!iiqAnalyticsAnalyticsAdapter.initOptions.lsValueInitialized) {
initLsValues();
initAdapterConfig();
}

if (isNaN(iiqAnalyticsAnalyticsAdapter.initOptions.partner) || iiqAnalyticsAnalyticsAdapter.initOptions.partner == -1) return;
Expand All @@ -159,13 +181,32 @@ function bidWon(args, isReportExternal) {
initReadLsIds();
}
if ((isReportExternal && iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled) || (!isReportExternal && !iiqAnalyticsAnalyticsAdapter.initOptions.manualWinReportEnabled)) {
ajax(constructFullUrl(preparePayload(args, true)), undefined, null, {method: 'GET'});
logInfo('IIQ ANALYTICS -> BID WON')
const { url, method, payload } = constructFullUrl(preparePayload(args, true));
if (method === 'POST') {
ajax(url, undefined, payload, {method, contentType: 'application/x-www-form-urlencoded'});
} else {
ajax(url, undefined, null, {method});
}
logInfo('IIQ ANALYTICS -> BID WON');
return true;
}
return false;
}

function parseReportingMethod(reportMethod) {
if (typeof reportMethod === 'string') {
switch (reportMethod.toUpperCase()) {
case 'GET':
return 'GET';
case 'POST':
return 'POST';
default:
return 'GET';
}
}
return 'GET';
}

function defineGlobalVariableName() {
function reportExternalWin(args) {
return bidWon(args, true);
Expand Down Expand Up @@ -302,30 +343,36 @@ function getDefaultDataObject() {

function constructFullUrl(data) {
let report = [];
const reportMethod = iiqAnalyticsAnalyticsAdapter.initOptions.reportMethod;
const currentBrowserLowerCase = detectBrowser();
data = btoa(JSON.stringify(data));
report.push(data);

const cmpData = getCmpData();
const gdprDetected = cmpData.gdprString;
const baseUrl = gdprDetected ? REPORT_ENDPOINT_GDPR : REPORT_ENDPOINT;
const baseUrl = reportingServerAddress(...getDataForDefineURL());

let url = baseUrl + '?pid=' + iiqAnalyticsAnalyticsAdapter.initOptions.partner +
'&mct=1' +
((iiqAnalyticsAnalyticsAdapter.initOptions?.fpid)
? '&iiqid=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.fpid.pcid) : '') +
'&agid=' + REPORTER_ID +
'&jsver=' + VERSION +
'&source=pbjs' +
'&payload=' + JSON.stringify(report) +
'&source=' + PREBID +
'&uh=' + encodeURIComponent(iiqAnalyticsAnalyticsAdapter.initOptions.clientsHints) +
(cmpData.uspString ? '&us_privacy=' + encodeURIComponent(cmpData.uspString) : '') +
(cmpData.gppString ? '&gpp=' + encodeURIComponent(cmpData.gppString) : '') +
(cmpData.gdprString
? '&gdpr_consent=' + encodeURIComponent(cmpData.gdprString) + '&gdpr=1'
: '&gdpr=0');

url = appendSPData(url, iiqAnalyticsAnalyticsAdapter.initOptions.fpid)
url = appendVrrefAndFui(url, iiqAnalyticsAnalyticsAdapter.initOptions.domainName);
return url;

if (reportMethod === 'POST') {
return { url, method: 'POST', payload: JSON.stringify(report) };
}
url += '&payload=' + encodeURIComponent(JSON.stringify(report));
url = handleAdditionalParams(currentBrowserLowerCase, url, 2, iiqAnalyticsAnalyticsAdapter.initOptions.additionalParams);
return { url, method: 'GET' };
}

iiqAnalyticsAnalyticsAdapter.originEnableAnalytics = iiqAnalyticsAnalyticsAdapter.enableAnalytics;
Expand Down
Loading