Skip to content

Commit

Permalink
initial version of ozone adapter for review (#3307)
Browse files Browse the repository at this point in the history
* initial version of ozone adapter for review

* updates made per prebid.org feedback

* Added a new line as failed prebid.org automated test

* Unit Tests for Ozone Adaptor

* updated URIs to include https protocol which failed tests
  • Loading branch information
afsheenb authored and jaiminpanchal27 committed Dec 4, 2018
1 parent 5e3f144 commit 96df52f
Show file tree
Hide file tree
Showing 3 changed files with 732 additions and 0 deletions.
233 changes: 233 additions & 0 deletions modules/ozoneBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';

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';

export const spec = {
code: BIDDER_CODE,

/**
* Basic check to see whether required parameters are in the request.
* @param bid
* @returns {boolean}
*/
isBidRequestValid(bid) {
if (!(bid.params.hasOwnProperty('placementId'))) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : missing placementId : siteId, placementId and publisherId are REQUIRED');
return false;
}
if (!(bid.params.placementId).toString().match(/^[0-9]{10}$/)) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : placementId must be exactly 10 numeric characters');
return false;
}
if (!(bid.params.hasOwnProperty('publisherId'))) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : missing publisherId : siteId, placementId and publisherId are REQUIRED');
return false;
}
if (!(bid.params.publisherId).toString().match(/^[a-zA-Z0-9\-]{12}$/)) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : publisherId must be exactly 12 alphanumieric characters including hyphens');
return false;
}
if (!(bid.params.hasOwnProperty('siteId'))) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : missing siteId : siteId, placementId and publisherId are REQUIRED');
return false;
}
if (!(bid.params.siteId).toString().match(/^[0-9]{10}$/)) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : siteId must be exactly 10 numeric characters');
return false;
}
if (bid.params.hasOwnProperty('customData')) {
if (typeof bid.params.customData !== 'object') {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : customData is not an object');
return false;
}
}
if (bid.params.hasOwnProperty('customParams')) {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : customParams should be renamed to customData');
return false;
}
if (bid.params.hasOwnProperty('ozoneData')) {
if (typeof bid.params.ozoneData !== 'object') {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : ozoneData is not an object');
return false;
}
}
if (bid.params.hasOwnProperty('lotameData')) {
if (typeof bid.params.lotameData !== 'object') {
utils.logInfo('OZONE BID ADAPTER VALIDATION FAILED : lotameData is not an object');
return false;
}
}
return true;
},
/**
* @param serverResponse
* @param request
* @returns {*}
*/
interpretResponse(serverResponse, request) {
serverResponse = serverResponse.body || {};
if (serverResponse.seatbid) {
if (utils.isArray(serverResponse.seatbid)) {
const {seatbid: arrSeatbid} = serverResponse;
let winnerAds = arrSeatbid.reduce((bid, ads) => {
var _seat = ads.seat;
let ad = ads.bid.reduce(function(currentWinningBid, considerBid) {
if (currentWinningBid.price < considerBid.price) {
const bid = matchRequest(considerBid.impid, request);
const {width, height} = defaultSize(bid);
considerBid.cpm = considerBid.price;
considerBid.bidId = considerBid.impid;
considerBid.requestId = considerBid.impid;
considerBid.width = considerBid.w || width;
considerBid.height = considerBid.h || height;
considerBid.ad = considerBid.adm;
considerBid.netRevenue = true;
considerBid.creativeId = considerBid.crid;
considerBid.currency = 'USD';
considerBid.ttl = 60;
considerBid.seat = _seat;

return considerBid;
} else {
currentWinningBid.cpm = currentWinningBid.price;
return currentWinningBid;
}
}, {price: 0});
if (ad.adm) {
bid.push(ad)
}
return bid;
}, [])
let winnersClean = winnerAds.filter(w => {
if (w.bidId) {
return true;
}
return false;
});
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();
}

if (!ozoneRequest.test) {
delete ozoneRequest.test;
}
if (bidderRequest.gdprConsent) {
ozoneRequest.regs = {};
ozoneRequest.regs.ext = {};
ozoneRequest.regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies === true ? 1 : 0;
if (ozoneRequest.regs.ext.gdpr) {
ozoneRequest.regs.ext.consent = bidderRequest.gdprConsent.consentString;
}
}
let tosendtags = validBidRequests.map(ozone => {
var obj = {};
obj.id = ozone.bidId;
obj.tagid = String(ozone.params.ozoneid);
obj.secure = window.location.protocol === 'https:' ? 1 : 0;
obj.banner = {
topframe: 1,
w: ozone.sizes[0][0] || 0,
h: ozone.sizes[0][1] || 0,
format: ozone.sizes.map(s => {
return {w: s[0], h: s[1]};
})
};
if (ozone.params.hasOwnProperty('customData')) {
obj.customData = ozone.params.customData;
}
if (ozone.params.hasOwnProperty('ozoneData')) {
obj.ozoneData = ozone.params.ozoneData;
}
if (ozone.params.hasOwnProperty('lotameData')) {
obj.lotameData = ozone.params.lotameData;
}
if (ozone.params.hasOwnProperty('publisherId')) {
obj.publisherId = (ozone.params.publisherId).toString();
}
if (ozone.params.hasOwnProperty('siteId')) {
obj.siteId = (ozone.params.siteId).toString();
}
obj.ext = {'prebid': {'storedrequest': {'id': (ozone.params.placementId).toString()}}};
return obj;
});
ozoneRequest.imp = tosendtags;
var ret = {
method: 'POST',
url: OZONEURI,
data: JSON.stringify(ozoneRequest),
bidderRequest: bidderRequest
};
utils.logInfo(['buildRequests going to return', ret]);
return ret;
},
getUserSyncs(optionsType, serverResponse) {
if (!serverResponse || serverResponse.length === 0) {
return [];
}
if (optionsType.iframeEnabled) {
return [{
type: 'iframe',
url: OZONECOOKIESYNC
}];
}
}
}

