Skip to content
Open
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
207 changes: 129 additions & 78 deletions modules/gptPreAuction.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import { getSignals as getSignalsFn, getSegments as getSegmentsFn, taxonomies } from '../libraries/gptUtils/gptUtils.js';
import { auctionManager } from '../src/auctionManager.js';
import { config } from '../src/config.js';
import { TARGETING_KEYS } from '../src/constants.js';
import { getHook } from '../src/hook.js';
import { find } from '../src/polyfill.js';
import {
getSignals as getSignalsFn,
getSegments as getSegmentsFn,
taxonomies,
} from "../libraries/gptUtils/gptUtils.js";
import { auctionManager } from "../src/auctionManager.js";
import { config } from "../src/config.js";
import { TARGETING_KEYS } from "../src/constants.js";
import { getHook } from "../src/hook.js";
import {
deepAccess,
deepSetValue,
isAdUnitCodeMatchingSlot,
isGptPubadsDefined,
logInfo,
logWarn,
pick,
uniques
} from '../src/utils.js';
uniques,
} from "../src/utils.js";

const MODULE_NAME = 'GPT Pre-Auction';
const MODULE_NAME = "GPT Pre-Auction";
export let _currentConfig = {};
let hooksAdded = false;

window.adpushup = window.adpushup || {};
const adp = window.adpushup;
const adpConfig = adp.config || {};

window.adpTags = window.adpTags || {};
const adpTags = window.adpTags;
const adpSlots = adpTags.adpSlots || {};

const getAdUnitPath = function (code) {
const adpSlot = adpSlots[code];
if (!adpSlot) return null;
const { childPublisherId, isMcmEnabled = false } = adpConfig.mcm;

let dfpNetwork = adpSlot.activeDFPNetwork;
if (isMcmEnabled) {
dfpNetwork += `,${childPublisherId}`;
}
const dfpAdUnitCode =
adpSlot.currentGptSlotData && adpSlot.currentGptSlotData.dfpAdunitCode;
if (!dfpAdUnitCode) {
return null;
}
return `/${dfpNetwork}/${dfpAdUnitCode}`;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add undefined condition here.

};
export function getSegments(fpd, sections, segtax) {
return getSegmentsFn(fpd, sections, segtax);
}
Expand All @@ -27,11 +51,14 @@ export function getSignals(fpd) {
return getSignalsFn(fpd);
}

export function getSignalsArrayByAuctionsIds(auctionIds, index = auctionManager.index) {
export function getSignalsArrayByAuctionsIds(
auctionIds,
index = auctionManager.index
) {
const signals = auctionIds
.map(auctionId => index.getAuction({ auctionId })?.getFPD()?.global)
.map((auctionId) => index.getAuction({ auctionId })?.getFPD()?.global)
.map(getSignals)
.filter(fpd => fpd);
.filter((fpd) => fpd);

return signals;
}
Expand All @@ -40,33 +67,35 @@ export function getSignalsIntersection(signals) {
const result = {};
taxonomies.forEach((taxonomy) => {
const allValues = signals
.flatMap(x => x)
.filter(x => x.taxonomy === taxonomy)
.map(x => x.values);
result[taxonomy] = allValues.length ? (
allValues.reduce((commonElements, subArray) => {
return commonElements.filter(element => subArray.includes(element));
})
) : []
.flatMap((x) => x)
.filter((x) => x.taxonomy === taxonomy)
.map((x) => x.values);
result[taxonomy] = allValues.length
? allValues.reduce((commonElements, subArray) => {
return commonElements.filter((element) => subArray.includes(element));
})
: [];
result[taxonomy] = { values: result[taxonomy] };
})
});
return result;
}

export function getAuctionsIdsFromTargeting(targeting, am = auctionManager) {
return Object.values(targeting)
.flatMap(x => Object.entries(x))
.filter((entry) => entry[0] === TARGETING_KEYS.AD_ID || entry[0].startsWith(TARGETING_KEYS.AD_ID + '_'))
.flatMap(entry => entry[1])
.map(adId => am.findBidByAdId(adId)?.auctionId)
.filter(id => id != null)
.flatMap((x) => Object.entries(x))
.filter(
(entry) =>
entry[0] === TARGETING_KEYS.AD_ID ||
entry[0].startsWith(TARGETING_KEYS.AD_ID + "_")
)
.flatMap((entry) => entry[1])
.map((adId) => am.findBidByAdId(adId)?.auctionId)
.filter((id) => id != null)
.filter(uniques);
}

