Skip to content

Commit

Permalink
AdagioAnalyticsAdapter: add bdrs_timeout, adsrv, adsrv_empty (#12281)
Browse files Browse the repository at this point in the history
  • Loading branch information
System-Glitch authored Oct 1, 2024
1 parent d807678 commit fd79aaa
Show file tree
Hide file tree
Showing 3 changed files with 293 additions and 2 deletions.
64 changes: 64 additions & 0 deletions libraries/gptUtils/gptUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,67 @@ export function getSegments(fpd, sections, segtax) {
.filter(ob => ob)
.filter(uniques)
}

/**
* Add an event listener on the given GAM event.
* If GPT Pubads isn't defined, window.googletag is set to a new object.
* @param {String} event
* @param {Function} callback
*/
export function subscribeToGamEvent(event, callback) {
const register = () => window.googletag.pubads().addEventListener(event, callback);
if (isGptPubadsDefined()) {
register();
return;
}
window.googletag = window.googletag || {};
window.googletag.cmd = window.googletag.cmd || [];
window.googletag.cmd.push(register);
}

/**
* @typedef {Object} Slot
* @property {function(String): (String|null)} get
* @property {function(): String} getAdUnitPath
* @property {function(): String[]} getAttributeKeys
* @property {function(): String[]} getCategoryExclusions
* @property {function(String): String} getSlotElementId
* @property {function(): String[]} getTargeting
* @property {function(): String[]} getTargetingKeys
* @see {@link https://developers.google.com/publisher-tag/reference#googletag.Slot GPT official docs}
*/

/**
* @typedef {Object} SlotRenderEndedEvent
* @property {(String|null)} advertiserId
* @property {(String|null)} campaignId
* @property {(String[]|null)} companyIds
* @property {(Number|null)} creativeId
* @property {(Number|null)} creativeTemplateId
* @property {(Boolean)} isBackfill
* @property {(Boolean)} isEmpty
* @property {(Number[]|null)} labelIds
* @property {(Number|null)} lineItemId
* @property {(String)} serviceName
* @property {(string|Number[]|null)} size
* @property {(Slot)} slot
* @property {(Boolean)} slotContentChanged
* @property {(Number|null)} sourceAgnosticCreativeId
* @property {(Number|null)} sourceAgnosticLineItemId
* @property {(Number[]|null)} yieldGroupIds
* @see {@link https://developers.google.com/publisher-tag/reference#googletag.events.SlotRenderEndedEvent GPT official docs}
*/

/**
* @callback SlotRenderEndedEventCallback
* @param {SlotRenderEndedEvent} event
* @returns {void}
*/

/**
* Add an event listener on the GAM event 'slotRenderEnded'.
* @param {SlotRenderEndedEventCallback} callback
*/
export function subscribeToGamSlotRenderEndedEvent(callback) {
subscribeToGamEvent('slotRenderEnded', callback)
}
72 changes: 70 additions & 2 deletions modules/adagioAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import adapter from '../libraries/analyticsAdapter/AnalyticsAdapter.js';
import adapterManager from '../src/adapterManager.js';
import { ajax } from '../src/ajax.js';
import { getGlobal } from '../src/prebidGlobal.js';
import { subscribeToGamSlotRenderEndedEvent, SlotRenderEndedEvent } from '../libraries/gptUtils/gptUtils.js';

const emptyUrl = '';
const analyticsType = 'endpoint';
Expand All @@ -24,7 +25,8 @@ const ADAGIO_CODE = 'adagio';
export const _internal = {
getAdagioNs: function() {
return _ADAGIO;
}
},
gamSlotCallback
};

const cache = {
Expand Down Expand Up @@ -52,6 +54,18 @@ const cache = {
},
getAdagioAuctionId(auctionId) {
return this.auctionIdReferences[auctionId];
},

// Map adunitcode with prebid auction ID
auctionByAdunit: {},
getAuctionIdByAdunit(adUnitPath, adSlotElementId) {
if (cache.auctionByAdunit[adUnitPath]) {
return { auctionId: cache.auctionByAdunit[adUnitPath], adUnitCode: adUnitPath }
}
if (cache.auctionByAdunit[adSlotElementId]) {
return { auctionId: cache.auctionByAdunit[adSlotElementId], adUnitCode: adSlotElementId }
}
return { auctionId: null, adUnitCode: null }
}
};
const enc = window.encodeURIComponent;
Expand Down Expand Up @@ -269,6 +283,7 @@ function handlerAuctionInit(event) {
}

