-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Kobler Bid Adapter: add new bid adapter (#6479)
* Implemented Kobler bidder adapter. * Added missing '' to dealId example in parameters table. * Added information on supporting the Floors module. * Implemented tests for isBidRequestValid. * Implemented tests for inherited functions. * Removed unnecessary conditions and quotation marks. * Added TODO about deciding what to do in debug mode. * Added TODO about checking which currencies are allowed. * Added information on parameters read from the first bid only. * Fixed missing indexing operator when checking if mainSize is 0x0. * Implemented tests for buildRequests. * Implemented tests for interpretResponse. * Implemented tests for onBidWon. * Implemented tests for onTimeout. * Added some missing semicolons. * Removed TODO about allowed currencies. * Removed setting test in debug mode and related TODOs. * Removed optional pos parameter. * Removed optional bidfloor parameter and use floorPrice instead of floorprice. * Added support for multiple deal ID parameters. * Fixed formatting. * Added more explanation about the value of position param. * Moved pos property into Kobler-specific banner extension. * Simplifications based on PR comments. * Use getRefererInfo to get page URL for timeout notifications. * Removed TODO about auction type. * Added information on how to generate a sample bid. * Removed reading currency from currency.adServerCurrency.
- Loading branch information
Showing
3 changed files
with
992 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
import * as utils from '../src/utils.js'; | ||
import {config} from '../src/config.js'; | ||
import {registerBidder} from '../src/adapters/bidderFactory.js'; | ||
import {BANNER} from '../src/mediaTypes.js'; | ||
import {getRefererInfo} from '../src/refererDetection.js'; | ||
|
||
const BIDDER_CODE = 'kobler'; | ||
const BIDDER_ENDPOINT = 'https://bid.essrtb.com/bid/prebid_rtb_call'; | ||
const TIMEOUT_NOTIFICATION_ENDPOINT = 'https://bid.essrtb.com/notify/prebid_timeout'; | ||
const SUPPORTED_CURRENCY = 'USD'; | ||
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); | ||
}; | ||
|
||
export const buildRequests = function (validBidRequests, bidderRequest) { | ||
return { | ||
method: 'POST', | ||
url: BIDDER_ENDPOINT, | ||
data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest), | ||
options: { | ||
contentType: 'application/json' | ||
} | ||
}; | ||
}; | ||
|
||
export const interpretResponse = function (serverResponse) { | ||
const res = serverResponse.body; | ||
const bids = [] | ||
if (res) { | ||
res.seatbid.forEach(sb => { | ||
sb.bid.forEach(b => { | ||
bids.push({ | ||
requestId: b.impid, | ||
cpm: b.price, | ||
currency: res.cur, | ||
width: b.w, | ||
height: b.h, | ||
creativeId: b.crid, | ||
dealId: b.dealid, | ||
netRevenue: true, | ||
ttl: TIME_TO_LIVE_IN_SECONDS, | ||
ad: b.adm, | ||
nurl: b.nurl, | ||
meta: { | ||
advertiserDomains: b.adomain | ||
} | ||
}) | ||
}) | ||
}); | ||
} | ||
return bids; | ||
}; | ||
|
||
export const onBidWon = function (bid) { | ||
const cpm = bid.cpm || 0; | ||
const adServerPrice = utils.deepAccess(bid, 'adserverTargeting.hb_pb', 0); | ||
if (utils.isStr(bid.nurl) && bid.nurl !== '') { | ||
const winNotificationUrl = utils.replaceAuctionPrice(bid.nurl, cpm) | ||
.replace(/\${AD_SERVER_PRICE}/g, adServerPrice); | ||
utils.triggerPixel(winNotificationUrl); | ||
} | ||
}; | ||
|
||
export const onTimeout = function (timeoutDataArray) { | ||
if (utils.isArray(timeoutDataArray)) { | ||
const refererInfo = getRefererInfo(); | ||
const pageUrl = (refererInfo && refererInfo.referer) | ||
? refererInfo.referer | ||
: window.location.href; | ||
timeoutDataArray.forEach(timeoutData => { | ||
const query = utils.parseQueryStringParameters({ | ||
ad_unit_code: timeoutData.adUnitCode, | ||
auction_id: timeoutData.auctionId, | ||
bid_id: timeoutData.bidId, | ||
timeout: timeoutData.timeout, | ||
placement_id: utils.deepAccess(timeoutData, 'params.0.placementId'), | ||
page_url: pageUrl, | ||
}); | ||
const timeoutNotificationUrl = `${TIMEOUT_NOTIFICATION_ENDPOINT}?${query}`; | ||
utils.triggerPixel(timeoutNotificationUrl); | ||
}); | ||
} | ||
}; | ||
|
||
function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) { | ||
const imps = validBidRequests.map(buildOpenRtbImpObject); | ||
const timeout = bidderRequest.timeout || config.getConfig('bidderTimeout') || DEFAULT_TIMEOUT; | ||
const pageUrl = (bidderRequest.refererInfo && bidderRequest.refererInfo.referer) | ||
? bidderRequest.refererInfo.referer | ||
: window.location.href; | ||
|
||
const request = { | ||
id: bidderRequest.auctionId, | ||
at: 1, | ||
tmax: timeout, | ||
cur: [SUPPORTED_CURRENCY], | ||
imp: imps, | ||
device: { | ||
devicetype: getDevice(), | ||
geo: getGeo(validBidRequests[0]) | ||
}, | ||
site: { | ||
page: pageUrl, | ||
}, | ||
test: getTest(validBidRequests[0]) | ||
}; | ||
|
||
return JSON.stringify(request); | ||
} | ||
|
||
function buildOpenRtbImpObject(validBidRequest) { | ||
const sizes = getSizes(validBidRequest); | ||
const mainSize = sizes[0]; | ||
const floorInfo = getFloorInfo(validBidRequest, mainSize); | ||
|
||
return { | ||
id: validBidRequest.bidId, | ||
banner: { | ||
format: buildFormatArray(sizes), | ||
w: mainSize[0], | ||
h: mainSize[1], | ||
ext: { | ||
kobler: { | ||
pos: getPosition(validBidRequest) | ||
} | ||
} | ||
}, | ||
tagid: validBidRequest.params.placementId, | ||
bidfloor: floorInfo.floor, | ||
bidfloorcur: floorInfo.currency, | ||
pmp: buildPmpObject(validBidRequest) | ||
}; | ||
} | ||
|
||
function getDevice() { | ||
const ws = utils.getWindowSelf(); | ||
const ua = ws.navigator.userAgent; | ||
|
||
if (/(tablet|ipad|playbook|silk|android 3.0|xoom|sch-i800|kindle)|(android(?!.*mobi))/i | ||
.test(ua.toLowerCase())) { | ||
return 5; // tablet | ||
} | ||
if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series([46])0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i | ||
.test(ua.toLowerCase())) { | ||
return 4; // phone | ||
} | ||
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; | ||
} | ||
|
||
function getSizes(validBidRequest) { | ||
const sizes = utils.deepAccess(validBidRequest, 'mediaTypes.banner.sizes', validBidRequest.sizes); | ||
if (utils.isArray(sizes) && sizes.length > 0) { | ||
return sizes; | ||
} | ||
|
||
return [[0, 0]]; | ||
} | ||
|
||
function buildFormatArray(sizes) { | ||
return sizes.map(size => { | ||
return { | ||
w: size[0], | ||
h: size[1] | ||
}; | ||
}); | ||
} | ||
|
||
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; | ||
return validBidRequest.getFloor({ | ||
currency: SUPPORTED_CURRENCY, | ||
mediaType: BANNER, | ||
size: sizeParam | ||
}); | ||
} else { | ||
return { | ||
currency: SUPPORTED_CURRENCY, | ||
floor: getFloorPrice(validBidRequest) | ||
}; | ||
} | ||
} | ||
|
||
function getFloorPrice(validBidRequest) { | ||
return parseFloat(validBidRequest.params.floorPrice) || 0.0; | ||
} | ||
|
||
function buildPmpObject(validBidRequest) { | ||
if (validBidRequest.params.dealIds) { | ||
return { | ||
deals: validBidRequest.params.dealIds.map(dealId => { | ||
return { | ||
id: dealId | ||
}; | ||
}) | ||
}; | ||
} | ||
return {}; | ||
} | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: [BANNER], | ||
isBidRequestValid, | ||
buildRequests, | ||
interpretResponse, | ||
onBidWon, | ||
onTimeout | ||
}; | ||
|
||
registerBidder(spec); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# Overview | ||
|
||
**Module Name**: Kobler Bidder Adapter | ||
**Module Type**: Bidder Adapter | ||
**Maintainer**: bidding-support@kobler.no | ||
|
||
# Description | ||
|
||
Connects to Kobler's demand sources. | ||
|
||
This adapter currently only supports Banner Ads. | ||
|
||
# Parameters | ||
|
||
| 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']` | | ||
|
||
# Test Parameters | ||
```javascript | ||
const adUnits = [{ | ||
code: 'div-gpt-ad-1460505748561-1', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[320, 250], [300, 250]], | ||
} | ||
}, | ||
bids: [{ | ||
bidder: 'kobler', | ||
params: { | ||
placementId: 'k5H7et3R0' | ||
} | ||
}] | ||
}]; | ||
``` | ||
|
||
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. | ||
|
||
# Test Optional Parameters | ||
```javascript | ||
const adUnits = [{ | ||
code: 'div-gpt-ad-1460505748561-1', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[320, 250], [300, 250]], | ||
} | ||
}, | ||
bids: [{ | ||
bidder: 'kobler', | ||
params: { | ||
placementId: 'k5H7et3R0', | ||
zip: '102 22', | ||
test: true, | ||
floorPrice: 5.0, | ||
position: 1, | ||
dealIds: ['abc328745', 'mxw243253'] | ||
} | ||
}] | ||
}]; | ||
``` |
Oops, something went wrong.