Skip to content

Commit

Permalink
Add Imonomy network BidAdapter (#3765)
Browse files Browse the repository at this point in the history
* Add Imonomy network BidAdapter

* add changes due to the comments on Prebid
  • Loading branch information
imonomy authored and Isaac Dettman committed May 9, 2019
1 parent bd1636a commit ad7b59d
Show file tree
Hide file tree
Showing 3 changed files with 323 additions and 0 deletions.
130 changes: 130 additions & 0 deletions modules/imonomyBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';

const BIDDER_CODE = 'imonomy';
const ENDPOINT = '//b.imonomy.com/openrtb/hb/00000';
const USYNCURL = '//b.imonomy.com/UserMatching/b/';

export const spec = {
code: BIDDER_CODE,

/**
* Determines whether or not the given bid request is valid. Valid bid request must have placementId and hbid
*
* @param {BidRequest} bid The bid params to validate.
* @return {boolean} True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: bid => {
return !!(bid && bid.params && bid.params.placementId && bid.params.hbid);
},
/**
* Make a server request from the list of BidRequests.
*
* @param {BidRequest[]} validBidRequests an array of bids
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: validBidRequests => {
const tags = validBidRequests.map(bid => {
// map each bid id to bid object to retrieve adUnit code in callback
let tag = {
uuid: bid.bidId,
sizes: bid.sizes,
trid: bid.transactionId,
hbid: bid.params.hbid,
placementid: bid.params.placementId
};

// add floor price if specified (not mandatory)
if (bid.params.floorPrice) {
tag.floorprice = bid.params.floorPrice;
}

return tag;
});

// Imonomy server config
const time = new Date().getTime();
const kbConf = {
ts_as: time,
hb_placements: [],
hb_placement_bidids: {},
hb_floors: {},
cb: _generateCb(time),
tz: new Date().getTimezoneOffset(),
};

validBidRequests.forEach(bid => {
kbConf.hdbdid = kbConf.hdbdid || bid.params.hbid;
kbConf.encode_bid = kbConf.encode_bid || bid.params.encode_bid;
kbConf.hb_placement_bidids[bid.params.placementId] = bid.bidId;
if (bid.params.floorPrice) {
kbConf.hb_floors[bid.params.placementId] = bid.params.floorPrice;
}
kbConf.hb_placements.push(bid.params.placementId);
});

let payload = {};
if (!utils.isEmpty(tags)) {
payload = { bids: [...tags], kbConf };
}

let endpointToUse = ENDPOINT;
if (kbConf.hdbdid) {
endpointToUse = endpointToUse.replace('00000', kbConf.hdbdid);
}

return {
method: 'POST',
url: endpointToUse,
data: JSON.stringify(payload)
};
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {*} response A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: (response) => {
const bidResponses = [];
if (response && response.body && response.body.bids) {
response.body.bids.forEach(bid => {
// The bid ID. Used to tie this bid back to the request.
if (bid.uuid) {
bid.requestId = bid.uuid;
} else {
utils.logError('No uuid for bid');
}
// The creative payload of the returned bid.
if (bid.creative) {
bid.ad = bid.creative;
} else {
utils.logError('No creative for bid');
}
bidResponses.push(bid);
});
}
return bidResponses;
},
/**
* Register User Sync.
*/
getUserSyncs: syncOptions => {
if (syncOptions.iframeEnabled) {
return [{
type: 'iframe',
url: USYNCURL
}];
}
}
};

/**
* Generated cache baster value to be sent to bid server
* @param {*} time current time to use for creating cb.
*/
function _generateCb(time) {
return Math.floor((time % 65536) + (Math.floor(Math.random() * 65536) * 65536));
}

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

**Module Name**: Imonomy Bidder Adapter
**Module Type**: Bidder Adapter
**Maintainer**: support@imonomy.com

# Description

Connects to Imonomy demand source to fetch bids.

# Test Parameters
```
var adUnits = [{
code: 'banner-ad-div',
sizes: [[300, 250]],
// Replace this object to test a new Adapter!
bids: [{
bidder: 'imonomy',
params: {
placementId: 'e69148e0ba6c4c07977dc2daae5e1577',
hbid: '14567718624',
floorPrice: 0.5
}
}]
}];
```


164 changes: 164 additions & 0 deletions test/spec/modules/imonomyBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { expect } from 'chai';
import { spec } from 'modules/imonomyBidAdapter';

describe('Imonomy Adapter Tests', function () {
const bidsRequest = [
{
bidder: 'imonomy',
params: {
placementId: '170577',
hbid: '14567718624',
},
placementCode: 'div-gpt-ad-1460505748561-0',
transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb',
sizes: [
[300, 250]
],
bidId: '2faedf1095f815',
bidderRequestId: '18065867f8ae39',
auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3'
},
{
bidder: 'imonomy',
params: {
placementId: '281277',
hbid: '14567718624',
floorPrice: 0.5
},
placementCode: 'div-gpt-ad-1460505748561-0',
transactionId: '9f801c02-bbe8-4683-8ed4-bc816ea186bb',
sizes: [
[728, 90]
],
bidId: '3c34e2367a3f59',
bidderRequestId: '18065867f8ae39',
auctionId: '529e1518-b872-45cf-807c-2d41dfa5bcd3'
}];

const bidsResponse = {
body: {
bids: [
{
placementid: '170577',
uuid: '2faedf1095f815',
width: 300,
height: 250,
cpm: 0.51,
creative: '<script type="text/javascript" src="http://creative.com/pathToNiceCreative"></script>',
ttl: 360,
currency: 'USD',
netRevenue: true,
creativeId: 'd30b58c2ba'
}
]
}
};

it('Verifies imonomyAdapter bidder code', function () {
expect(spec.code).to.equal('imonomy');
});

it('Verifies imonomyAdapter bid request validation', function () {
expect(spec.isBidRequestValid(bidsRequest[0])).to.equal(true);
expect(spec.isBidRequestValid(bidsRequest[1])).to.equal(true);
expect(spec.isBidRequestValid({})).to.equal(false);
expect(spec.isBidRequestValid({ params: {} })).to.equal(false);
expect(spec.isBidRequestValid({ params: { hbid: 12345 } })).to.equal(false);
expect(spec.isBidRequestValid({ params: { placementid: 12345 } })).to.equal(false);
expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890 } })).to.equal(true);
expect(spec.isBidRequestValid({ params: { hbid: 12345, placementId: 67890, floorPrice: 0.8 } })).to.equal(true);
});

