Skip to content
Open
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
56 changes: 44 additions & 12 deletions modules/taboolaBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {BANNER, NATIVE} from '../src/mediaTypes.js';
import {config} from '../src/config.js';
import {deepSetValue, getWindowSelf, replaceAuctionPrice, isArray, safeJSONParse, isPlainObject, getWinDimensions} from '../src/utils.js';
import {getStorageManager} from '../src/storageManager.js';
Expand All @@ -15,7 +15,8 @@ import {getBoundingClientRect} from '../libraries/boundingClientRect/boundingCli
const BIDDER_CODE = 'taboola';
const GVLID = 42;
const CURRENCY = 'USD';
export const END_POINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction';
export const BANNER_ENDPOINT_URL = 'https://display.bidder.taboola.com/OpenRTB/TaboolaHB/auction';
export const NATIVE_ENDPOINT_URL = 'https://native.bidder.taboola.com/OpenRTB/TaboolaHB/auction';
export const USER_SYNC_IMG_URL = 'https://trc.taboola.com/sg/prebidJS/1/cm';
export const USER_SYNC_IFRAME_URL = 'https://cdn.taboola.com/scripts/prebid_iframe_sync.html';
const USER_ID = 'user-id';
Expand Down Expand Up @@ -169,7 +170,6 @@ export function getElementSignals(adUnitCode) {
const converter = ortbConverter({
context: {
netRevenue: true,
mediaType: BANNER,
ttl: 300
},
imp(buildImp, bidRequest, context) {
Expand All @@ -183,12 +183,24 @@ const converter = ortbConverter({
return reqData;
},
bidResponse(buildBidResponse, bid, context) {
const { mediaType } = getMediaType(context.bidRequest);
context.mediaType = mediaType;

if (context.mediaType === NATIVE) {
const admObj = safeJSONParse(bid.adm);
if (admObj?.native) {
bid.adm = JSON.stringify(admObj.native);
}
}

const bidResponse = buildBidResponse(bid, context);
bidResponse.nurl = bid.nurl;
if (bid.burl) {
bidResponse.burl = bid.burl;
}
bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price);
if (bidResponse.mediaType !== NATIVE) {
bidResponse.ad = replaceAuctionPrice(bid.adm, bid.price);
}
if (bid.ext && bid.ext.dchain) {
deepSetValue(bidResponse, 'meta.dchain', bid.ext.dchain);
}
Expand All @@ -197,14 +209,18 @@ const converter = ortbConverter({
});

export const spec = {
supportedMediaTypes: [BANNER],
supportedMediaTypes: [BANNER, NATIVE],
gvlid: GVLID,
code: BIDDER_CODE,
isBidRequestValid: (bidRequest) => {
return !!(bidRequest.sizes &&
bidRequest.params &&
const hasPublisherAndTag = !!(bidRequest.params &&
bidRequest.params.publisherId &&
bidRequest.params.tagId);
if (!hasPublisherAndTag) {
return false;
}
const { hasBanner, hasNative } = getMediaType(bidRequest);
return hasBanner || hasNative;
},
buildRequests: (validBidRequests, bidderRequest) => {
const [bidRequest] = validBidRequests;
Expand All @@ -215,7 +231,9 @@ export const spec = {
context: { auctionId }
});
const {publisherId} = bidRequest.params;
const url = END_POINT_URL + '?publisher=' + publisherId;
const { mediaType } = getMediaType(bidRequest);
const baseUrl = mediaType === NATIVE ? NATIVE_ENDPOINT_URL : BANNER_ENDPOINT_URL;
const url = baseUrl + '?publisher=' + publisherId;

return {
url,
Expand Down Expand Up @@ -433,12 +451,16 @@ function fillTaboolaReqData(bidderRequest, bidRequest, data, context) {

function fillTaboolaImpData(bid, imp) {
const {tagId, position} = bid.params;
imp.banner = getBanners(bid, position);
imp.tagid = tagId;
const { mediaType, hasBanner } = getMediaType(bid);
if (hasBanner) {
imp.banner = getBanners(bid.mediaTypes.banner.sizes, position);
}

imp.tagid = tagId;
if (typeof bid.getFloor === 'function') {
const floorInfo = bid.getFloor({
currency: CURRENCY,
mediaType: mediaType,
size: '*'
});
if (isPlainObject(floorInfo) && floorInfo.currency === CURRENCY && !isNaN(parseFloat(floorInfo.floor))) {
Expand Down Expand Up @@ -476,9 +498,9 @@ function fillTaboolaImpData(bid, imp) {
}
}

function getBanners(bid, pos) {
function getBanners(sizes, pos) {
return {
...getSizes(bid.sizes),
...getSizes(sizes),
pos: pos
}
}
Expand All @@ -494,4 +516,14 @@ function getSizes(sizes) {
}
}

function getMediaType(bidRequest) {
const hasBanner = !!bidRequest?.mediaTypes?.banner?.sizes;
const hasNative = !!bidRequest?.mediaTypes?.native;
return {
hasBanner,
hasNative,
mediaType: hasNative && !hasBanner ? NATIVE : BANNER
};
}

registerBidder(spec);
201 changes: 193 additions & 8 deletions test/spec/modules/taboolaBidAdapter_spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expect} from 'chai';
import {spec, internal, END_POINT_URL, userData, EVENT_ENDPOINT, detectBot, getPageVisibility} from 'modules/taboolaBidAdapter.js';
import {spec, internal, BANNER_ENDPOINT_URL, userData, EVENT_ENDPOINT, detectBot, getPageVisibility} from 'modules/taboolaBidAdapter.js';
import {config} from '../../../src/config.js'
import * as utils from '../../../src/utils.js'
import {server} from '../../mocks/xhr.js'
Expand Down Expand Up @@ -33,7 +33,11 @@ describe('Taboola Adapter', function () {
})

const displayBidRequestParams = {
sizes: [[300, 250], [300, 600]]
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
}
}
}

const createBidRequest = () => ({
Expand Down Expand Up @@ -270,18 +274,18 @@ describe('Taboola Adapter', function () {
const expectedData = {
'imp': [{
'id': res.data.imp[0].id,
'secure': 1,
'banner': {
format: [{
w: displayBidRequestParams.sizes[0][0],
h: displayBidRequestParams.sizes[0][1]
w: displayBidRequestParams.mediaTypes.banner.sizes[0][0],
h: displayBidRequestParams.mediaTypes.banner.sizes[0][1]
},
{
w: displayBidRequestParams.sizes[1][0],
h: displayBidRequestParams.sizes[1][1]
w: displayBidRequestParams.mediaTypes.banner.sizes[1][0],
h: displayBidRequestParams.mediaTypes.banner.sizes[1][1]
}
]
},
'secure': 1,
'tagid': commonBidRequest.params.tagId,
'bidfloor': null,
'bidfloorcur': 'USD',
Expand Down Expand Up @@ -315,7 +319,7 @@ describe('Taboola Adapter', function () {
'ext': res.data.ext
};

expect(res.url).to.equal(`${END_POINT_URL}?publisher=${commonBidRequest.params.publisherId}`);
expect(res.url).to.equal(`${BANNER_ENDPOINT_URL}?publisher=${commonBidRequest.params.publisherId}`);
expect(JSON.stringify(res.data)).to.deep.equal(JSON.stringify(expectedData));
expect(res.data.ext.prebid.version).to.equal('$prebid.version$');
});
Expand Down Expand Up @@ -1980,4 +1984,185 @@ describe('Taboola Adapter', function () {
});
});
})

describe('native', function () {
const commonBidderRequest = {
bidderRequestId: 'mock-uuid',
refererInfo: {
page: 'https://example.com/ref',
ref: 'https://ref',
domain: 'example.com',
},
ortb2: {
device: {
ua: navigator.userAgent,
},
}
};

const nativeBidRequestParams = {
mediaTypes: {
native: {
title: {required: true, len: 150},
image: {required: true, sizes: [300, 250]},
sponsoredBy: {required: true}
}
}
};

describe('isBidRequestValid', function () {
it('should return true for valid native bid without sizes', function () {
const bid = {
bidder: 'taboola',
params: {
publisherId: 'publisherId',
tagId: 'native-placement'
},
...nativeBidRequestParams
};
expect(spec.isBidRequestValid(bid)).to.equal(true);
});

it('should return false for native bid without publisherId', function () {
const bid = {
bidder: 'taboola',
params: {
tagId: 'native-placement'
},
...nativeBidRequestParams
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});

it('should return false for native bid without tagId', function () {
const bid = {
bidder: 'taboola',
params: {
publisherId: 'publisherId'
},
...nativeBidRequestParams
};
expect(spec.isBidRequestValid(bid)).to.equal(false);
});
});

describe('buildRequests', function () {
if (FEATURES.NATIVE) {
it('should build native request without banner imp', function () {
const nativeBidRequest = {
bidder: 'taboola',
params: {
publisherId: 'publisherId',
tagId: 'native-placement'
},
...nativeBidRequestParams,
nativeOrtbRequest: {
ver: '1.2',
assets: [
{id: 1, required: 1, title: {len: 150}},
{id: 2, required: 1, img: {type: 3, w: 300, h: 250}},
{id: 3, required: 1, data: {type: 1}}
]
},
bidId: utils.generateUUID(),
auctionId: utils.generateUUID(),
};

const res = spec.buildRequests([nativeBidRequest], commonBidderRequest);

expect(res.data.imp[0]).to.not.have.property('banner');
expect(res.data.imp[0]).to.have.property('native');
expect(res.data.imp[0].tagid).to.equal('native-placement');
});
}

it('should build banner request without native imp', function () {
const bannerBidRequest = {
bidder: 'taboola',
params: {
publisherId: 'publisherId',
tagId: 'banner-placement'
},
mediaTypes: {
banner: {
sizes: [[300, 250]]
}
},
bidId: utils.generateUUID(),
auctionId: utils.generateUUID(),
};

const res = spec.buildRequests([bannerBidRequest], commonBidderRequest);

expect(res.data.imp[0]).to.have.property('banner');
expect(res.data.imp[0]).to.not.have.property('native');
expect(res.data.imp[0]).to.not.have.property('native');
});
});

describe('interpretResponse', function () {
if (FEATURES.NATIVE) {
it('should interpret native response correctly', function () {
const nativeBidRequest = {
bidder: 'taboola',
params: {
publisherId: 'publisherId',
tagId: 'native-placement'
},
...nativeBidRequestParams,
nativeOrtbRequest: {
ver: '1.2',
assets: [
{id: 1, required: 1, title: {len: 150}},
{id: 2, required: 1, img: {type: 3, w: 300, h: 250}}
]
},
bidId: utils.generateUUID(),
auctionId: utils.generateUUID(),
};

const request = spec.buildRequests([nativeBidRequest], commonBidderRequest);

const nativeAdm = {
ver: '1.2',
assets: [
{id: 1, title: {text: 'Native Ad Title'}},
{id: 2, img: {url: 'https://example.com/image.jpg', w: 300, h: 250}}
],
link: {
url: 'https://example.com/click'
}
};

const serverResponse = {
body: {
id: 'response-id',
seatbid: [{
bid: [{
id: 'bid-id',
impid: request.data.imp[0].id,
price: 1.5,
adm: JSON.stringify(nativeAdm),
adomain: ['example.com'],
crid: 'creative-id',
exp: 300,
nurl: 'https://example.com/win'
}],
seat: 'taboola'
}],
cur: 'USD'
}
};

const res = spec.interpretResponse(serverResponse, request);

expect(res).to.be.an('array').with.lengthOf(1);
expect(res[0].mediaType).to.equal('native');
expect(res[0].native).to.exist;
expect(res[0].native.ortb).to.deep.equal(nativeAdm);
expect(res[0]).to.not.have.property('ad');
});
}
});
});
})
Loading