diff --git a/modules/ixBidAdapter.js b/modules/ixBidAdapter.js index 89506a5659b..0778917efb1 100644 --- a/modules/ixBidAdapter.js +++ b/modules/ixBidAdapter.js @@ -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 @@ -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; @@ -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; } @@ -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 = {}; diff --git a/modules/ixBidAdapter.md b/modules/ixBidAdapter.md index 90592f258a6..e99c42408f2 100644 --- a/modules/ixBidAdapter.md +++ b/modules/ixBidAdapter.md @@ -29,8 +29,6 @@ Here are examples of both formats. var adUnits = [{ // ... - mediaType: 'banner', - sizes: [ [300, 250], [300, 600] @@ -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 @@ -75,15 +73,10 @@ object are detailed here. | Key | Scope | Type | Description | --- | --- | --- | --- -| 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:

-| 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:

-| bidFloor | Optional1 | Number |

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.

Examples:

| N/A -| bidFloorCur | Optional1 | String |

The currency of the bid floor.

Examples:

+| 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]` + -

- 1 bidFloor and bidFloorCur must - both be set when a bid floor is being configured. -

Setup Guide =========== @@ -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 @@ -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 diff --git a/test/spec/modules/ixBidAdapter_spec.js b/test/spec/modules/ixBidAdapter_spec.js index 36b2b0e9629..3bf0fb27280 100644 --- a/test/spec/modules/ixBidAdapter_spec.js +++ b/test/spec/modules/ixBidAdapter_spec.js @@ -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; @@ -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); @@ -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: { @@ -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', () => { @@ -300,7 +414,7 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, dealId: undefined } @@ -321,7 +435,7 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, dealId: undefined } @@ -342,7 +456,7 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'JPY', - ttl: 60, + ttl: 35, netRevenue: true, dealId: undefined } @@ -363,7 +477,7 @@ describe('IndexexchangeAdapter', () => { height: 250, ad: '', currency: 'USD', - ttl: 60, + ttl: 35, netRevenue: true, dealId: 'deal' }