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

Kobler adapter: remove outdated parameters, simplify testing #8445

Merged
merged 15 commits into from
May 26, 2022
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
99 changes: 54 additions & 45 deletions modules/koblerBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { deepAccess, isStr, replaceAuctionPrice, triggerPixel, isArray, parseQueryStringParameters, getWindowSelf } from '../src/utils.js';
import {
deepAccess,
getWindowSelf,
isArray,
isStr,
parseQueryStringParameters,
replaceAuctionPrice,
triggerPixel
} 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 DEV_BIDDER_ENDPOINT = 'https://bid-service.dev.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);
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) {
const bidderEndpoint = isTest(validBidRequests[0]) ? DEV_BIDDER_ENDPOINT : BIDDER_ENDPOINT;
return {
method: 'POST',
url: BIDDER_ENDPOINT,
url: bidderEndpoint,
data: buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest),
options: {
contentType: 'application/json'
Expand All @@ -27,14 +42,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,
Expand All @@ -45,7 +57,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
Expand All @@ -58,13 +70,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);
Expand All @@ -73,17 +87,13 @@ export const onBidWon = function (bid) {

export const onTimeout = function (timeoutDataArray) {
if (isArray(timeoutDataArray)) {
const refererInfo = getRefererInfo();
const pageUrl = (refererInfo && refererInfo.referer)
? refererInfo.referer
: window.location.href;
const pageUrl = getPageUrlFromRefererInfo();
timeoutDataArray.forEach(timeoutData => {
const query = parseQueryStringParameters({
ad_unit_code: timeoutData.adUnitCode,
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}`;
Expand All @@ -92,27 +102,41 @@ export const onTimeout = function (timeoutDataArray) {
}
};

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)
function getPageUrlFromRefererInfo() {
const refererInfo = getRefererInfo();
return (refererInfo && refererInfo.referer)
? refererInfo.referer
: window.location.href;
}

function getPageUrlFromRequest(validBidRequest, bidderRequest) {
// pageUrl is considered only when testing to ensure that non-test requests always contain the correct URL
if (isTest(validBidRequest) && config.getConfig('pageUrl')) {
return config.getConfig('pageUrl');
}

return (bidderRequest.refererInfo && bidderRequest.refererInfo.referer)
? bidderRequest.refererInfo.referer
: window.location.href;
}

function buildOpenRtbBidRequestPayload(validBidRequests, bidderRequest) {
const imps = validBidRequests.map(buildOpenRtbImpObject);
const timeout = bidderRequest.timeout || config.getConfig('bidderTimeout') || DEFAULT_TIMEOUT;
const pageUrl = getPageUrlFromRequest(validBidRequests[0], bidderRequest)
const request = {
id: bidderRequest.auctionId,
at: 1,
tmax: timeout,
cur: [SUPPORTED_CURRENCY],
imp: imps,
device: {
devicetype: getDevice(),
geo: getGeo(validBidRequests[0])
devicetype: getDevice()
},
site: {
page: pageUrl,
},
test: getTest(validBidRequests[0])
test: getTestAsNumber(validBidRequests[0])
};

return JSON.stringify(request);
Expand All @@ -128,14 +152,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)
Expand All @@ -157,17 +175,12 @@ function getDevice() {
return 2; // personal computers
}

function getGeo(validBidRequest) {
if (validBidRequest.params.zip) {
return {
zip: validBidRequest.params.zip
};
}
return {};
function getTestAsNumber(validBidRequest) {
return isTest(validBidRequest) ? 1 : 0;
}

function getTest(validBidRequest) {
return validBidRequest.params.test ? 1 : 0;
function isTest(validBidRequest) {
return validBidRequest.params && validBidRequest.params.test === true;
}

function getSizes(validBidRequest) {
Expand All @@ -188,10 +201,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;
Expand All @@ -209,11 +218,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 {
Expand Down
29 changes: 12 additions & 17 deletions modules/koblerBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ 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']` |
| Parameter (in `params`) | Scope | Type | Description | Example |
|-------------------------|----------|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|
| 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. Enables providing a custom URL through config.pageUrl. 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` |
| 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
Expand All @@ -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.
- Set the `test` parameter to `true`.
- Set `config.pageUrl` to `'https://www.tv2.no/mening-og-analyse/14555348/'`. This is necessary because Kobler only bids on recognized articles. Kobler runs its own test campaign to make sure there is always a bid for this specific page URL.

# Test Optional Parameters
```javascript
Expand All @@ -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']
}
}]
Expand Down
Loading