Skip to content

Commit

Permalink
One Video Adapter (#2445)
Browse files Browse the repository at this point in the history
* One Video Adapter

* adding more test cases
  • Loading branch information
ankur-modi authored and snapwich committed May 4, 2018
1 parent 4e80a29 commit 3116d7f
Show file tree
Hide file tree
Showing 3 changed files with 371 additions and 0 deletions.
189 changes: 189 additions & 0 deletions modules/oneVideoBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import * as utils from 'src/utils';
import {registerBidder} from 'src/adapters/bidderFactory';
const BIDDER_CODE = 'oneVideo';
export const spec = {
code: 'oneVideo',
ENDPOINT: '//ads.adaptv.advertising.com/rtb/openrtb?ext_id=',
SYNC_ENDPOINT1: 'https://cm.g.doubleclick.net/pixel?google_nid=adaptv_dbm&google_cm&google_sc',
SYNC_ENDPOINT2: 'https://pr-bh.ybp.yahoo.com/sync/adaptv_ortb/{combo_uid}',
SYNC_ENDPOINT3: 'https://sync-tm.everesttech.net/upi/pid/m7y5t93k?redir=https%3A%2F%2Fsync.adap.tv%2Fsync%3Ftype%3Dgif%26key%3Dtubemogul%26uid%3D%24%7BUSER_ID%7D',
SYNC_ENDPOINT4: 'https://match.adsrvr.org/track/cmf/generic?ttd_pid=adaptv&ttd_tpi=1',
supportedMediaTypes: ['video'],
/**
* Determines whether or not the given bid request is valid.
*
* @param {BidRequest} bid The bid params to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function(bid) {
if (bid.bidder !== BIDDER_CODE || typeof bid.params === 'undefined') {
return false;
}

// Video validations
if (typeof bid.params.video === 'undefined' || typeof bid.params.video.playerWidth === 'undefined' || typeof bid.params.video.playerHeight == 'undefined' || typeof bid.params.video.mimes == 'undefined') {
return false;
}

// Pub Id validation
if (typeof bid.params.pubId === 'undefined') {
return false;
}

return true;
},
/**
* Make a server request from the list of BidRequests.
*
* @param {validBidRequests[]} - an array of bids
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function(bids) {
return bids.map(bid => {
return {
method: 'POST',
url: location.protocol + spec.ENDPOINT + bid.params.pubId,
data: getRequestData(bid),
options: {contentType: 'application/json'},
bidRequest: bid
}
})
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {*} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(response, { bidRequest }) {
let bid;
let size;
let bidResponse;
try {
response = response.body;
bid = response.seatbid[0].bid[0];
} catch (e) {
response = null;
}
if (!response || !bid || (!bid.adm && !bid.nurl) || !bid.price) {
utils.logWarn(`No valid bids from ${spec.code} bidder`);
return [];
}
size = getSize(bidRequest.sizes);
bidResponse = {
requestId: bidRequest.bidId,
bidderCode: spec.code,
cpm: bid.price,
creativeId: bid.id,
width: size.width,
height: size.height,
mediaType: 'video',
currency: response.cur,
ttl: 100,
netRevenue: true
};
if (bid.nurl) {
bidResponse.vastUrl = bid.nurl;
} else if (bid.adm) {
bidResponse.vastXml = bid.adm;
}
return bidResponse;
},
/**
* Register the user sync pixels which should be dropped after the auction.
*
* @param {SyncOptions} syncOptions Which user syncs are allowed?
* @param {ServerResponse[]} serverResponses List of server's responses.
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs: function(syncOptions) {
if (syncOptions.pixelEnabled) {
return [{
type: 'image',
url: spec.SYNC_ENDPOINT1
},
{
type: 'image',
url: spec.SYNC_ENDPOINT2
},
{
type: 'image',
url: spec.SYNC_ENDPOINT3
},
{
type: 'image',
url: spec.SYNC_ENDPOINT4
}];
}
}
};

function getSize(sizes) {
let parsedSizes = utils.parseSizesInput(sizes);
let [ width, height ] = parsedSizes.length ? parsedSizes[0].split('x') : [];
return {
width: parseInt(width, 10) || undefined,
height: parseInt(height, 10) || undefined
};
}

function getRequestData(bid) {
let loc = utils.getTopWindowLocation();
let global = (window.top) ? window.top : window;
let page = (bid.params.site.page) ? (bid.params.site.page) : (loc.href);
let ref = (bid.params.site.referrer) ? bid.params.site.referrer : utils.getTopWindowReferrer();
let bidData = {
id: utils.generateUUID(),
at: 2,
cur: bid.cur || 'USD',
imp: [{
id: '1',
secure: isSecure(),
bidfloor: bid.params.bidfloor,
video: {
mimes: bid.params.video.mimes,
w: bid.params.video.playerWidth,
h: bid.params.video.playerHeight,
linearity: 1,
protocols: bid.params.video.protocols || [2, 5]
}
}],
site: {
page: page,
ref: ref
},
device: {
ua: global.navigator.userAgent
},
tmax: 200
};

if (bid.params.video.maxbitrate) {
bidData.imp[0].video.maxbitrate = bid.params.video.maxbitrate
}
if (bid.params.video.maxduration) {
bidData.imp[0].video.maxduration = bid.params.video.maxduration
}
if (bid.params.video.minduration) {
bidData.imp[0].video.minduration = bid.params.video.minduration
}
if (bid.params.video.api) {
bidData.imp[0].video.api = bid.params.video.api
}
if (bid.params.video.delivery) {
bidData.imp[0].video.delivery = bid.params.video.delivery
}
if (bid.params.video.position) {
bidData.imp[0].video.pos = bid.params.video.position
}
if (bid.params.site.id) {
bidData.site.id = bid.params.site.id
}
return bidData;
}

function isSecure() {
return document.location.protocol === 'https:';
}

registerBidder(spec);
47 changes: 47 additions & 0 deletions modules/oneVideoBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Overview

**Module Name**: One Video Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: ankur.modi@oath.com

# Description

Connects to One Video demand source to fetch bids.


# Test Parameters
```
var adUnits = [
{
code: 'video1',
sizes: [640,480],
mediaTypes: {
video: {
context: "instream"
}
},
bids: [
{
bidder: 'oneVideo',
params: {
video: {
playerWidth: 480,
playerHeight: 640,
mimes: ['video/mp4', 'application/javascript'],
protocols: [2,5],
api: [2],
position: 1,
delivery: [2]
},
site: {
id: 1,
page: 'http://abhi12345.com',
referrer: 'http://abhi12345.com'
},
pubId: 'brxd'
}
}
]
}
];
```
135 changes: 135 additions & 0 deletions test/spec/modules/oneVideoBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { expect } from 'chai';
import { spec } from 'modules/oneVideoBidAdapter';
import * as utils from 'src/utils';

describe('OneVideoBidAdapter', () => {
let bidRequest;

beforeEach(() => {
bidRequest = {
bidder: 'oneVideo',
sizes: [640, 480],
bidId: '30b3efwfwe1e',
params: {
video: {
playerWidth: 640,
playerHeight: 480,
mimes: ['video/mp4', 'application/javascript'],
protocols: [2, 5],
api: [2],
position: 1,
delivery: [2]
},
site: {
id: 1,
page: 'https://news.yahoo.com/portfolios',
referrer: 'http://www.yahoo.com'
},
pubId: 'brxd'
}
};
});

describe('spec.isBidRequestValid', () => {
it('should return true when the required params are passed', () => {
expect(spec.isBidRequestValid(bidRequest)).to.equal(true);
});

it('should return false when the "video" param is missing', () => {
bidRequest.params = {
pubId: 'brxd'
};
expect(spec.isBidRequestValid(bidRequest)).to.equal(false);
});

it('should return false when the "pubId" param is missing', () => {
bidRequest.params = {
video: {
playerWidth: 480,
playerHeight: 640,
mimes: ['video/mp4', 'application/javascript'],
protocols: [2, 5],
api: [2],
position: 1,
delivery: [2]
}
};
expect(spec.isBidRequestValid(bidRequest)).to.equal(false);
});

it('should return false when no bid params are passed', () => {
bidRequest.params = {};
expect(spec.isBidRequestValid(bidRequest)).to.equal(false);
});
});

describe('spec.buildRequests', () => {
it('should create a POST request for every bid', () => {
const requests = spec.buildRequests([ bidRequest ]);
expect(requests[0].method).to.equal('POST');
expect(requests[0].url).to.equal(location.protocol + spec.ENDPOINT + bidRequest.params.pubId);
});

it('should attach the bid request object', () => {
const requests = spec.buildRequests([ bidRequest ]);
expect(requests[0].bidRequest).to.equal(bidRequest);
});

it('should attach request data', () => {
const requests = spec.buildRequests([ bidRequest ]);
const data = requests[0].data;
const [ width, height ] = bidRequest.sizes;
expect(data.imp[0].video.w).to.equal(width);
expect(data.imp[0].video.h).to.equal(height);
expect(data.imp[0].bidfloor).to.equal(bidRequest.params.bidfloor);
});

it('must parse bid size from a nested array', () => {
const width = 640;
const height = 480;
bidRequest.sizes = [[ width, height ]];
const requests = spec.buildRequests([ bidRequest ]);
const data = requests[0].data;
expect(data.imp[0].video.w).to.equal(width);
expect(data.imp[0].video.h).to.equal(height);
});
});

describe('spec.interpretResponse', () => {
it('should return no bids if the response is not valid', () => {
const bidResponse = spec.interpretResponse({ body: null }, { bidRequest });
expect(bidResponse.length).to.equal(0);
});

it('should return no bids if the response "nurl" and "adm" are missing', () => {
const serverResponse = {seatbid: [{bid: [{price: 6.01}]}]};
const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest });
expect(bidResponse.length).to.equal(0);
});

it('should return no bids if the response "price" is missing', () => {
const serverResponse = {seatbid: [{bid: [{adm: '<VAST></VAST>'}]}]};
const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest });
expect(bidResponse.length).to.equal(0);
});

it('should return a valid bid response with just "adm"', () => {
const serverResponse = {seatbid: [{bid: [{id: 1, price: 6.01, adm: '<VAST></VAST>'}]}], cur: 'USD'};
const bidResponse = spec.interpretResponse({ body: serverResponse }, { bidRequest });
let o = {
requestId: bidRequest.bidId,
bidderCode: spec.code,
cpm: serverResponse.seatbid[0].bid[0].price,
creativeId: serverResponse.seatbid[0].bid[0].id,
vastXml: serverResponse.seatbid[0].bid[0].adm,
width: 640,
height: 480,
mediaType: 'video',
currency: 'USD',
ttl: 100,
netRevenue: true
};
expect(bidResponse).to.deep.equal(o);
});
});
});

0 comments on commit 3116d7f

Please sign in to comment.