Skip to content

Commit

Permalink
+ Add Optimatic Bid Adapter (prebid#1837)
Browse files Browse the repository at this point in the history
* + Add Optimatic Bid Adapter

* + cleaned up player log ajax call (as requested)
+ replaced "partnerId" with correct "placement" parameter name

* + commit again
  • Loading branch information
optimatic58 authored and dbemiller committed Nov 20, 2017
1 parent 4ee64ac commit 0d7b35d
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 0 deletions.
101 changes: 101 additions & 0 deletions modules/optimaticBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as utils from 'src/utils';
import { config } from 'src/config';
import { registerBidder } from 'src/adapters/bidderFactory';
export const ENDPOINT = '//mg-bid.optimatic.com/adrequest/';

export const spec = {
code: 'optimatic',

supportedMediaTypes: ['video'],

isBidRequestValid: function(bid) {
return !!(bid && bid.params && bid.params.placement && bid.params.bidfloor);
},

buildRequests: function(bids) {
return bids.map(bid => {
return {
method: 'POST',
url: ENDPOINT + bid.params.placement,
data: getData(bid),
options: {contentType: 'application/json'},
bidRequest: bid
}
})
},

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.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,
vastXml: bid.adm,
width: size.width,
height: size.height,
mediaType: 'video',
currency: response.cur,
ttl: 300,
netRevenue: true
};
return bidResponse;
}
};

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 getData (bid) {
let size = getSize(bid.sizes);
let loc = utils.getTopWindowLocation();
let global = (window.top) ? window.top : window;
return {
id: utils.generateUUID(),
imp: [{
id: '1',
bidfloor: bid.params.bidfloor,
video: {
mimes: ['video/mp4', 'video/ogg', 'video/webm', 'video/x-flv', 'application/javascript', 'application/x-shockwave-flash'],
width: size.width,
height: size.height
}
}],
site: {
id: '1',
domain: loc.host,
page: loc.href,
ref: utils.getTopWindowReferrer(),
publisher: {
id: '1'
}
},
device: {
ua: global.navigator.userAgent,
ip: '127.0.0.1',
devicetype: 1
}
};
}

config.setConfig({ usePrebidCache: true });
registerBidder(spec);
30 changes: 30 additions & 0 deletions modules/optimaticBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Overview

```
Module Name: Optimatic Bidder Adapter
Module Type: Bidder Adapter
Maintainer: prebid@optimatic.com
```

# Description

Optimatic Bid Adapter Module connects to Optimatic Demand Sources for Video Ads

# Test Parameters
```
var adUnits = [
{
code: 'test-div',
sizes: [[640,480]], // a video size
bids: [
{
bidder: "optimatic",
params: {
placement: "2chy7Gc2eSQL",
bidfloor: 2.5
}
}
]
},
];
```
140 changes: 140 additions & 0 deletions test/spec/modules/optimaticBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import { expect } from 'chai';
import { spec, ENDPOINT } from 'modules/optimaticBidAdapter';
import * as utils from 'src/utils';

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

beforeEach(() => {
bidRequest = {
bidder: 'optimatic',
params: {
placement: '2chy7Gc2eSQL',
bidfloor: 5.00
},
adUnitCode: 'adunit-code',
sizes: [ 640, 480 ],
bidId: '30b31c1838de1e',
bidderRequestId: '22edbae2733bf6',
auctionId: '1d1a030790a475'
};
});

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 "bidfloor" param is missing', () => {
bidRequest.params = {
placement: '2chy7Gc2eSQL'
};
expect(spec.isBidRequestValid(bidRequest)).to.equal(false);
});

it('should return false when the "placement" param is missing', () => {
bidRequest.params = {
bidfloor: 5.00
};
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);
});

it('should return false when a bid request is not passed', () => {
expect(spec.isBidRequestValid()).to.equal(false);
expect(spec.isBidRequestValid({})).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(ENDPOINT + bidRequest.params.placement);
});

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.width).to.equal(width);
expect(data.imp[0].video.height).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.width).to.equal(width);
expect(data.imp[0].video.height).to.equal(height);
});

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

it('must handle an empty bid size', () => {
bidRequest.sizes = [];
const requests = spec.buildRequests([ bidRequest ]);
const data = requests[0].data;
expect(data.imp[0].video.width).to.equal(undefined);
expect(data.imp[0].video.height).to.equal(undefined);
});
});

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 "adm" is missing', () => {
const serverResponse = {seatbid: [{bid: [{price: 5.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', () => {
const serverResponse = {seatbid: [{bid: [{id: 1, price: 5.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: 300,
netRevenue: true
};
expect(bidResponse).to.deep.equal(o);
});
});
});

0 comments on commit 0d7b35d

Please sign in to comment.