cache.auctions[prebidAuctionId][adUnitCode] = qp;
cache.auctionByAdunit[adUnitCode] = prebidAuctionId;
sendNewBeacon(prebidAuctionId, adUnitCode);
});
};
Expand Down Expand Up @@ -316,13 +331,18 @@ function handlerAuctionEnd(event) {

const perfNavigation = performance.getEntriesByType('navigation')[0];

const auction = cache.getAuction(auctionId, adUnitCode);
const bdrs = auction.bdrs.split(',');
const bdrsTimeout = auction.bdrs_timeout || [];

cache.updateAuction(auctionId, adUnitCode, {
bdrs_bid: cache.getBiddersFromAuction(auctionId, adUnitCode).map(bidResponseMapper).join(','),
bdrs_cpm: cache.getBiddersFromAuction(auctionId, adUnitCode).map(bidCpmMapper).join(','),
// check timings at the end of the auction to leave time to the browser to update it
dom_i: Math.round(perfNavigation['domInteractive']) || null,
dom_c: Math.round(perfNavigation['domComplete']) || null,
loa_e: Math.round(perfNavigation['loadEventEnd']) || null,
bdrs_timeout: bdrs.map(b => bdrsTimeout.includes(b) ? '1' : '0').join(','),
});

sendNewBeacon(auctionId, adUnitCode);
Expand Down Expand Up @@ -378,6 +398,23 @@ function handlerAdRender(event, isSuccess) {
sendNewBeacon(auctionId, adUnitCode);
};

function handlerBidTimeout(args) {
args.forEach(event => {
const auction = cache.getAuction(event.auctionId, event.adUnitCode);
if (!auction) {
logWarn(`bid timeout on auction ${event.auctionId}, with adunitCode ${event.adUnitCode}: could not retrieve auction from cache`);
return;
}

// an array of bidder names is first created
// in AUCTION_END handler, this array is sorted
// and transformed in a comma-separated list.
const bdrsTimeout = auction.bdrs_timeout || [];
bdrsTimeout.push(event.bidder);
auction.bdrs_timeout = bdrsTimeout;
});
};

/**
* handlerPbsAnalytics add to the cache data coming from Adagio PBS AdResponse.
* The data is retrieved from an AnalyticsTag (set by a custom PBS module named `adg-pba`),
Expand Down Expand Up @@ -409,10 +446,36 @@ function handlerPbsAnalytics(event) {
* END HANDLERS
*/

/**
* @param {SlotRenderEndedEvent} event
* @returns {void}
*/
function gamSlotCallback(event) {
const { auctionId, adUnitCode } = cache.getAuctionIdByAdunit(event.slot.getAdUnitPath(), event.slot.getSlotElementId());
if (!auctionId) {
const slotName = `${event.slot.getAdUnitPath()} - ${event.slot.getSlotElementId()}`;
logWarn('Could not find configured ad unit matching GAM render of slot: ' + slotName);
return;
}

cache.updateAuction(auctionId, adUnitCode, {
adsrv: 'gam',
adsrv_empty: event.isEmpty
});

// This event can be triggered after AUCTION_END
// To make sure the data is sent, we must send a new beacon version.
const auction = cache.getAuction(auctionId, adUnitCode)
if (auction?.loa_e !== undefined) {
// loa_e = loadEventEnd
// It means the AUCTION_END has already been sent.
sendNewBeacon(auctionId, adUnitCode);
}
}

let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), {
track: function(event) {
const { eventType, args } = event;

try {
switch (eventType) {
case EVENTS.AUCTION_INIT:
Expand All @@ -435,6 +498,9 @@ let adagioAdapter = Object.assign(adapter({ emptyUrl, analyticsType }), {
case EVENTS.PBS_ANALYTICS:
handlerPbsAnalytics(args);
break;
case EVENTS.BID_TIMEOUT:
handlerBidTimeout(args);
break;
}
} catch (error) {
logError('Error on Adagio Analytics Adapter', error);
Expand Down Expand Up @@ -478,6 +544,8 @@ adagioAdapter.enableAnalytics = config => {
adagioAdapter.options.site = undefined;
}
adagioAdapter.originEnableAnalytics(config);

subscribeToGamSlotRenderEndedEvent(gamSlotCallback)
}

adapterManager.registerAnalyticsAdapter({
Expand Down
Loading

0 comments on commit fd79aaa

Please sign in to comment.