it('Verify imonomyAdapter build request', function () {
var startTime = new Date().getTime();

const request = spec.buildRequests(bidsRequest);
expect(request.url).to.equal('//b.imonomy.com/openrtb/hb/14567718624');
expect(request.method).to.equal('POST');
const requestData = JSON.parse(request.data);

// bids object
let bids = requestData.bids;
expect(bids).to.have.lengthOf(2);

// first bid request: no floor price
expect(bids[0].uuid).to.equal('2faedf1095f815');
expect(bids[0].floorprice).to.be.undefined;
expect(bids[0].placementid).to.equal('170577');
expect(bids[0].hbid).to.equal('14567718624');
expect(bids[0].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb');
expect(bids[0].sizes).to.have.lengthOf(1);
expect(bids[0].sizes[0][0]).to.equal(300);
expect(bids[0].sizes[0][1]).to.equal(250);

// second bid request: with floor price
expect(bids[1].uuid).to.equal('3c34e2367a3f59');
expect(bids[1].floorprice).to.equal(0.5);
expect(bids[1].placementid).to.equal('281277');
expect(bids[1].hbid).to.equal('14567718624');
expect(bids[1].trid).to.equal('9f801c02-bbe8-4683-8ed4-bc816ea186bb');
expect(bids[1]).to.have.property('sizes')
.that.is.an('array')
.of.length(1)
.that.deep.equals([[728, 90]]);

// kbConf object
let kbConf = requestData.kbConf;
expect(kbConf.hdbdid).to.equal(bids[0].hbid);
expect(kbConf.hdbdid).to.equal(bids[1].hbid);
expect(kbConf.encode_bid).to.be.undefined;
// kbConf timezone and cb
expect(kbConf.cb).not.to.be.undefined;
expect(kbConf.ts_as).to.be.above(startTime - 1);
expect(kbConf.tz).to.equal(new Date().getTimezoneOffset());
// kbConf bid ids
expect(kbConf.hb_placement_bidids)
.to.have.property(bids[0].placementid)
.that.equal(bids[0].uuid);
expect(kbConf.hb_placement_bidids)
.to.have.property(bids[1].placementid)
.that.equal(bids[1].uuid);
// kbConf floor price
expect(kbConf.hb_floors).not.to.have.property(bids[0].placementid)
expect(kbConf.hb_floors).to.have.property(bids[1].placementid).that.equal(bids[1].floorprice);
// kbConf placement ids
expect(kbConf.hb_placements).to.have.lengthOf(2);
expect(kbConf.hb_placements[0]).to.equal(bids[0].placementid);
expect(kbConf.hb_placements[1]).to.equal(bids[1].placementid);
});

it('Verify imonomyAdapter build response', function () {
const request = spec.buildRequests(bidsRequest);
const bids = spec.interpretResponse(bidsResponse, request);

// 'server' return single bid
expect(bids).to.have.lengthOf(1);

// verify bid object
const bid = bids[0];
const responseBids = bidsResponse.body.bids;

expect(bid.cpm).to.equal(responseBids[0].cpm);
expect(bid.ad).to.equal(responseBids[0].creative);
expect(bid.requestId).equal(responseBids[0].uuid);
expect(bid.uuid).equal(responseBids[0].uuid);
expect(bid.width).to.equal(responseBids[0].width);
expect(bid.height).to.equal(responseBids[0].height);
expect(bid.ttl).to.equal(responseBids[0].ttl);
expect(bid.currency).to.equal('USD');
expect(bid.netRevenue).to.equal(true);
expect(bid.creativeId).to.equal(responseBids[0].creativeId);
});

it('Verifies imonomyAdapter sync options', function () {
// user sync disabled
expect(spec.getUserSyncs({})).to.be.undefined;
expect(spec.getUserSyncs({ iframeEnabled: false })).to.be.undefined;
// user sync enabled
const options = spec.getUserSyncs({ iframeEnabled: true });
expect(options).to.not.be.undefined;
expect(options).to.have.lengthOf(1);
expect(options[0].type).to.equal('iframe');
expect(options[0].url).to.equal('//b.imonomy.com/UserMatching/b/');
});
});

0 comments on commit ad7b59d

Please sign in to comment.