Skip to content

Commit

Permalink
Taboola bid adapter: initial release (#8483)
Browse files Browse the repository at this point in the history
* create taboola adapter

* create taboola adapter md

* taboolaBidAdapter.js - small fixes
taboolaBidAdapter_spec.js - new UT

* taboolaBidAdapter.js - small fixes
taboolaBidAdapter_spec.js - new UT

* update the md

* update the Maintainer email

* * update MD page
* refactor code for better readability
* small fix in UT

* * add privacy to the request builder
* add relevant Ut
* small fixes in UT

* * code refactoring + add more accurate way to get page url and referer
* add relevant Ut
* small fixes in md

* * code refactoring + gte user id
* add relevant Ut
* small fixes

* * code refactoring + gte user id
* add relevant Ut
* small fixes

* * update end point url
* update UT
* Update banner End point structure

* small fixes + update epi url

* remove the destruction from the bidResponse property

* (update the unit tests) remove the destruction from the bidResponse property

* fix tests

* fix tests - run stubs on each test

* rerun because of another adapter flaky test

* rerun because of another adapter flaky test

* fix cors issue, switch between height, width position

* update badv, bcat to be based in the ortb2 to support prebid 7 new protocols + update Ut

* retry run circleci

* retry run circleci

* pull from upstream
update md (placement + pub )

* update badv, bcat UT

* rerun build

* rerun build

* support storageAllowed restriction on unit tests for prebid 7

* add it also to the aftereach

* add it also to the aftereach
  • Loading branch information
mikizi committed May 26, 2022
1 parent b898bba commit 7116c9c
Show file tree
Hide file tree
Showing 3 changed files with 769 additions and 0 deletions.
265 changes: 265 additions & 0 deletions modules/taboolaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
'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: GVLID, bidderCode: BIDDER_CODE}),
getUserId: () => {
const {getFromLocalStorage, getFromCookie, getFromTRC} = userData;

try {
return getFromLocalStorage() || getFromCookie() || getFromTRC();
} catch (ex) {
return 0;
}
},
getFromCookie() {
const {cookiesAreEnabled, getCookie} = userData.storageManager;
if (cookiesAreEnabled()) {
const cookieData = getCookie(COOKIE_KEY);
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;
}
},
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 {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')) {
regs.coppa = 1
}

const ortb2 = config.getConfig('ortb2') || {
badv: [],
bcat: []
};

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

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

return {
url,
method: 'POST',
data: JSON.stringify(request),
bids: validBidRequests,
options: {
withCredentials: false
},
};
},
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);
},
};

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 {
w: size[0],
h: size[1]
}
})
}
}

function getBidResponses({body}) {
if (!body) {
return [];
}

const {seatbid, cur} = body;

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: 'tester-placement', // Placement Name
publisherId: 'tester-pub', // 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

0 comments on commit 7116c9c

Please sign in to comment.