export const appendGptSlots = adUnits => {
const { customGptSlotMatching } = _currentConfig;

if (!isGptPubadsDefined()) {
export const appendGptSlots = (adUnits) => {
if (!adpSlots) {
return;
}

Expand All @@ -76,52 +105,51 @@ export const appendGptSlots = adUnits => {
return acc;
}, {});

const adUnitPaths = {};

window.googletag.pubads().getSlots().forEach(slot => {
const matchingAdUnitCode = find(Object.keys(adUnitMap), customGptSlotMatching
? customGptSlotMatching(slot)
: isAdUnitCodeMatchingSlot(slot));

for (adUnit in adpSlots) {
const matchingAdUnitCode = Object.keys(adUnitMap).find(
(key) => key === adUnit
);
if (matchingAdUnitCode) {
const path = adUnitPaths[matchingAdUnitCode] = slot.getAdUnitPath();
const adserver = {
name: 'gam',
adslot: sanitizeSlotPath(path)
name: "gam",
adslot: sanitizeSlotPath(getAdUnitPath(adUnit)),
};
adUnitMap[matchingAdUnitCode].forEach((adUnit) => {
deepSetValue(adUnit, 'ortb2Imp.ext.data.adserver', Object.assign({}, adUnit.ortb2Imp?.ext?.data?.adserver, adserver));
deepSetValue(
adUnit,
"ortb2Imp.ext.data.adserver",
Object.assign({}, adUnit.ortb2Imp?.ext?.data?.adserver, adserver)
);
});
}
});
return adUnitPaths;
}
};

const sanitizeSlotPath = (path) => {
const gptConfig = config.getConfig('gptPreAuction') || {};
const gptConfig = config.getConfig("gptPreAuction") || {};

if (gptConfig.mcmEnabled) {
return path.replace(/(^\/\d*),\d*\//, '$1/');
return path.replace(/(^\/\d*),\d*\//, "$1/");
}

return path;
}
};

const defaultPreAuction = (adUnit, adServerAdSlot, adUnitPath) => {
const defaultPreAuction = (adUnit, adServerAdSlot) => {
const context = adUnit.ortb2Imp.ext.data;

// use pbadslot if supplied
if (context.pbadslot) {
return context.pbadslot;
}

// confirm that GPT is set up
if (!isGptPubadsDefined()) {
if (!adpSlots) {
return;
}

// find all GPT slots with this name
var gptSlots = window.googletag.pubads().getSlots().filter(slot => slot.getAdUnitPath() === adUnitPath);
var gptSlots = Object.keys(adpSlots).filter(
(slot) => getAdUnitPath(slot) === adServerAdSlot
);

if (gptSlots.length === 0) {
return; // should never happen
Expand All @@ -133,9 +161,9 @@ const defaultPreAuction = (adUnit, adServerAdSlot, adUnitPath) => {

// else the adunit code must be div id. append it.
return `${adServerAdSlot}#${adUnit.code}`;
}
};

export const appendPbAdSlot = adUnit => {
export const appendPbAdSlot = (adUnit) => {
const context = adUnit.ortb2Imp.ext.data;
const { customPbAdSlot } = _currentConfig;

Expand All @@ -145,7 +173,10 @@ export const appendPbAdSlot = adUnit => {
}

if (customPbAdSlot) {
context.pbadslot = customPbAdSlot(adUnit.code, deepAccess(context, 'adserver.adslot'));
context.pbadslot = customPbAdSlot(
adUnit.code,
deepAccess(context, "adserver.adslot")
);
return;
}

Expand All @@ -158,7 +189,7 @@ export const appendPbAdSlot = adUnit => {
}
} catch (e) {}
// banner adUnit, use GPT adunit if defined
if (deepAccess(context, 'adserver.adslot')) {
if (deepAccess(context, "adserver.adslot")) {
context.pbadslot = context.adserver.adslot;
return;
}
Expand All @@ -167,13 +198,16 @@ export const appendPbAdSlot = adUnit => {
};

function warnDeprecation(adUnit) {
logWarn(`pbadslot is deprecated and will soon be removed, use gpid instead`, adUnit)
logWarn(
`pbadslot is deprecated and will soon be removed, use gpid instead`,
adUnit
);
}

export const makeBidRequestsHook = (fn, adUnits, ...args) => {
const adUnitPaths = appendGptSlots(adUnits);
appendGptSlots(adUnits);
const { useDefaultPreAuction, customPreAuction } = _currentConfig;
adUnits.forEach(adUnit => {
adUnits.forEach((adUnit) => {
// init the ortb2Imp if not done yet
adUnit.ortb2Imp = adUnit.ortb2Imp || {};
adUnit.ortb2Imp.ext = adUnit.ortb2Imp.ext || {};
Expand All @@ -191,12 +225,16 @@ export const makeBidRequestsHook = (fn, adUnits, ...args) => {
if (context.data?.pbadslot) {
warnDeprecation(adUnit);
}
let adserverSlot = deepAccess(context, 'data.adserver.adslot');
let adserverSlot = deepAccess(context, "data.adserver.adslot");
let result;
if (customPreAuction) {
result = customPreAuction(adUnit, adserverSlot, adUnitPaths?.[adUnit.code]);
result = customPreAuction(
adUnit,
adserverSlot,
adUnitPaths?.[adUnit.code]
);
} else if (useDefaultPreAuction) {
result = defaultPreAuction(adUnit, adserverSlot, adUnitPaths?.[adUnit.code]);
result = defaultPreAuction(adUnit, adserverSlot);
}
if (result) {
context.gpid = context.data.pbadslot = result;
Expand All @@ -209,35 +247,48 @@ export const makeBidRequestsHook = (fn, adUnits, ...args) => {
const setPpsConfigFromTargetingSet = (next, targetingSet) => {
// set gpt config
const auctionsIds = getAuctionsIdsFromTargeting(targetingSet);
const signals = getSignalsIntersection(getSignalsArrayByAuctionsIds(auctionsIds));
window.googletag.setConfig && window.googletag.setConfig({pps: { taxonomies: signals }});
const signals = getSignalsIntersection(
getSignalsArrayByAuctionsIds(auctionsIds)
);
window.googletag.setConfig &&
window.googletag.setConfig({ pps: { taxonomies: signals } });
next(targetingSet);
};

const handleSetGptConfig = moduleConfig => {
const handleSetGptConfig = (moduleConfig) => {
_currentConfig = pick(moduleConfig, [
'enabled', enabled => enabled !== false,
'customGptSlotMatching', customGptSlotMatching =>
typeof customGptSlotMatching === 'function' && customGptSlotMatching,
'customPbAdSlot', customPbAdSlot => typeof customPbAdSlot === 'function' && customPbAdSlot,
'customPreAuction', customPreAuction => typeof customPreAuction === 'function' && customPreAuction,
'useDefaultPreAuction', useDefaultPreAuction => useDefaultPreAuction ?? true,
"enabled",
(enabled) => enabled !== false,
"customGptSlotMatching",
(customGptSlotMatching) =>
typeof customGptSlotMatching === "function" && customGptSlotMatching,
"customPbAdSlot",
(customPbAdSlot) => typeof customPbAdSlot === "function" && customPbAdSlot,
"customPreAuction",
(customPreAuction) =>
typeof customPreAuction === "function" && customPreAuction,
"useDefaultPreAuction",
(useDefaultPreAuction) => useDefaultPreAuction ?? true,
]);

if (_currentConfig.enabled) {
if (!hooksAdded) {
getHook('makeBidRequests').before(makeBidRequestsHook);
getHook('targetingDone').after(setPpsConfigFromTargetingSet)
getHook("makeBidRequests").before(makeBidRequestsHook);
getHook("targetingDone").after(setPpsConfigFromTargetingSet);
hooksAdded = true;
}
} else {
logInfo(`${MODULE_NAME}: Turning off module`);
_currentConfig = {};
getHook('makeBidRequests').getHooks({hook: makeBidRequestsHook}).remove();
getHook('targetingDone').getHooks({hook: setPpsConfigFromTargetingSet}).remove();
getHook("makeBidRequests").getHooks({ hook: makeBidRequestsHook }).remove();
getHook("targetingDone")
.getHooks({ hook: setPpsConfigFromTargetingSet })
.remove();
hooksAdded = false;
}
};

config.getConfig('gptPreAuction', config => handleSetGptConfig(config.gptPreAuction));
config.getConfig("gptPreAuction", (config) =>
handleSetGptConfig(config.gptPreAuction)
);
handleSetGptConfig({});