/**
* 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])) {
return Arr[0];
} else {
return Arr;
}
} else {
return Arr;
}
}
export function defaultSize(thebidObj) {
const {sizes} = thebidObj;
const returnObject = {};
returnObject.width = checkDeepArray(sizes)[0];
returnObject.height = checkDeepArray(sizes)[1];
return returnObject;
}
registerBidder(spec);
45 changes: 45 additions & 0 deletions modules/ozoneBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

# Overview

```
Module Name: Ozone Project Bidder Adapter
Module Type: Bidder Adapter
Maintainer: engineering@ozoneproject.com
```

# Description

Module that connects to the Ozone Project's demand source(s).

The Ozone Project bid adapter supports Banner mediaTypes ONLY.

# Test Parameters


A test ad unit that will consistently return test creatives:

```
//Banner adUnit
adUnits = [{
code: 'id-of-your-banner-div',
mediaTypes: {
banner: {
sizes: [[300, 250], [300,600]]
}
},
bids: [{
bidder: 'ozone',
params: {
publisherId: 'OZONENUK0001', /* an ID to identify the publisher account - required */
siteId: '4204204201', /* An ID used to identify a site within a publisher account - required */
placementId: '0420420421', /* an ID used to identify the piece of inventory - required - for appnexus test use 13144370. */
customData: {"key1": "value1", "key2": "value2}, /* optional JSON placeholder for passing publisher key-values for targeting. */
ozoneData: {"key1": "value1", "key2": "value2"}, /* optional JSON placeholder for for passing ozone project key-values for targeting. */
lotameData: {"key1": "value1", "key2": "value2} /* optional JSON placeholder for passing Lotame DMP data */
}
}]
}];
```
Loading

0 comments on commit 96df52f

Please sign in to comment.