Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create undertone udapter #1850

Merged
merged 16 commits into from
Nov 29, 2017
75 changes: 75 additions & 0 deletions modules/undertoneBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Adapter to send bids to Undertone
*/

import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';

const BIDDER_CODE = 'undertone';
const URL = '//hb.undertone.com/hb'; // //ads.undertone.com/hb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's //ads.undertone.com/hb ? Could remove this comment


export const spec = {
code: BIDDER_CODE,
isBidRequestValid: function(bid) {
if (bid && bid.params && bid.params.publisherId && bid.params.placementId) {
bid.params.publisherId = parseInt(bid.params.publisherId);
return true;
}
},
buildRequests: function(validBidRequests) {
const payload = {
'x-ut-hb-params': []
};
const timeout = window.PREBID_TIMEOUT || null;
Copy link
Collaborator

@jsnellbaker jsnellbaker Nov 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify why the timeout value is needed for the bid object passed to your system? Is this a reporting metric or actively used by the system?

Prebid data can't be accessed directly through the window object. So we want to understand the need to help figure out some alternatives around supplying this information (if it's really needed).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our API enable us to pass the timeout value to the server, so we can response with nobid if we are not able to respond within this time frame. This should improve the user experience and reduce load from our server.
Is there another way to get the timeout you're using? Or maybe there is a constant value you're using? If so, we can just add it here.

const host = utils.getTopWindowLocation().host;
const domain = /[-\w]+\.(?:[-\w]+\.xn--[-\w]+|[-\w]{3,}|[-\w]+\.[-\w]{2})$/i.exec(host);

const pubid = validBidRequests[0].params.publisherId;
const REQ_URL = `${URL}?pid=${pubid}&domain=${domain}`;

validBidRequests.map(bidReq => {
const bid = {
bidRequestId: bidReq.bidId,
hbadaptor: 'prebid',
domain: domain,
placementId: bidReq.params.placementId,
publisherId: bidReq.params.publisherId,
sizes: bidReq.sizes,
timeout: timeout,
params: bidReq.params
};
payload['x-ut-hb-params'].push(bid);
});
return {
method: 'POST',
url: REQ_URL,
withCredentials: true,
data: JSON.stringify(payload)
};
},
interpretResponse: function(serverResponse, request) {
const bids = [];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1748 changed the first argument of interpretResponse to:

{
  body: responseBody,
  headers: {
    get: function(header) { /* returns a header from the HTTP response */ }
  }
}

so adding something like

serverResponse = serverResponse.body;

just below this line, or however you'd prefer to grab the body, and updating corresponding tests if needed should get this back to working properly


if (serverResponse && Array.isArray(serverResponse) && serverResponse.length > 0) {
serverResponse.forEach((bidRes) => {
if (bidRes.ad && bidRes.cpm > 0) {
const bid = {
requestId: bidRes.bidRequestId,
bidderCode: BIDDER_CODE,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bidderCode will be set by bidderFactory automatically now, this line can be dropped

cpm: bidRes.cpm,
width: bidRes.width,
height: bidRes.height,
creativeId: bidRes.adId,
currency: bidRes.currency,
netRevenue: bidRes.netRevenue,
ttl: bidRes.ttl,
ad: bidRes.ad
};
bids.push(bid);
}
});
}
return bids;
}
};
registerBidder(spec);
29 changes: 29 additions & 0 deletions modules/undertoneBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Overview

```
Module Name: Example Bidder Adapter
Module Type: Bidder Adapter
Maintainer: [email protected]
```
# Description

Module that connects to Undertone's demand sources

# Test Parameters
```
var adUnits = [
{
code: 'test-div',
sizes: [[300, 250]],
bids: [
{
bidder: "undertone",
params: {
placementId: '10433394',
publisherId: 12345
}
}
]
}
];
```
141 changes: 141 additions & 0 deletions test/spec/modules/undertoneBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { expect } from 'chai';
import { spec } from 'modules/undertoneBidAdapter';

