diff --git a/modules/koblerBidAdapter.js b/modules/koblerBidAdapter.js index 80aa038a9f7..816254bc7ad 100644 --- a/modules/koblerBidAdapter.js +++ b/modules/koblerBidAdapter.js @@ -12,7 +12,12 @@ const DEFAULT_TIMEOUT = 1000; const TIME_TO_LIVE_IN_SECONDS = 10 * 60; export const isBidRequestValid = function (bid) { - return !!(bid && bid.bidId && bid.params && bid.params.placementId); + if (!bid || !bid.bidId) { + return false; + } + + const sizes = deepAccess(bid, 'mediaTypes.banner.sizes', bid.sizes); + return isArray(sizes) && sizes.length > 0; }; export const buildRequests = function (validBidRequests, bidderRequest) { @@ -27,14 +32,11 @@ export const buildRequests = function (validBidRequests, bidderRequest) { }; export const interpretResponse = function (serverResponse) { - const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; const res = serverResponse.body; const bids = [] if (res) { res.seatbid.forEach(sb => { sb.bid.forEach(b => { - const adWithCorrectCurrency = b.adm - .replace(/\${AUCTION_PRICE_CURRENCY}/g, adServerPriceCurrency); bids.push({ requestId: b.impid, cpm: b.price, @@ -45,7 +47,7 @@ export const interpretResponse = function (serverResponse) { dealId: b.dealid, netRevenue: true, ttl: TIME_TO_LIVE_IN_SECONDS, - ad: adWithCorrectCurrency, + ad: b.adm, nurl: b.nurl, meta: { advertiserDomains: b.adomain @@ -58,13 +60,15 @@ export const interpretResponse = function (serverResponse) { }; export const onBidWon = function (bid) { - const cpm = bid.cpm || 0; - const cpmCurrency = bid.currency || SUPPORTED_CURRENCY; + // We intentionally use the price set by the publisher to replace the ${AUCTION_PRICE} macro + // instead of the `originalCpm` here. This notification is not used for billing, only for extra logging. + const publisherPrice = bid.cpm || 0; + const publisherCurrency = bid.currency || config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; const adServerPrice = deepAccess(bid, 'adserverTargeting.hb_pb', 0); const adServerPriceCurrency = config.getConfig('currency.adServerCurrency') || SUPPORTED_CURRENCY; if (isStr(bid.nurl) && bid.nurl !== '') { - const winNotificationUrl = replaceAuctionPrice(bid.nurl, bid.originalCpm || cpm) - .replace(/\${AUCTION_PRICE_CURRENCY}/g, cpmCurrency) + const winNotificationUrl = replaceAuctionPrice(bid.nurl, publisherPrice) + .replace(/\${AUCTION_PRICE_CURRENCY}/g, publisherCurrency) .replace(/\${AD_SERVER_PRICE}/g, adServerPrice) .replace(/\${AD_SERVER_PRICE_CURRENCY}/g, adServerPriceCurrency); triggerPixel(winNotificationUrl); @@ -83,7 +87,6 @@ export const onTimeout = function (timeoutDataArray) { auction_id: timeoutData.auctionId, bid_id: timeoutData.bidId, timeout: timeoutData.timeout, - placement_id: deepAccess(timeoutData, 'params.0.placementId'), page_url: pageUrl, }); const timeoutNotificationUrl = `${TIMEOUT_NOTIFICATION_ENDPOINT}?${query}`; @@ -106,8 +109,7 @@ function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { cur: [SUPPORTED_CURRENCY], imp: imps, device: { - devicetype: getDevice(), - geo: getGeo(validBidRequests[0]) + devicetype: getDevice() }, site: { page: pageUrl, @@ -128,14 +130,8 @@ function buildOpenRtbImpObject(validBidRequest) { banner: { format: buildFormatArray(sizes), w: mainSize[0], - h: mainSize[1], - ext: { - kobler: { - pos: getPosition(validBidRequest) - } - } + h: mainSize[1] }, - tagid: validBidRequest.params.placementId, bidfloor: floorInfo.floor, bidfloorcur: floorInfo.currency, pmp: buildPmpObject(validBidRequest) @@ -157,17 +153,8 @@ function getDevice() { return 2; // personal computers } -function getGeo(validBidRequest) { - if (validBidRequest.params.zip) { - return { - zip: validBidRequest.params.zip - }; - } - return {}; -} - function getTest(validBidRequest) { - return validBidRequest.params.test ? 1 : 0; + return validBidRequest.params && validBidRequest.params.test ? 1 : 0; } function getSizes(validBidRequest) { @@ -188,10 +175,6 @@ function buildFormatArray(sizes) { }); } -function getPosition(validBidRequest) { - return parseInt(validBidRequest.params.position) || 0; -} - function getFloorInfo(validBidRequest, mainSize) { if (typeof validBidRequest.getFloor === 'function') { const sizeParam = mainSize[0] === 0 && mainSize[1] === 0 ? '*' : mainSize; @@ -209,11 +192,11 @@ function getFloorInfo(validBidRequest, mainSize) { } function getFloorPrice(validBidRequest) { - return parseFloat(validBidRequest.params.floorPrice) || 0.0; + return parseFloat(deepAccess(validBidRequest, 'params.floorPrice', 0.0)); } function buildPmpObject(validBidRequest) { - if (validBidRequest.params.dealIds) { + if (validBidRequest.params && validBidRequest.params.dealIds && isArray(validBidRequest.params.dealIds)) { return { deals: validBidRequest.params.dealIds.map(dealId => { return { diff --git a/modules/koblerBidAdapter.md b/modules/koblerBidAdapter.md index 7a7b2388367..ba70be099f2 100644 --- a/modules/koblerBidAdapter.md +++ b/modules/koblerBidAdapter.md @@ -14,13 +14,14 @@ This adapter currently only supports Banner Ads. | Parameter (in `params`) | Scope | Type | Description | Example | | --- | --- | --- | --- | --- | -| placementId | Required | String | The identifier of the placement, it has to be issued by Kobler. | `'xjer0ch8'` | -| zip | Optional | String | Zip code of the user or the medium. When multiple ad units are submitted together, it is enough to set this parameter on the first one. | `'102 22'` | | test | Optional | Boolean | Whether the request is for testing only. When multiple ad units are submitted together, it is enough to set this parameter on the first one. Defaults to false. | `true` | | floorPrice | Optional | Float | Floor price in CPM and USD. Can be used as an alternative to the [Floors module](https://docs.prebid.org/dev-docs/modules/floors.html), which is also supported by this adapter. Defaults to 0. | `5.0` | -| position | Optional | Integer | The position of the ad unit. Can be used to differentiate between ad units if the same placement ID is used across multiple ad units. The first ad unit should have a `position` of 0, the second one should have a `position` of 1 and so on. Defaults to 0. | `1` | | dealIds | Optional | Array of Strings | Array of deal IDs. | `['abc328745', 'mxw243253']` | +## Implicit parameters + +Kobler identifies the placement using the combination of the page URL and the allowed sizes. As a result, it's important that the correct sizes are provided in `banner.sizes` in order for Kobler to correctly identify the placement. The main, desired format should be the first element of this array. + # Test Parameters ```javascript const adUnits = [{ @@ -31,17 +32,14 @@ This adapter currently only supports Banner Ads. } }, bids: [{ - bidder: 'kobler', - params: { - placementId: 'k5H7et3R0' - } + bidder: 'kobler' }] }]; ``` In order to see a sample bid from Kobler (without a proper setup), you have to also do the following: - Change the [`refererInfo` function](https://github.com/prebid/Prebid.js/blob/master/src/refererDetection.js) to return `'https://www.tv2.no/a/11734615'` as a [`referer`](https://github.com/prebid/Prebid.js/blob/caead3ccccc448e4cd09d074fd9f8833f56fe9b3/src/refererDetection.js#L169). This is necessary because Kobler only bids on recognized articles. -- Change the adapter's [`BIDDER_ENDPOINT`](https://github.com/prebid/Prebid.js/blob/master/modules/koblerBidAdapter.js#L8) to `'https://bid-service.dev.essrtb.com/bid/prebid_rtb_call'`. This endpoint belongs to the development server that is set up to always return a bid for the correct `placementId` and page URL combination. +- Change the adapter's [`BIDDER_ENDPOINT`](https://github.com/prebid/Prebid.js/blob/master/modules/koblerBidAdapter.js#L8) to `'https://bid-service.dev.essrtb.com/bid/prebid_rtb_call'`. This endpoint belongs to the development server that is set up to always return a bid for the correct format size and page URL combination. # Test Optional Parameters ```javascript @@ -55,11 +53,8 @@ In order to see a sample bid from Kobler (without a proper setup), you have to a bids: [{ bidder: 'kobler', params: { - placementId: 'k5H7et3R0', - zip: '102 22', test: true, floorPrice: 5.0, - position: 1, dealIds: ['abc328745', 'mxw243253'] } }] diff --git a/test/spec/modules/koblerBidAdapter_spec.js b/test/spec/modules/koblerBidAdapter_spec.js index 76c2c287989..d965a7d1c95 100644 --- a/test/spec/modules/koblerBidAdapter_spec.js +++ b/test/spec/modules/koblerBidAdapter_spec.js @@ -16,7 +16,7 @@ function createBidderRequest(auctionId, timeout, pageUrl) { } function createValidBidRequest(params, bidId, sizes) { - return { + const validBidRequest = { adUnitCode: 'adunit-code', bidId: bidId || '22c4871113f461', bidder: 'kobler', @@ -28,11 +28,12 @@ function createValidBidRequest(params, bidId, sizes) { sizes: sizes || [[300, 250], [320, 100]] } }, - params: params || { - placementId: 'tpw58278' - }, transactionTd: '04314114-15bd-4638-8664-bdb8bdc60bff' }; + if (params) { + validBidRequest.params = params; + } + return validBidRequest; } describe('KoblerAdapter', function () { @@ -56,7 +57,7 @@ describe('KoblerAdapter', function () { expect(result).to.be.false; }); - it('should not accept a request without params as valid', function () { + it('should not accept a request without mediaTypes and sizes as valid', function () { const bid = { bidId: 'e11768e8-3b71-4453-8698-0a2feb866589' }; @@ -66,11 +67,56 @@ describe('KoblerAdapter', function () { expect(result).to.be.false; }); - it('should not accept a request without placementId as valid', function () { + it('should not accept a request without mediaTypes and string sizes as valid', function () { const bid = { bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', - params: { - someParam: 'abc' + sizes: "string" + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without mediaTypes and empty sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + sizes: [] + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without mediaTypes.banner and sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + mediaTypes: {} + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without mediaTypes.banner and empty sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + sizes: [], + mediaTypes: {} + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without sizes and string mediaTypes.banner as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + mediaTypes: { + banner: "string" } }; @@ -79,12 +125,67 @@ describe('KoblerAdapter', function () { expect(result).to.be.false; }); - it('should accept a request with bidId and placementId as valid', function () { + it('should not accept a request without sizes and mediaTypes.banner.sizes as valid', function () { const bid = { bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', - params: { - someParam: 'abc', - placementId: '8bde0923-1409-4253-9594-495b58d931ba' + mediaTypes: { + banner: {} + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without sizes and string mediaTypes.banner.sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + mediaTypes: { + banner: { + sizes: "string" + } + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should not accept a request without sizes and empty mediaTypes.banner.sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + mediaTypes: { + banner: { + sizes: [] + } + } + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.false; + }); + + it('should accept a request with bidId and sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + sizes: [[5, 5]] + }; + + const result = spec.isBidRequestValid(bid); + + expect(result).to.be.true; + }); + + it('should accept a request with bidId and mediaTypes.banner.sizes as valid', function () { + const bid = { + bidId: 'e11768e8-3b71-4453-8698-0a2feb866589', + mediaTypes: { + banner: { + sizes: [[0, 0]] + } } }; @@ -114,13 +215,10 @@ describe('KoblerAdapter', function () { const firstSize = [400, 800]; const secondSize = [450, 950]; const sizes = [firstSize, secondSize]; - const placementId = 'tsjs86325'; const bidId = '3a56a019-4835-4f75-811c-76fac6853a2c'; const validBidRequests = [ createValidBidRequest( - { - placementId: placementId - }, + undefined, bidId, sizes ) @@ -132,7 +230,6 @@ describe('KoblerAdapter', function () { expect(openRtbRequest.imp.length).to.be.equal(1); expect(openRtbRequest.imp[0].id).to.be.equal(bidId); - expect(openRtbRequest.imp[0].tagid).to.be.equal(placementId); expect(openRtbRequest.imp[0].banner.w).to.be.equal(firstSize[0]); expect(openRtbRequest.imp[0].banner.h).to.be.equal(firstSize[1]); expect(openRtbRequest.imp[0].banner.format.length).to.be.equal(2); @@ -163,40 +260,10 @@ describe('KoblerAdapter', function () { expect(openRtbRequest.imp[0].banner.format[0].h).to.be.equal(0); }); - it('should use 0 as default position', function () { - const validBidRequests = [createValidBidRequest()]; - const bidderRequest = createBidderRequest(); - - const result = spec.buildRequests(validBidRequests, bidderRequest); - const openRtbRequest = JSON.parse(result.data); - - expect(openRtbRequest.imp.length).to.be.equal(1); - expect(openRtbRequest.imp[0].banner.ext.kobler.pos).to.be.equal(0); - }); - - it('should read zip from valid bid requests', function () { - const zip = '700 02'; - const validBidRequests = [ - createValidBidRequest( - { - placementId: 'nmah8324234', - zip: zip - } - ) - ]; - const bidderRequest = createBidderRequest(); - - const result = spec.buildRequests(validBidRequests, bidderRequest); - const openRtbRequest = JSON.parse(result.data); - - expect(openRtbRequest.device.geo.zip).to.be.equal(zip); - }); - it('should read test from valid bid requests', function () { const validBidRequests = [ createValidBidRequest( { - placementId: 'zwop842799', test: true } ) @@ -214,7 +281,6 @@ describe('KoblerAdapter', function () { const validBidRequests = [ createValidBidRequest( { - placementId: 'oqr3224234', floorPrice: floorPrice } ) @@ -228,55 +294,17 @@ describe('KoblerAdapter', function () { expect(openRtbRequest.imp[0].bidfloor).to.be.equal(floorPrice); }); - it('should read position from valid bid requests', function () { - const placementId = 'yzksf234592'; - const validBidRequests = [ - createValidBidRequest( - { - placementId: placementId, - position: 1 - } - ), - createValidBidRequest( - { - placementId: placementId, - position: 2 - } - ), - createValidBidRequest( - { - placementId: placementId, - position: 3 - } - ) - ]; - const bidderRequest = createBidderRequest(); - - const result = spec.buildRequests(validBidRequests, bidderRequest); - const openRtbRequest = JSON.parse(result.data); - - expect(openRtbRequest.imp.length).to.be.equal(3); - expect(openRtbRequest.imp[0].banner.ext.kobler.pos).to.be.equal(1); - expect(openRtbRequest.imp[0].tagid).to.be.equal(placementId); - expect(openRtbRequest.imp[1].banner.ext.kobler.pos).to.be.equal(2); - expect(openRtbRequest.imp[1].tagid).to.be.equal(placementId); - expect(openRtbRequest.imp[2].banner.ext.kobler.pos).to.be.equal(3); - expect(openRtbRequest.imp[2].tagid).to.be.equal(placementId); - }); - it('should read dealIds from valid bid requests', function () { const dealIds1 = ['78214682234823']; const dealIds2 = ['89913861235234', '27368423545328640']; const validBidRequests = [ createValidBidRequest( { - placementId: 'rsl1239823', dealIds: dealIds1 } ), createValidBidRequest( { - placementId: 'pqw234232', dealIds: dealIds2 } ) @@ -353,10 +381,7 @@ describe('KoblerAdapter', function () { const validBidRequests = [ createValidBidRequest( { - placementId: 'pcha322364', - zip: '0015', floorPrice: 5.6234, - position: 1, dealIds: ['623472534328234'] }, '953ee65d-d18a-484f-a840-d3056185a060', @@ -364,19 +389,14 @@ describe('KoblerAdapter', function () { ), createValidBidRequest( { - placementId: 'sdfgoi32y4', floorPrice: 3.2543, - position: 2, dealIds: ['92368234753283', '263845832942'] }, '8320bf79-9d90-4a17-87c6-5d505706a921', [[400, 500], [200, 250], [300, 350]] ), createValidBidRequest( - { - placementId: 'gwms2738647', - position: 3 - }, + undefined, 'd0de713b-32e3-4191-a2df-a007f08ffe72', [[800, 900]] ) @@ -406,14 +426,8 @@ describe('KoblerAdapter', function () { } ], w: 400, - h: 600, - ext: { - kobler: { - pos: 1 - } - } + h: 600 }, - tagid: 'pcha322364', bidfloor: 5.6234, bidfloorcur: 'USD', pmp: { @@ -442,14 +456,8 @@ describe('KoblerAdapter', function () { } ], w: 400, - h: 500, - ext: { - kobler: { - pos: 2 - } - } + h: 500 }, - tagid: 'sdfgoi32y4', bidfloor: 3.2543, bidfloorcur: 'USD', pmp: { @@ -473,24 +481,15 @@ describe('KoblerAdapter', function () { } ], w: 800, - h: 900, - ext: { - kobler: { - pos: 3 - } - } + h: 900 }, - tagid: 'gwms2738647', bidfloor: 0, bidfloorcur: 'USD', pmp: {} } ], device: { - devicetype: 2, - geo: { - zip: '0015' - } + devicetype: 2 }, site: { page: 'bid.kobler.no' @@ -531,7 +530,7 @@ describe('KoblerAdapter', function () { dealid: '', w: 320, h: 250, - adm: '', + adm: '', adomain: [ 'https://kobler.no' ] @@ -544,7 +543,7 @@ describe('KoblerAdapter', function () { dealid: '2783483223432342', w: 580, h: 400, - adm: '', + adm: '', adomain: [ 'https://bid.kobler.no' ] @@ -568,7 +567,7 @@ describe('KoblerAdapter', function () { dealId: '', netRevenue: true, ttl: 600, - ad: '', + ad: '', nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', meta: { advertiserDomains: [ @@ -586,7 +585,7 @@ describe('KoblerAdapter', function () { dealId: '2783483223432342', netRevenue: true, ttl: 600, - ad: '', + ad: '', nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=nbashgufvishdafjk23432&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', meta: { advertiserDomains: [ @@ -628,6 +627,7 @@ describe('KoblerAdapter', function () { } }); spec.onBidWon({ + originalCpm: 1.532, cpm: 8.341, currency: 'NOK', nurl: 'https://atag.essrtb.com/serve/prebid_win_notification?payload=sdhfusdaobfadslf234324&sp=${AUCTION_PRICE}&sp_cur=${AUCTION_PRICE_CURRENCY}&asp=${AD_SERVER_PRICE}&asp_cur=${AD_SERVER_PRICE_CURRENCY}', @@ -670,22 +670,14 @@ describe('KoblerAdapter', function () { auctionId: 'a1fba829-dd41-409f-acfb-b7b0ac5f30c6', bidId: 'ef236c6c-e934-406b-a877-d7be8e8a839a', timeout: 100, - params: [ - { - placementId: 'xrwg62731', - } - ], + params: [], }, { adUnitCode: 'adunit-code-2', auctionId: 'a1fba829-dd41-409f-acfb-b7b0ac5f30c6', bidId: 'ca4121c8-9a4a-46ba-a624-e9b64af206f2', timeout: 100, - params: [ - { - placementId: 'bc482234', - } - ], + params: [], } ]); @@ -693,12 +685,12 @@ describe('KoblerAdapter', function () { expect(utils.triggerPixel.getCall(0).args[0]).to.be.equal( 'https://bid.essrtb.com/notify/prebid_timeout?ad_unit_code=adunit-code&' + 'auction_id=a1fba829-dd41-409f-acfb-b7b0ac5f30c6&bid_id=ef236c6c-e934-406b-a877-d7be8e8a839a&timeout=100&' + - 'placement_id=xrwg62731&page_url=' + encodeURIComponent(getRefererInfo().referer) + 'page_url=' + encodeURIComponent(getRefererInfo().referer) ); expect(utils.triggerPixel.getCall(1).args[0]).to.be.equal( 'https://bid.essrtb.com/notify/prebid_timeout?ad_unit_code=adunit-code-2&' + 'auction_id=a1fba829-dd41-409f-acfb-b7b0ac5f30c6&bid_id=ca4121c8-9a4a-46ba-a624-e9b64af206f2&timeout=100&' + - 'placement_id=bc482234&page_url=' + encodeURIComponent(getRefererInfo().referer) + 'page_url=' + encodeURIComponent(getRefererInfo().referer) ); }); });