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

Add taboola bid adapter #1

Merged
merged 15 commits into from
Apr 24, 2022
Merged
257 changes: 257 additions & 0 deletions modules/taboolaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
'use strict';

import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
import {getWindowSelf, getWindowTop} from '../src/utils.js'
import {getStorageManager} from '../src/storageManager.js';

const BIDDER_CODE = 'taboola';
const GVLID = 42;
const CURRENCY = 'USD';
export const END_POINT_URL = 'http://hb.bidder.taboola.com/TaboolaHBOpenRTBRequestHandlerServlet';
const USER_ID = 'user-id';
const STORAGE_KEY = `taboola global:${USER_ID}`;
const COOKIE_KEY = 'trc_cookie_storage';

/**
* extract User Id by that order:
* 1. local storage
* 2. first party cookie
* 3. rendered trc
* 4. new user set it to 0
*/
export const userData = {
storageManager: getStorageManager(GVLID, BIDDER_CODE),
getUserId: () => {
const {getFromLocalStorage, getFromCookie, getFromTRC} = userData;

try {
return getFromLocalStorage() || getFromCookie() || getFromTRC();
} catch (ex) {
return 0;
mikizi marked this conversation as resolved.
Show resolved Hide resolved
}
},
getFromCookie() {
const {cookiesAreEnabled, getCookie} = userData.storageManager;
if (cookiesAreEnabled()) {
const cookieData = getCookie(COOKIE_KEY);
mikizi marked this conversation as resolved.
Show resolved Hide resolved
const userId = userData.getCookieDataByKey(cookieData, USER_ID);
if (userId) {
return userId;
}
}
},
getCookieDataByKey(cookieData, key) {
const [, value = ''] = cookieData.split(`${key}=`)
return value;
},
getFromLocalStorage() {
const {hasLocalStorage, localStorageIsEnabled, getDataFromLocalStorage} = userData.storageManager;

if (hasLocalStorage() && localStorageIsEnabled()) {
return getDataFromLocalStorage(STORAGE_KEY);
}
},
getFromTRC() {
return window.TRC ? window.TRC.user_id : 0;
}
}

export const internal = {
getPageUrl: (refererInfo = {}) => {
if (refererInfo.canonicalUrl) {
return refererInfo.canonicalUrl;
}

if (config.getConfig('pageUrl')) {
return config.getConfig('pageUrl');
}

try {
return getWindowTop().location.href;
} catch (e) {
return getWindowSelf().location.href;
mikizi marked this conversation as resolved.
Show resolved Hide resolved
}
},
getReferrer: (refererInfo = {}) => {
if (refererInfo.referer) {
return refererInfo.referer;
}

try {
return getWindowTop().document.referrer;
} catch (e) {
return getWindowSelf().document.referrer;
}
}
}

export const spec = {
supportedMediaTypes: [BANNER],
gvlid: GVLID,
code: BIDDER_CODE,
isBidRequestValid: (bidRequest) => {
return !!(bidRequest.sizes &&
bidRequest.params &&
bidRequest.params.publisherId &&
bidRequest.params.tagId);
},
buildRequests: (validBidRequests, bidderRequest) => {
const [bidRequest] = validBidRequests;
const {refererInfo, gdprConsent = {}, uspConsent} = bidderRequest;
const {bcat = [], badv = [], publisherId} = bidRequest.params;
const site = getSiteProperties(bidRequest.params, refererInfo);
const device = {ua: navigator.userAgent};
const imps = getImps(validBidRequests);
const user = {
buyeruid: userData.getUserId(gdprConsent, uspConsent),
ext: {}
};
const regs = {
coppa: 0,
ext: {}
};

if (gdprConsent.gdprApplies) {
user.ext.consent = bidderRequest.gdprConsent.consentString;
regs.ext.gdpr = 1;
}

if (uspConsent) {
regs.ext.us_privacy = uspConsent;
}

if (config.getConfig('coppa')) {
mikizi marked this conversation as resolved.
Show resolved Hide resolved
regs.coppa = 1
}

const request = {
id: bidderRequest.auctionId,
imp: imps,
site,
device,
source: {fd: 1},
tmax: bidderRequest.timeout,
bcat,
badv,
user,
regs
};

const url = [END_POINT_URL, publisherId].join('/');

return {
url,
method: 'POST',
data: JSON.stringify(request),
bids: validBidRequests
};
},
interpretResponse: (serverResponse, {bids}) => {
if (!bids) {
return [];
}

const {bidResponses, cur: currency} = getBidResponses(serverResponse);

if (!bidResponses) {
return [];
}

return bids.map((bid, id) => getBid(bid.bidId, currency, bidResponses[id])).filter(Boolean);
mikizi marked this conversation as resolved.
Show resolved Hide resolved
},
};