const URL = '//hb.undertone.com/hb';
const BIDDER_CODE = 'undertone';
const validBidReq = {
bidder: BIDDER_CODE,
params: {
placementId: '10433394',
publisherId: 12345
},
sizes: [[300, 250], [300, 600]],
bidId: '263be71e91dd9d',
requestId: '9ad1fa8d-2297-4660-a018-b39945054746',
auctionId: '1d1a030790a475'
};

const invalidBidReq = {
bidder: BIDDER_CODE,
params: {
placementId: '123456789'
},
sizes: [[300, 250], [300, 600]],
bidId: '263be71e91dd9d',
requestId: '9ad1fa8d-2297-4660-a018-b39945054746'
};

const bidReq = [{
bidder: BIDDER_CODE,
params: {
placementId: '10433394',
publisherId: 12345
},
sizes: [[300, 250], [300, 600]],
bidId: '263be71e91dd9d',
requestId: '9ad1fa8d-2297-4660-a018-b39945054746',
auctionId: '1d1a030790a475'
}];

const validBidRes = {
ad: '<div>Hello</div>',
publisherId: 12345,
bidRequestId: '263be71e91dd9d',
adId: 15,
cpm: 100,
nCampaignId: 2,
creativeId: '123abc',
currency: 'USD',
netRevenue: true,
width: 300,
height: 250,
ttl: 360
};

const bidResponse = [validBidRes];

const bidResArray = [
validBidRes,
{
ad: '',
bidRequestId: '263be71e91dd9d',
cpm: 100,
adId: '123abc',
currency: 'USD',
netRevenue: true,
width: 300,
height: 250,
ttl: 360
},
{
ad: '<div>Hello</div>',
bidRequestId: '',
cpm: 0,
adId: '123abc',
currency: 'USD',
netRevenue: true,
width: 300,
height: 250,
ttl: 360
}
];

describe('Undertone Adapter', () => {
describe('request', () => {
it('should validate bid request', () => {
expect(spec.isBidRequestValid(validBidReq)).to.equal(true);
});
it('should not validate incorrect bid request', () => {
expect(spec.isBidRequestValid(invalidBidReq)).to.equal(undefined);
});
});
describe('build request', () => {
it('should send request to correct url via POST', () => {
const request = spec.buildRequests(bidReq);
const domain = null;
const REQ_URL = `${URL}?pid=${bidReq[0].params.publisherId}&domain=${domain}`;
expect(request.url).to.equal(REQ_URL);
expect(request.method).to.equal('POST');
});
it('should have all relevant fields', () => {
const request = spec.buildRequests(bidReq);
const bid = JSON.parse(request.data)['x-ut-hb-params'][0];
expect(bid.bidRequestId).to.equal('263be71e91dd9d');
expect(bid.sizes.length > 0).to.equal(true);
expect(bid.placementId).to.equal('10433394');
expect(bid.publisherId).to.equal(12345);
expect(bid.params).to.be.an('object');
});
});

describe('interpretResponse', () => {
it('should build bid array', () => {
let result = spec.interpretResponse(bidResponse);
expect(result.length).to.equal(1);
});

it('should have all relevant fields', () => {
const result = spec.interpretResponse(bidResponse);
const bid = result[0];

expect(bid.requestId).to.equal('263be71e91dd9d');
expect(bid.bidderCode).to.equal(BIDDER_CODE);
expect(bid.cpm).to.equal(100);
expect(bid.width).to.equal(300);
expect(bid.height).to.equal(250);
expect(bid.creativeId).to.equal(15);
expect(bid.currency).to.equal('USD');
expect(bid.netRevenue).to.equal(true);
expect(bid.ttl).to.equal(360);
});

it('should return empty array when response is incorrect', () => {
expect(spec.interpretResponse({}).length).to.equal(0);
expect(spec.interpretResponse([]).length).to.equal(0);
});

it('should only use valid bid responses', () => {
expect(spec.interpretResponse(bidResArray).length).to.equal(1);
});
});
});