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

IX Adapter: Backward compatible with legacy config & reduced TTL #2744

Merged
merged 2 commits into from
Jul 3, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions modules/ixBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const BANNER_INSECURE_BID_URL = 'http://as.casalemedia.com/cygnus';
const SUPPORTED_AD_TYPES = [BANNER];
const ENDPOINT_VERSION = 7.2;
const CENT_TO_DOLLAR_FACTOR = 100;
const TIME_TO_LIVE = 60;
const TIME_TO_LIVE = 35;
const NET_REVENUE = true;

// Always start by assuming the protocol is HTTPS. This way, it will work
Expand Down Expand Up @@ -45,9 +45,15 @@ function bidToBannerImp(bid) {
imp.banner.topframe = utils.inIframe() ? 0 : 1;

imp.ext = {};
imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`;
imp.ext.siteID = bid.params.siteId;

if (bid.params.hasOwnProperty('id') &&
(typeof bid.params.id === 'string' || typeof bid.params.id === 'number')) {
imp.ext.sid = String(bid.params.id);
} else {
imp.ext.sid = `${bid.params.size[0]}x${bid.params.size[1]}`;
}

if (bid.params.hasOwnProperty('bidFloor') && bid.params.hasOwnProperty('bidFloorCur')) {
imp.bidfloor = bid.params.bidFloor;
imp.bidfloorcur = bid.params.bidFloorCur;
Expand Down Expand Up @@ -153,6 +159,14 @@ export const spec = {
return false;
}

if (bid.hasOwnProperty('mediaType') && bid.mediaType !== 'banner') {
return false;
}

if (bid.hasOwnProperty('mediaTypes') && !utils.deepAccess(bid, 'mediaTypes.banner.sizes')) {
return false;
}

if (typeof bid.params.siteId !== 'string' && typeof bid.params.siteId !== 'number') {
return false;
}
Expand Down Expand Up @@ -183,13 +197,9 @@ export const spec = {
for (let i = 0; i < validBidRequests.length; i++) {
validBidRequest = validBidRequests[i];

// If the bid request is for banner, then transform the bid request based on banner format.
if (utils.deepAccess(validBidRequest, 'mediaTypes.banner') ||
validBidRequest.mediaType === 'banner' ||
(validBidRequest.mediaType === undefined && utils.deepAccess(validBidRequest, 'mediaTypes.banner') === undefined)) {
bannerImp = bidToBannerImp(validBidRequest);
bannerImps.push(bannerImp);
}
// Transform the bid request based on the banner format.
bannerImp = bidToBannerImp(validBidRequest);
bannerImps.push(bannerImp);
}

const r = {};
Expand Down
25 changes: 10 additions & 15 deletions modules/ixBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ Here are examples of both formats.
var adUnits = [{
// ...

mediaType: 'banner',

sizes: [
[300, 250],
[300, 600]
Expand Down Expand Up @@ -63,7 +61,7 @@ var adUnits = [{
| Type | Support
| --- | ---
| Banner | Fully supported for all IX approved sizes.
| Video | Only in-stream supported.
| Video | Not supported.
| Native | Not supported.

# Bid Parameters
Expand All @@ -75,15 +73,10 @@ object are detailed here.

| Key | Scope | Type | Description
| --- | --- | --- | ---
| siteId | Required | String | <p>An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have.</p><p>Examples:<ul><li>`'3723'`</li><li>`'6482'`</li><li>`'3639'`</li></ul></p>
| size | Required | Number[] | <p>The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`.</p><p>Examples:<ul><li>`[300, 250]`</li><li>`[300, 600]`</li><li>`[728, 90]`</li></ul></p>
| bidFloor | Optional<sup>1</sup> | Number | <p>The minimum bid required to participate in an auction for this ad unit. Assuming the bid floor currency that is set has a main unit (e.g. dollars, pounds) and a sub-unit (e.g. cents, pence), the bid floor should be in decimal-point format. If the currency only has main a unit (e.g. JPY), then the bid floor should be a whole number.</p><p>Examples:<ul><li>10.26 USD => `bidFloor: 10.26`</li><li>13.41 GBP => `bidFloor: 13.41`</li><li>600 JPY => `bidFloor: 600`</li></ul></p> | N/A
| bidFloorCur | Optional<sup>1</sup> | String | <p>The currency of the bid floor.</p><p>Examples:<ul><li>`'USD'`</li><li>`'GBP'`</li><li>`'JPY'`</li></ul></p>
| siteId | Required | String | An IX-specific identifier that is associated with a specific size on this ad unit. This is similar to a placement ID or an ad unit ID that some other modules have. Examples: `'3723'`, `'6482'`, `'3639'`
| size | Required | Number[] | The single size associated with the site ID. It should be one of the sizes listed in the ad unit under `adUnits[].sizes` or `adUnits[].mediaTypes.banner.sizes`. Examples: `[300, 250]`, `[300, 600]`, `[728, 90]`


<p>
<sup>1</sup> <code>bidFloor</code> and <code>bidFloorCur</code> <b>must</b>
both be set when a bid floor is being configured.
</p>

Setup Guide
===========
Expand Down Expand Up @@ -154,7 +147,10 @@ var adUnits = [{
}];
```

Please note that you can re-use the existing `siteId` within the same flex position.
Please note that you can re-use the existing `siteId` within the same flex
position.



##### 2. Include `ixBidAdapter` in your build process

Expand Down Expand Up @@ -246,12 +242,11 @@ the rest will be ignored.

To avoid this situation, ensure that when `pbjs.requestBid` is invoked, that the
number of bid objects (i.e. `adUnits[].bids`) with `adUnits[].bids[].bidder` set
to `'ix'` across all ad units that bids are being requested for does not exceed
20.
to `'ix'` across all ad units that bids are being requested for does not exceed 20.

### Time-To-Live (TTL)

All bids received from IX have a TTL of 60 seconds, after which time they become
All bids received from IX have a TTL of 35 seconds, after which time they become
invalid.

If an invalid bid wins, and its associated ad is rendered, it will not count
Expand Down
152 changes: 133 additions & 19 deletions test/spec/modules/ixBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,65 @@ describe('IndexexchangeAdapter', () => {
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when mediaTypes is not banner', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.mediaTypes = {
video: {
sizes: [[300, 250]]
}
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when mediaTypes.banner does not have sizes', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.mediaTypes = {
banner: {
size: [[300, 250]]
}
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when mediaType is not banner', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
delete bid.params.mediaTypes;
bid.mediaType = 'banne';
bid.sizes = [[300, 250]];
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when mediaType is video', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
delete bid.params.mediaTypes;
bid.mediaType = 'video';
bid.sizes = [[300, 250]];
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false when mediaType is native', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
delete bid.params.mediaTypes;
bid.mediaType = 'native';
bid.sizes = [[300, 250]];
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return true when mediaType is missing and has sizes', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
delete bid.mediaTypes;
bid.sizes = [[300, 250]];
expect(spec.isBidRequestValid(bid)).to.equal(true);
});

it('should return true when mediaType is banner', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
delete bid.mediaTypes;
bid.mediaType = 'banner';
bid.sizes = [[300, 250]];
expect(spec.isBidRequestValid(bid)).to.equal(true);
});

it('should return false when there is only bidFloor', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.params.bidFloor = 50;
Expand Down Expand Up @@ -144,6 +203,12 @@ describe('IndexexchangeAdapter', () => {
const requestMethod = request.method;
const query = request.data;

const bidWithoutMediaType = utils.deepClone(DEFAULT_BANNER_VALID_BID);
delete bidWithoutMediaType[0].mediaTypes;
bidWithoutMediaType[0].sizes = [[300, 250], [300, 600]];
const requestWithoutMediaType = spec.buildRequests(bidWithoutMediaType);
const queryWithoutMediaType = requestWithoutMediaType.data;

it('request should be made to IX endpoint with GET method', () => {
expect(requestMethod).to.equal('GET');
expect(requestUrl).to.equal(IX_ENDPOINT);
Expand Down Expand Up @@ -199,6 +264,70 @@ describe('IndexexchangeAdapter', () => {
expect(impression.bidfloorcur).to.equal(bid.params.bidFloorCur);
});

it('payload without mediaType should have correct format and value', () => {
const payload = JSON.parse(queryWithoutMediaType.r);

expect(payload.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidderRequestId);
expect(payload.site).to.exist;
expect(payload.site.page).to.exist;
expect(payload.site.page).to.contain('http');
expect(payload.site.ref).to.exist;
expect(payload.site.ref).to.be.a('string');
expect(payload.ext).to.exist;
expect(payload.ext.source).to.equal('prebid');
expect(payload.imp).to.exist;
expect(payload.imp).to.be.an('array');
expect(payload.imp).to.have.lengthOf(1);
});

it('impression without mediaType should have correct format and value', () => {
const impression = JSON.parse(queryWithoutMediaType.r).imp[0];
const sidValue = `${DEFAULT_BANNER_VALID_BID[0].params.size[0].toString()}x${DEFAULT_BANNER_VALID_BID[0].params.size[1].toString()}`;

expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId);
expect(impression.banner).to.exist;
expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);
expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]);
expect(impression.banner.topframe).to.exist;
expect(impression.banner.topframe).to.be.oneOf([0, 1]);
expect(impression.ext).to.exist;
expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString());
expect(impression.ext.sid).to.equal(sidValue);
});

it('impression should have sid if id is configured as number', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.params.id = 50;
const requestBidFloor = spec.buildRequests([bid]);
const impression = JSON.parse(requestBidFloor.data.r).imp[0];

expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId);
expect(impression.banner).to.exist;
expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);
expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]);
expect(impression.banner.topframe).to.exist;
expect(impression.banner.topframe).to.be.oneOf([0, 1]);
expect(impression.ext).to.exist;
expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString());
expect(impression.ext.sid).to.equal('50');
});

it('impression should have sid if id is configured as string', () => {
const bid = utils.deepClone(DEFAULT_BANNER_VALID_BID[0]);
bid.params.id = 'abc';
const requestBidFloor = spec.buildRequests([bid]);
const impression = JSON.parse(requestBidFloor.data.r).imp[0];
expect(impression.id).to.equal(DEFAULT_BANNER_VALID_BID[0].bidId);
expect(impression.banner).to.exist;
expect(impression.banner.w).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[0]);
expect(impression.banner.h).to.equal(DEFAULT_BANNER_VALID_BID[0].params.size[1]);
expect(impression.banner.topframe).to.exist;
expect(impression.banner.topframe).to.be.oneOf([0, 1]);
expect(impression.ext).to.exist;
expect(impression.ext.siteID).to.equal(DEFAULT_BANNER_VALID_BID[0].params.siteId.toString());
expect(impression.ext.sid).to.equal('abc');
});

it('should add first party data to page url in bid request if it exists in config', () => {
config.setConfig({
ix: {
Expand Down Expand Up @@ -272,21 +401,6 @@ describe('IndexexchangeAdapter', () => {

expect(requestStringTimeout.data.t).to.be.undefined;
});

it('should default to assuming media type is banner', () => {
const bidsWithoutMediaType = [
Object.assign({}, DEFAULT_BANNER_VALID_BID[0])
];
delete bidsWithoutMediaType[0].mediaTypes;

const request = spec.buildRequests(bidsWithoutMediaType);
const payload = JSON.parse(request.data.r);

expect(payload.id).to.equal(bidsWithoutMediaType[0].bidderRequestId);
expect(payload.imp).to.exist;
expect(payload.imp).to.be.an('array');
expect(payload.imp).to.have.lengthOf(1);
});
});

describe('interpretResponseBanner', () => {
Expand All @@ -300,7 +414,7 @@ describe('IndexexchangeAdapter', () => {
height: 250,
ad: '<a target="_blank" href="http://www.indexexchange.com"></a>',
currency: 'USD',
ttl: 60,
ttl: 35,
netRevenue: true,
dealId: undefined
}
Expand All @@ -321,7 +435,7 @@ describe('IndexexchangeAdapter', () => {
height: 250,
ad: '<a target="_blank" href="http://www.indexexchange.com"></a>',
currency: 'USD',
ttl: 60,
ttl: 35,
netRevenue: true,
dealId: undefined
}
Expand All @@ -342,7 +456,7 @@ describe('IndexexchangeAdapter', () => {
height: 250,
ad: '<a target="_blank" href="http://www.indexexchange.com"></a>',
currency: 'JPY',
ttl: 60,
ttl: 35,
netRevenue: true,
dealId: undefined
}
Expand All @@ -363,7 +477,7 @@ describe('IndexexchangeAdapter', () => {
height: 250,
ad: '<a target="_blank" href="http://www.indexexchange.com"></a>',
currency: 'USD',
ttl: 60,
ttl: 35,
netRevenue: true,
dealId: 'deal'
}
Expand Down