Skip to content

Commit

Permalink
Merge pull request #8 from jwplayer/feat/AD-1583
Browse files Browse the repository at this point in the history
[AD-1583] Allow publisher to specify tracking urls
  • Loading branch information
karimMourra authored Nov 3, 2021
2 parents 00977b0 + e55d127 commit 6489b13
Show file tree
Hide file tree
Showing 6 changed files with 625 additions and 6 deletions.
53 changes: 51 additions & 2 deletions modules/videoModule/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@ import { allVideoEvents } from './constants/events.js';
import CONSTANTS from '../../src/constants.json';
import { videoCoreFactory } from './coreVideo.js';
import { coreAdServerFactory } from './adServer.js';
import find from 'core-js-pure/features/array/find.js';
import { vastXmlEditorFactory } from './shared/vastXmlEditor.js';

events.addEvents(allVideoEvents);

export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_, adServerCore_) {
export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvents_, adServerCore_, vastXmlEditor_) {
const videoCore = videoCore_;
const getConfig = getConfig_;
const pbGlobal = pbGlobal_;
const requestBids = pbGlobal.requestBids;
const pbEvents = pbEvents_;
const videoEvents = videoEvents_;
const adServerCore = adServerCore_;
const vastXmlEditor = vastXmlEditor_;

function init() {
getConfig('video', ({ video }) => {
Expand Down Expand Up @@ -42,6 +45,20 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent
}
});
});

const cache = getConfig('cache');
if (!cache) {
return;
}

pbEvents.on(CONSTANTS.EVENTS.BID_ADJUSTMENT, function (bid) {
const adUnitCode = bid.adUnitCode;
const adUnit = find(pbGlobal.adUnits, adUnit => adUnitCode === adUnit.code);
const videoConfig = adUnit && adUnit.video;
const adServerConfig = videoConfig && videoConfig.adServer;
const trackingConfig = adServerConfig && adServerConfig.tracking;
addTrackingNodesToVastXml(bid, trackingConfig);
});
}

return { init };
Expand Down Expand Up @@ -81,12 +98,44 @@ export function PbVideo(videoCore_, getConfig_, pbGlobal_, pbEvents_, videoEvent
options.adXml = highestBid.vastXml;
videoCore.setAdTagUrl(adTagUrl, divId, options);
}

function addTrackingNodesToVastXml(bid, trackingConfig) {
if (!trackingConfig) {
return;
}

let { vastXml, vastUrl, adId } = bid;
let impressionUrl;
let impressionId;
let errorUrl;

const impressionTracking = trackingConfig.impression;
const errorTracking = trackingConfig.error;

if (impressionTracking) {
impressionUrl = impressionTracking.url;
impressionId = impressionTracking.id || adId + '-impression';
}

if (errorTracking) {
errorUrl = errorTracking.url;
}

if (vastXml) {
vastXml = vastXmlEditor.getVastXmlWithTrackingNodes(vastXml, impressionUrl, impressionId, errorUrl);
} else if (vastUrl) {
vastXml = vastXmlEditor.buildVastWrapper(adId, vastUrl, impressionUrl, impressionId, errorUrl);
}

bid.vastXml = vastXml;
}
}

export function pbVideoFactory() {
const videoCore = videoCoreFactory();
const adServerCore = coreAdServerFactory();
const pbVideo = PbVideo(videoCore, config.getConfig, $$PREBID_GLOBAL$$, events, allVideoEvents, adServerCore);
const vastXmlEditor = vastXmlEditorFactory();
const pbVideo = PbVideo(videoCore, config.getConfig, $$PREBID_GLOBAL$$, events, allVideoEvents, adServerCore, vastXmlEditor);
pbVideo.init();
return pbVideo;
}
76 changes: 76 additions & 0 deletions modules/videoModule/shared/vastXmlBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@

export function buildVastWrapper(adId, adTagUrl, impressionUrl, impressionId, errorUrl) {
let wrapperBody = getAdSystemNode('Prebid org', $$PREBID_GLOBAL$$.version);

if (adTagUrl) {
wrapperBody += getAdTagUriNode(adTagUrl);
}

if (impressionUrl) {
wrapperBody += getImpressionNode(impressionUrl, impressionId);
}

if (errorUrl) {
wrapperBody += getErrorNode(errorUrl);
}

return getVastNode(getAdNode(getWrapperNode(wrapperBody), adId), '4.2');
}

