Skip to content

Commit

Permalink
Added SupplyChain Object support and an onTimeout Callback (prebid#4137)
Browse files Browse the repository at this point in the history
* - Implemented the 'onTimeout' callback to fire a pixel when there's a timeout.
- Added the ability to serialize an schain object according to the description provided here: https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/supplychainobject.md

* some mods to the schain tag generation

* - added tests for schain param checking.

* - fixed a malformed url for timeouts

* - Removed a trailing ',' while generating a schain param.
  • Loading branch information
telariaEng authored and Mike Chowla committed Sep 3, 2019
1 parent 80cbd2c commit e61b246
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 28 deletions.
126 changes: 101 additions & 25 deletions modules/telariaBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import {VIDEO} from '../src/mediaTypes';
import {STATUS} from '../src/constants';

const BIDDER_CODE = 'telaria';
const ENDPOINT = '.ads.tremorhub.com/ad/tag';
const DOMAIN = 'tremorhub.com';
const TAG_ENDPOINT = `ads.${DOMAIN}/ad/tag`;
const EVENTS_ENDPOINT = `events.${DOMAIN}/diag`;

export const spec = {
code: BIDDER_CODE,
Expand Down Expand Up @@ -82,7 +84,7 @@ export const spec = {
errorMessage += `: ${bidResult.error}`;
}
utils.logError(errorMessage);
} else if (bidResult.seatbid && bidResult.seatbid.length > 0) {
} else if (!utils.isEmpty(bidResult.seatbid)) {
bidResult.seatbid[0].bid.forEach(tag => {
bids.push(createBid(STATUS.GOOD, bidderRequest, tag, width, height, BIDDER_CODE));
});
Expand All @@ -100,11 +102,89 @@ export const spec = {
getUserSyncs: function (syncOptions, serverResponses) {
const syncs = [];
if (syncOptions.pixelEnabled && serverResponses.length) {
try {
serverResponses[0].body.ext.telaria.userSync.forEach(url => syncs.push({type: 'image', url: url}));
} catch (e) {}
(utils.deepAccess(serverResponses, '0.body.ext.telaria.userSync') || []).forEach(url => syncs.push({type: 'image', url: url}));
}
return syncs;
},

/**
* See http://prebid.org/dev-docs/bidder-adaptor.html#registering-on-timeout for detailed semantic.
* @param timeoutData bidRequest
*/
onTimeout: function (timeoutData) {
let url = getTimeoutUrl(timeoutData);
if (url) {
utils.triggerPixel(url);
}
}
};

function getScheme() {
return ((document.location.protocol === 'https:') ? 'https' : 'http') + '://';
}

function getSrcPageUrl(params) {
return (params && params['srcPageUrl']) || encodeURIComponent(document.location.href);
}

function getEncodedValIfNotEmpty(val) {
return !utils.isEmpty(val) ? encodeURIComponent(val) : '';
}

/**
* Converts the schain object to a url param value. Please refer to
* https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/supplychainobject.md
* (schain for non ORTB section) for more information
* @param schainObject
* @returns {string}
*/
function getSupplyChainAsUrlParam(schainObject) {
if (utils.isEmpty(schainObject)) {
return '';
}

let scStr = `&schain=${schainObject.ver},${schainObject.complete}`;

schainObject.nodes.forEach((node) => {
scStr += '!';
scStr += `${getEncodedValIfNotEmpty(node.asi)},`;
scStr += `${getEncodedValIfNotEmpty(node.sid)},`;
scStr += `${getEncodedValIfNotEmpty(node.hp)},`;
scStr += `${getEncodedValIfNotEmpty(node.rid)},`;
scStr += `${getEncodedValIfNotEmpty(node.name)},`;
scStr += `${getEncodedValIfNotEmpty(node.domain)}`;
});

return scStr;
}

function getUrlParams(params) {
let urlSuffix = '';

if (!utils.isEmpty(params)) {
for (let key in params) {
if (key !== 'schain' && params.hasOwnProperty(key) && !utils.isEmpty(params[key])) {
urlSuffix += `&${key}=${params[key]}`;
}
}
urlSuffix += getSupplyChainAsUrlParam(params['schain']);
}

return urlSuffix;
}

export const getTimeoutUrl = function(timeoutData) {
let params = utils.deepAccess(timeoutData, '0.params.0');

if (!utils.isEmpty(params)) {
let url = `${getScheme()}${EVENTS_ENDPOINT}`;

url += `?srcPageUrl=${getSrcPageUrl(params)}`;
url += `${getUrlParams(params)}`;

url += '&hb=1&evt=TO';

return url;
}
};

Expand All @@ -116,9 +196,9 @@ export const spec = {
* @returns {string}
*/
function generateUrl(bid, bidderRequest) {
let playerSize = (bid.mediaTypes && bid.mediaTypes.video && bid.mediaTypes.video.playerSize);
let playerSize = utils.deepAccess(bid, 'mediaTypes.video.playerSize');
if (!playerSize) {
utils.logWarn('Although player size isn\'t required it is highly recommended');
utils.logWarn(`Although player size isn't required it is highly recommended`);
}

let width, height;
Expand All @@ -132,45 +212,41 @@ function generateUrl(bid, bidderRequest) {
}
}

if (bid.params.supplyCode && bid.params.adCode) {
let scheme = ((document.location.protocol === 'https:') ? 'https' : 'http') + '://';
let url = scheme + bid.params.supplyCode + ENDPOINT + '?adCode=' + bid.params.adCode;
let supplyCode = utils.deepAccess(bid, 'params.supplyCode');
let adCode = utils.deepAccess(bid, 'params.adCode');

if (supplyCode && adCode) {
let url = `${getScheme()}${supplyCode}.${TAG_ENDPOINT}?adCode=${adCode}`;

if (width) {
url += ('&playerWidth=' + width);
url += (`&playerWidth=${width}`);
}
if (height) {
url += ('&playerHeight=' + height);
url += (`&playerHeight=${height}`);
}

for (let key in bid.params) {
if (bid.params.hasOwnProperty(key) && bid.params[key]) {
url += ('&' + key + '=' + bid.params[key]);
}
}
url += `${getUrlParams(bid.params)}`;

if (!bid.params['srcPageUrl']) {
url += ('&srcPageUrl=' + encodeURIComponent(document.location.href));
}
url += `&srcPageUrl=${getSrcPageUrl(bid.params)}`;

url += ('&transactionId=' + bid.transactionId + '&hb=1');
url += (`&transactionId=${bid.transactionId}`);

if (bidderRequest) {
if (bidderRequest.gdprConsent) {
if (typeof bidderRequest.gdprConsent.gdprApplies === 'boolean') {
url += ('&gdpr=' + (bidderRequest.gdprConsent.gdprApplies ? 1 : 0));
url += (`&gdpr=${(bidderRequest.gdprConsent.gdprApplies ? 1 : 0)}`);
}
if (bidderRequest.gdprConsent.consentString) {
url += ('&gdpr_consent=' + bidderRequest.gdprConsent.consentString);
url += (`&gdpr_consent=${bidderRequest.gdprConsent.consentString}`);
}
}

if (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
url += ('&referrer=' + encodeURIComponent(bidderRequest.refererInfo.referer));
url += (`&referrer=${encodeURIComponent(bidderRequest.refererInfo.referer)}`);
}
}

return (url + '&fmt=json');
return (url + '&hb=1&fmt=json');
}
}

Expand Down
69 changes: 66 additions & 3 deletions test/spec/modules/telariaBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {expect} from 'chai';
import {expect, should} from 'chai';
import {newBidder} from 'src/adapters/bidderFactory';
import {spec} from 'modules/telariaBidAdapter';
import {spec, getTimeoutUrl} from 'modules/telariaBidAdapter';

const ENDPOINT = '.ads.tremorhub.com/ad/tag';
const AD_CODE = 'ssp-!demo!-lufip';
Expand All @@ -16,14 +16,44 @@ const REQUEST = {
},
'mediaType': 'video',
'bids': [{
'bidder': 'tremor',
'bidder': 'telaria',
'params': {
'videoId': 'MyCoolVideo',
'inclSync': true
}
}]
};

const REQUEST_WITH_SCHAIN = [{
'bidder': 'telaria',
'params': {
'videoId': 'MyCoolVideo',
'inclSync': true,
'schain': {
'ver': '1.0',
'complete': 1,
'nodes': [
{
'asi': 'exchange1.com',
'sid': '1234',
'hp': 1,
'rid': 'bid-request-1',
'name': 'publisher',
'domain': 'publisher.com'
},
{
'asi': 'exchange2.com',
'sid': 'abcd',
'hp': 1,
'rid': 'bid-request-2',
'name': 'intermediary',
'domain': 'intermediary.com'
}
]
}
}
}];

const BIDDER_REQUEST = {
'refererInfo': {
'referer': 'www.test.com'
Expand Down Expand Up @@ -102,6 +132,8 @@ describe('TelariaAdapter', () => {
}
}];

const schainStub = REQUEST_WITH_SCHAIN;

it('exists and is a function', () => {
expect(spec.buildRequests).to.exist.and.to.be.a('function');
});
Expand Down Expand Up @@ -147,6 +179,14 @@ describe('TelariaAdapter', () => {

expect(tempRequest.length).to.equal(0);
});

it('converts the schain object into a tag param', () => {
let tempBid = schainStub;
tempBid[0].params.adCode = 'ssp-!demo!-lufip';
tempBid[0].params.supplyCode = 'ssp-demo-rm6rh';
let builtRequests = spec.buildRequests(tempBid, BIDDER_REQUEST);
expect(builtRequests.length).to.equal(1);
});
});

describe('interpretResponse', () => {
Expand Down Expand Up @@ -215,4 +255,27 @@ describe('TelariaAdapter', () => {
expect(urls.length).to.equal(2);
});
});

describe('onTimeout', () => {
const timeoutData = [{
adUnitCode: 'video1',
auctionId: 'd8d239f4-303a-4798-8c8c-dd3151ced4e7',
bidId: '2c749c0101ea92',
bidder: 'telaria',
params: [{
adCode: 'ssp-!demo!-lufip',
supplyCode: 'ssp-demo-rm6rh',
mediaId: 'MyCoolVideo'
}]
}];

it('should return a pixel url', () => {
let url = getTimeoutUrl(timeoutData);
assert(url);
});

it('should fire a pixel', () => {
expect(spec.onTimeout(timeoutData)).to.be.undefined;
});
});
});

0 comments on commit e61b246

Please sign in to comment.