function getSiteProperties({publisherId, bcat = []}, refererInfo) {
const {getPageUrl, getReferrer} = internal;
return {
id: publisherId,
name: publisherId,
domain: window.location.host,
page: getPageUrl(refererInfo),
ref: getReferrer(refererInfo),
publisher: {
id: publisherId
},
content: {
language: navigator.language
}
}
}

function getImps(validBidRequests) {
return validBidRequests.map((bid, id) => {
const {tagId, bidfloor = null, bidfloorcur = CURRENCY} = bid.params;

return {
id: id + 1,
banner: getBanners(bid),
tagid: tagId,
bidfloor,
bidfloorcur,
};
});
}

function getBanners(bid) {
return getSizes(bid.sizes);
}

function getSizes(sizes) {
return {
format: sizes.map(size => {
return {
h: size[0],
w: size[1]
}
})
}
}

function getBidResponses({body}) {
if (!body || (body && !body.bidResponse)) {
return [];
}

const {seatbid, cur} = body.bidResponse;

if (!seatbid.length || !seatbid[0].bid) {
return [];
}

return {
bidResponses: seatbid[0].bid,
cur
};
}

function getBid(requestId, currency, bidResponse) {
if (!bidResponse) {
return;
}

const {
price: cpm, crid: creativeId, adm: ad, w: width, h: height, adomain: advertiserDomains, meta = {}
} = bidResponse;

if (advertiserDomains && advertiserDomains.length > 0) {
meta.advertiserDomains = advertiserDomains
}

return {
requestId,
ttl: 360,
mediaType: BANNER,
cpm,
creativeId,
currency,
ad,
width,
height,
meta,
netRevenue: false
};
}

registerBidder(spec);
49 changes: 49 additions & 0 deletions modules/taboolaBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Overview

```
Module Name: Taboola Adapter
Module Type: Bidder Adapter
Maintainer: prebid@taboola.com
```

# Description

Module that connects to Taboola bidder to fetch bids.
- Supports `display` format
- Uses `OpenRTB` standard

The Taboola Bidding adapter requires setup before beginning. Please contact us on prebid@taboola.com

# Test Display Parameters
``` javascript
var adUnits = [{
code: 'your-unit-container-id',
mediaTypes: {
banner: {
sizes: [[300, 250], [300,600]]
}
},
bids: [{
bidder: 'taboola',
params: {
tagId: 'Placement Name',
publisherId: 'your-publisher-id',
bidfloor: 0.25, // Optional - default is null
bcat: ['IAB1-1'], // Optional - default is []
badv: ['example.com'] // Optional - default is []
}
}]
}];
```

# Parameters

| Name | Scope | Description | Example | Type |
|----------------|----------|---------------------------------------------------------|----------------------------|--------------|
| `tagId` | required | Tag ID / Placement Name <br>(as provided by Taboola) | `'Below The Article'` | `String` |
| `publisherId` | required | Alphabetic Publisher ID <br>(as provided by Taboola) | `'acme-publishing'` | `String` |
| `bcat` | optional | List of blocked advertiser categories (IAB) | `['IAB1-1']` | `Array` |
| `badv` | optional | Blocked Advertiser Domains | `'example.com'` | `String Url` |
| `bidfloor` | optional | CPM bid floor | `0.25` | `Integer` |


Loading