export function getVastNode(body, vastVersion) {
return getNode('VAST', body, { version: vastVersion });
}

export function getAdNode(body, adId) {
return getNode('Ad', body, { id: adId });
}

export function getWrapperNode(body) {
return getNode('Wrapper', body);
}

export function getAdSystemNode(system, version) {
return getNode('AdSystem', system, { version });
}

export function getAdTagUriNode(adTagUrl) {
return getUrlNode('VASTAdTagURI', adTagUrl);
}

export function getImpressionNode(pingUrl, id) {
return getUrlNode('Impression', pingUrl, { id });
}

export function getErrorNode(pingUrl) {
return getUrlNode('Error', pingUrl);
}

// Helpers

function getUrlNode(labelName, url, attributes) {
const body = `<![CDATA[${url}]]>`;
return getNode(labelName, body, attributes);
}

function getNode(labelName, body, attributes) {
const openingLabel = getOpeningLabel(labelName, attributes);
return `<${openingLabel}>${body}</${labelName}>`;
}

/*
attributes is a KVP Object.
*/
function getOpeningLabel(name, attributes) {
if (!attributes) {
return name;
}

return Object.keys(attributes).reduce((label, key) => {
const value = attributes[key];
if (!value) {
return label;
}

return label + ` ${key}="${value}"`;
}, name);
}
98 changes: 98 additions & 0 deletions modules/videoModule/shared/vastXmlEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { getErrorNode, getImpressionNode, buildVastWrapper } from './vastXmlBuilder.js';

export const XML_MIME_TYPE = 'application/xml';

export function VastXmlEditor(xmlUtil_) {
const xmlUtil = xmlUtil_;

function getVastXmlWithTrackingNodes(vastXml, impressionUrl, impressionId, errorUrl) {
const impressionDoc = getImpressionDoc(impressionUrl, impressionId);
const errorDoc = getErrorDoc(errorUrl);
if (!impressionDoc && !errorDoc) {
return vastXml;
}

const vastXmlDoc = xmlUtil.parse(vastXml);
const nodes = vastXmlDoc.querySelectorAll('InLine,Wrapper');
const nodeCount = nodes.length;
for (let i = 0; i < nodeCount; i++) {
const node = nodes[i];
// A copy of the child is required until we reach the last node.
const requiresCopy = i < nodeCount - 1;
appendChild(node, impressionDoc, requiresCopy);
appendChild(node, errorDoc, requiresCopy);
}

return xmlUtil.serialize(vastXmlDoc);
}

return {
getVastXmlWithTrackingNodes,
buildVastWrapper
}

function getImpressionDoc(impressionUrl, impressionId) {
if (!impressionUrl) {
return;
}

const impressionNode = getImpressionNode(impressionUrl, impressionId);
return xmlUtil.parse(impressionNode);
}

function getErrorDoc(errorUrl) {
if (!errorUrl) {
return;
}

const errorNode = getErrorNode(errorUrl);
return xmlUtil.parse(errorNode);
}

function appendChild(node, child, copy) {
if (!child) {
return;
}

const doc = copy ? child.cloneNode(true) : child;
node.appendChild(doc.documentElement);
}
}

function XMLUtil() {
let parser;
let serializer;

function getParser() {
if (!parser) {
// DOMParser instantiation is costly; instantiate only once throughout Prebid lifecycle.
parser = new DOMParser();
}
return parser;
}

function getSerializer() {
if (!serializer) {
// XMLSerializer instantiation is costly; instantiate only once throughout Prebid lifecycle.
serializer = new XMLSerializer();
}
return serializer;
}

function parse(xmlString) {
return getParser().parseFromString(xmlString, XML_MIME_TYPE);
}

function serialize(xmlDoc) {
return getSerializer().serializeToString(xmlDoc);
}

return {
parse,
serialize
};
}

export function vastXmlEditorFactory() {
return VastXmlEditor(XMLUtil());
}
Loading

0 comments on commit 6489b13

Please sign in to comment.