Skip to content

Commit

Permalink
added setTargeting code for custom key-value pairs (prebid#3550)
Browse files Browse the repository at this point in the history
* added setTargeting code for custom key-value pairs

*      v1.4.3 - refactored setTargeting code, includes support for pub common ID being passed inside ext.ozone object, refactored the ext.ozone object to carry any data objects defined in on-page bidder config

* Update to setTargeting functionality per feedback from prebid.org.

* switching from // to https:// to match unit test
  • Loading branch information
afsheenb authored and idettman committed Feb 22, 2019
1 parent 753f76a commit b12e6dd
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 96 deletions.
297 changes: 201 additions & 96 deletions modules/ozoneBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as utils from '../src/utils';
import { registerBidder } from '../src/adapters/bidderFactory';
import { BANNER, NATIVE, VIDEO } from '../src/mediaTypes';
import {config} from '../src/config';

const BIDDER_CODE = 'ozone';

const OZONEURI = 'https://elb.the-ozone-project.com/openrtb2/auction';
const OZONECOOKIESYNC = 'https://elb.the-ozone-project.com/static/load-cookie.html';

const OZONEVERSION = '1.4.4';
export const spec = {
code: BIDDER_CODE,

Expand Down Expand Up @@ -63,59 +65,17 @@ export const spec = {
}
return true;
},
/**
* Interpret the response if the array contains BIDDER elements, in the format: [ [bidder1 bid 1, bidder1 bid 2], [bidder2 bid 1, bidder2 bid 2] ]
* @param serverResponse
* @param request
* @returns {*}
*/
interpretResponse(serverResponse, request) {
utils.logInfo('ozone interpretResponse version 2018-12-05 16:46');
serverResponse = serverResponse.body || {};
if (serverResponse.seatbid) {
if (utils.isArray(serverResponse.seatbid)) {
// serverResponse seems good, let's get the list of bids from the request object:
let arrRequestBids = request.bidderRequest.bids;
// build up a list of winners, one for each bidId in arrBidIds
let arrWinners = [];
for (let i = 0; i < arrRequestBids.length; i++) {
let winner = ozoneGetWinnerForRequestBid(arrRequestBids[i], serverResponse.seatbid);
if (winner !== null) {
const {defaultWidth, defaultHeight} = defaultSize(arrRequestBids[i]);
winner = ozoneAddStandardProperties(winner, defaultWidth, defaultHeight);
arrWinners.push(winner);
}
}
let winnersClean = arrWinners.filter(w => {
return (w.bidId); // will be cast to boolean
});
utils.logInfo(['going to return winnersClean:', winnersClean]);
return winnersClean;
} else {
return [];
}
} else {
return [];
}
},
buildRequests(validBidRequests, bidderRequest) {
let ozoneRequest = validBidRequests[0].params;
ozoneRequest['id'] = utils.generateUUID();
ozoneRequest['auctionId'] = bidderRequest['auctionId'];

if (bidderRequest.hasOwnProperty('placementId')) {
bidderRequest.placementId = (bidderRequest.placementId).toString();
}
if (bidderRequest.hasOwnProperty('siteId')) {
bidderRequest.siteId = (bidderRequest.siteId).toString();
}
if (bidderRequest.hasOwnProperty('publisherId')) {
bidderRequest.publisherId = (bidderRequest.publisherId).toString();
}
utils.logInfo('ozone v' + OZONEVERSION + ' validBidRequests', validBidRequests, 'bidderRequest', bidderRequest);
utils.logInfo('buildRequests setting auctionId', bidderRequest.auctionId);
let singleRequest = config.getConfig('ozone.singleRequest');
singleRequest = singleRequest !== false; // undefined & true will be true
utils.logInfo('config ozone.singleRequest : ', singleRequest);
let htmlParams = validBidRequests[0].params; // the html page config params will be included in each element
let ozoneRequest = {}; // we only want to set specific properties on this, not validBidRequests[0].params
// ozoneRequest['id'] = utils.generateUUID();

// if (!ozoneRequest.test) {
delete ozoneRequest.test; // don't allow test to be set in the config - ONLY use $_GET['pbjs_debug']
// }
if (bidderRequest.gdprConsent) {
ozoneRequest.regs = {};
ozoneRequest.regs.ext = {};
Expand All @@ -126,48 +86,182 @@ export const spec = {
}
let tosendtags = validBidRequests.map(ozoneBidRequest => {
var obj = {};
obj.id = ozoneBidRequest.bidId;
obj.tagid = String(ozoneBidRequest.params.ozoneid);
obj.id = ozoneBidRequest.bidId; // this causes a failure if we change it to something else
// obj.id = ozoneBidRequest.adUnitCode; // (eg. 'mpu' or 'leaderboard') A unique identifier for this impression within the context of the bid request (typically, starts with 1 and increments.
obj.tagid = (ozoneBidRequest.params.placementId).toString();
obj.secure = window.location.protocol === 'https:' ? 1 : 0;
obj.banner = {
topframe: 1,
w: ozoneBidRequest.sizes[0][0] || 0,
h: ozoneBidRequest.sizes[0][1] || 0,
format: ozoneBidRequest.sizes.map(s => {
return {w: s[0], h: s[1]};
})
};
if (ozoneBidRequest.params.hasOwnProperty('customData')) {
obj.customData = ozoneBidRequest.params.customData;
// is there a banner (or nothing declared, so banner is the default)?
let arrBannerSizes = [];
/* NOTE - if there is sizes element in the config root then there will be a mediaTypes.banner element automatically generated for us, so this code is deprecated */
if (!ozoneBidRequest.hasOwnProperty('mediaTypes')) {
if (ozoneBidRequest.hasOwnProperty('sizes')) {
utils.logInfo('no mediaTypes detected - will use the sizes array in the config root');
arrBannerSizes = ozoneBidRequest.sizes;
} else {
utils.logInfo('no mediaTypes detected, no sizes array in the config root either. Cannot set sizes for banner type');
}
} else {
if (ozoneBidRequest.mediaTypes.hasOwnProperty(BANNER)) {
arrBannerSizes = ozoneBidRequest.mediaTypes[BANNER].sizes; /* Note - if there is a sizes element in the config root it will be pushed into here */
utils.logInfo('setting banner size from the mediaTypes.banner element for bidId ' + obj.id + ': ', arrBannerSizes);
}
// Video integration is not complete yet
if (ozoneBidRequest.mediaTypes.hasOwnProperty(VIDEO)) {
obj.video = ozoneBidRequest.mediaTypes[VIDEO];
utils.logInfo('setting video object from the mediaTypes.video element: ' + obj.id + ':', obj.video);
}
// Native integration is not complete yet
if (ozoneBidRequest.mediaTypes.hasOwnProperty(NATIVE)) {
obj.native = ozoneBidRequest.mediaTypes[NATIVE];
utils.logInfo('setting native object from the mediaTypes.native element: ' + obj.id + ':', obj.native);
}
}
if (ozoneBidRequest.params.hasOwnProperty('ozoneData')) {
obj.ozoneData = ozoneBidRequest.params.ozoneData;
// build the banner request using banner sizes we found in either possible location:
if (arrBannerSizes.length > 0) {
obj.banner = {
topframe: 1,
w: arrBannerSizes[0][0] || 0,
h: arrBannerSizes[0][1] || 0,
format: arrBannerSizes.map(s => {
return {w: s[0], h: s[1]};
})
};
}
if (ozoneBidRequest.params.hasOwnProperty('lotameData')) {
obj.lotameData = ozoneBidRequest.params.lotameData;
if (ozoneBidRequest.params.hasOwnProperty('placementId')) {
obj.placementId = (ozoneBidRequest.params.placementId).toString();
}
if (ozoneBidRequest.params.hasOwnProperty('publisherId')) {
obj.publisherId = (ozoneBidRequest.params.publisherId).toString();
}
if (ozoneBidRequest.params.hasOwnProperty('siteId')) {
obj.siteId = (ozoneBidRequest.params.siteId).toString();
}
obj.ext = {'prebid': {'storedrequest': {'id': (ozoneBidRequest.params.placementId).toString()}}};
// build the imp['ext'] object
obj.ext = {'prebid': {'storedrequest': {'id': (ozoneBidRequest.params.placementId).toString()}}, 'ozone': {}};
obj.ext.ozone.adUnitCode = ozoneBidRequest.adUnitCode; // eg. 'mpu'
obj.ext.ozone.transactionId = ozoneBidRequest.transactionId; // this is the transactionId PER adUnit, common across bidders for this unit
if (ozoneBidRequest.params.hasOwnProperty('customData')) {
obj.ext.ozone.customData = ozoneBidRequest.params.customData;
}
if (ozoneBidRequest.params.hasOwnProperty('ozoneData')) {
obj.ext.ozone.ozoneData = ozoneBidRequest.params.ozoneData;
}
if (ozoneBidRequest.params.hasOwnProperty('lotameData')) {
obj.ext.ozone.lotameData = ozoneBidRequest.params.lotameData;
}
if (ozoneBidRequest.hasOwnProperty('crumbs') && ozoneBidRequest.crumbs.hasOwnProperty('pubcid')) {
obj.ext.ozone.pubcid = ozoneBidRequest.crumbs.pubcid;
}
return obj;
});
ozoneRequest.imp = tosendtags;
ozoneRequest.site = {'publisher': {'id': ozoneRequest.publisherId}, 'page': document.location.href};
utils.logInfo('tosendtags = ', tosendtags);

ozoneRequest.site = {'publisher': {'id': htmlParams.publisherId}, 'page': document.location.href};
ozoneRequest.test = parseInt(getTestQuerystringValue()); // will be 1 or 0
var ret = {
method: 'POST',
url: OZONEURI,
data: JSON.stringify(ozoneRequest),
bidderRequest: bidderRequest
};
utils.logInfo(['buildRequests going to return', ret]);
return ret;
// utils.logInfo('_ozoneInternal is', _ozoneInternal);
// return the single request object OR the array:
if (singleRequest) {
utils.logInfo('buildRequests starting to generate response for a single request');
ozoneRequest.id = bidderRequest.auctionId; // Unique ID of the bid request, provided by the exchange.
ozoneRequest.auctionId = bidderRequest.auctionId; // not sure if this should be here?
ozoneRequest.imp = tosendtags;
ozoneRequest.source = {'tid': bidderRequest.auctionId}; // RTB 2.5 : tid is Transaction ID that must be common across all participants in this bid request (e.g., potentially multiple exchanges).
var ret = {
method: 'POST',
url: OZONEURI,
data: JSON.stringify(ozoneRequest),
bidderRequest: bidderRequest
};
utils.logInfo('buildRequests ozoneRequest for single = ', ozoneRequest);
utils.logInfo('buildRequests going to return for single: ', ret);
return ret;
}

// not single request - pull apart the tosendtags array & return an array of objects each containing one element in the imp array.
let arrRet = tosendtags.map(imp => {
utils.logInfo('buildRequests starting to generate non-single response, working on imp : ', imp);
let ozoneRequestSingle = Object.assign({}, ozoneRequest);
imp.ext.ozone.pageAuctionId = bidderRequest['auctionId']; // make a note in the ext object of what the original auctionId was, in the bidderRequest object
ozoneRequestSingle.id = imp.ext.ozone.transactionId; // Unique ID of the bid request, provided by the exchange.
ozoneRequestSingle.auctionId = imp.ext.ozone.transactionId; // not sure if this should be here?
ozoneRequestSingle.imp = [imp];
ozoneRequestSingle.source = {'tid': imp.ext.ozone.transactionId};
utils.logInfo('buildRequests ozoneRequestSingle (for non-single) = ', ozoneRequestSingle);
return {
method: 'POST',
url: OZONEURI,
data: JSON.stringify(ozoneRequestSingle),
bidderRequest: bidderRequest
};
});
utils.logInfo('buildRequests going to return for non-single: ', arrRet);
return arrRet;
},
/**
* Interpret the response if the array contains BIDDER elements, in the format: [ [bidder1 bid 1, bidder1 bid 2], [bidder2 bid 1, bidder2 bid 2] ]
* NOte that in singleRequest mode this will be called once, else it will be called for each adSlot's response
* @param serverResponse
* @param request
* @returns {*}
*/
interpretResponse(serverResponse, request) {
utils.logInfo('ozone v' + OZONEVERSION + ' interpretResponse', serverResponse, request);
serverResponse = serverResponse.body || {};
if (serverResponse.seatbid) {
if (utils.isArray(serverResponse.seatbid)) {
// serverResponse seems good, let's get the list of bids from the request object:
let arrRequestBids = request.bidderRequest.bids;
// build up a list of winners, one for each bidId in arrBidIds
let arrWinners = [];
for (let i = 0; i < arrRequestBids.length; i++) {
let thisBid = arrRequestBids[i];
let ozoneInternalKey = thisBid.bidId;
let {seat: winningSeat, bid: winningBid} = ozoneGetWinnerForRequestBid(thisBid, serverResponse.seatbid);

if (winningBid == null) {
utils.logInfo('FAILED to get winning bid for bid : ', thisBid, 'will skip. Possibly a non-single request, which will be missing some bid IDs');
continue;
}

const {defaultWidth, defaultHeight} = defaultSize(arrRequestBids[i]);
winningBid = ozoneAddStandardProperties(winningBid, defaultWidth, defaultHeight);

utils.logInfo('Going to add the adserverTargeting custom parameters for key: ', ozoneInternalKey);
let adserverTargeting = {};
let allBidsForThisBidid = ozoneGetAllBidsForBidId(ozoneInternalKey, serverResponse.seatbid);
// add all the winning & non-winning bids for this bidId:
Object.keys(allBidsForThisBidid).forEach(function(bidderName, index, ar2) {
utils.logInfo('inside allBidsForThisBidid:foreach', bidderName, index, ar2, allBidsForThisBidid);
adserverTargeting['oz_' + bidderName] = bidderName;
adserverTargeting['oz_' + bidderName + '_pb'] = String(allBidsForThisBidid[bidderName].price);
adserverTargeting['oz_' + bidderName + '_crid'] = String(allBidsForThisBidid[bidderName].crid);
adserverTargeting['oz_' + bidderName + '_adv'] = String(allBidsForThisBidid[bidderName].adomain);
adserverTargeting['oz_' + bidderName + '_imp_id'] = String(allBidsForThisBidid[bidderName].impid);
});
// now add the winner data:
adserverTargeting['oz_auc_id'] = String(request.bidderRequest.auctionId);
adserverTargeting['oz_winner'] = String(winningSeat);
adserverTargeting['oz_winner_auc_id'] = String(winningBid.id);
adserverTargeting['oz_winner_imp_id'] = String(winningBid.impid);
adserverTargeting['oz_response_id'] = String(serverResponse.id);

winningBid.adserverTargeting = adserverTargeting;
utils.logInfo('winner is', winningBid);
arrWinners.push(winningBid);
utils.logInfo('arrWinners is', arrWinners);
}
let winnersClean = arrWinners.filter(w => {
return (w.bidId); // will be cast to boolean
});
utils.logInfo('going to return winnersClean:', winnersClean);
return winnersClean;
} else {
return [];
}
} else {
return [];
}
},
getUserSyncs(optionsType, serverResponse) {
if (!serverResponse || serverResponse.length === 0) {
return [];
Expand All @@ -180,22 +274,6 @@ export const spec = {
}
}
}

/**
* Function matchRequest(id: string, BidRequest: object)
* @param id
* @type string
* @param bidRequest
* @type Object
* @returns Object
*
*/
export function matchRequest(id, bidRequest) {
const {bids} = bidRequest.bidderRequest;
const [returnValue] = bids.filter(bid => bid.bidId === id);
return returnValue;
}

export function checkDeepArray(Arr) {
if (Array.isArray(Arr)) {
if (Array.isArray(Arr[0])) {
Expand Down Expand Up @@ -223,19 +301,45 @@ export function defaultSize(thebidObj) {
*/
export function ozoneGetWinnerForRequestBid(requestBid, serverResponseSeatBid) {
let thisBidWinner = null;
let winningSeat = null;
for (let j = 0; j < serverResponseSeatBid.length; j++) {
let theseBids = serverResponseSeatBid[j].bid;
let thisSeat = serverResponseSeatBid[j].seat;
for (let k = 0; k < theseBids.length; k++) {
if (theseBids[k].impid === requestBid.bidId) { // we've found a matching server response bid for this request bid
// if (theseBids[k].impid === requestBid.adUnitCode) { // we've found a matching server response bid for this request bid
if ((thisBidWinner == null) || (thisBidWinner.price < theseBids[k].price)) {
thisBidWinner = theseBids[k];
thisBidWinner.seat = thisSeat; // we need to add this here - it's the name of the winning bidder, not guaranteed to be available in the bid object.
winningSeat = thisSeat;
break;
}
}
}
}
return thisBidWinner;
return {'seat': winningSeat, 'bid': thisBidWinner};
}

/**
* Get a list of all the bids, for this bidId
* @param matchBidId
* @param serverResponseSeatBid
* @returns {} = {ozone:{obj}, appnexus:{obj}, ... }
*/
export function ozoneGetAllBidsForBidId(matchBidId, serverResponseSeatBid) {
utils.logInfo('ozoneGetAllBidsForBidId - starting, with: ', matchBidId, serverResponseSeatBid);
let objBids = {};
for (let j = 0; j < serverResponseSeatBid.length; j++) {
let theseBids = serverResponseSeatBid[j].bid;
let thisSeat = serverResponseSeatBid[j].seat;
for (let k = 0; k < theseBids.length; k++) {
if (theseBids[k].impid === matchBidId) { // we've found a matching server response bid for the request bid we're looking for
utils.logInfo('ozoneGetAllBidsForBidId - found matching bid: ', matchBidId, theseBids[k]);
objBids[thisSeat] = theseBids[k];
}
}
}
utils.logInfo('ozoneGetAllBidsForBidId - going to return: ', objBids);
return objBids;
}

/**
Expand Down Expand Up @@ -275,3 +379,4 @@ export function getTestQuerystringValue() {
}

registerBidder(spec);
utils.logInfo('ozoneBidAdapter ended');
Loading

0 comments on commit b12e6dd

Please sign in to comment.