-
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.
- Loading branch information
Showing
3 changed files
with
850 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,256 @@ | ||
// jshint esversion: 6, es3: false, node: true | ||
'use strict'; | ||
|
||
import { | ||
registerBidder | ||
} from '../src/adapters/bidderFactory.js'; | ||
import { NATIVE, BANNER } from '../src/mediaTypes.js'; | ||
import * as utils from '../src/utils.js'; | ||
import { ajax } from '../src/ajax.js'; | ||
import { config } from '../src/config.js'; | ||
|
||
const BIDDER_CODE = 'zemanta'; | ||
const CURRENCY = 'USD'; | ||
const NATIVE_ASSET_IDS = { 0: 'title', 2: 'icon', 3: 'image', 5: 'sponsoredBy', 4: 'body', 1: 'cta' }; | ||
const NATIVE_PARAMS = { | ||
title: { id: 0, name: 'title' }, | ||
icon: { id: 2, type: 1, name: 'img' }, | ||
image: { id: 3, type: 3, name: 'img' }, | ||
sponsoredBy: { id: 5, name: 'data', type: 1 }, | ||
body: { id: 4, name: 'data', type: 2 }, | ||
cta: { id: 1, type: 12, name: 'data' } | ||
}; | ||
|
||
export const spec = { | ||
code: BIDDER_CODE, | ||
supportedMediaTypes: [ NATIVE, BANNER ], | ||
isBidRequestValid: (bid) => { | ||
return ( | ||
!!config.getConfig('zemanta.bidderUrl') && | ||
!!utils.deepAccess(bid, 'params.publisher.id') && | ||
!!(bid.nativeParams || bid.sizes) | ||
); | ||
}, | ||
buildRequests: (validBidRequests, bidderRequest) => { | ||
const page = bidderRequest.refererInfo.referer; | ||
const ua = navigator.userAgent; | ||
const test = setOnAny(validBidRequests, 'params.test'); | ||
const publisher = setOnAny(validBidRequests, 'params.publisher'); | ||
const cur = CURRENCY; | ||
const endpointUrl = config.getConfig('zemanta.bidderUrl'); | ||
const timeout = bidderRequest.timeout; | ||
|
||
const imps = validBidRequests.map((bid, id) => { | ||
bid.netRevenue = 'net'; | ||
const imp = { | ||
id: id + 1 + '' | ||
} | ||
|
||
if (bid.params.tagid) { | ||
imp.tagid = bid.params.tagid | ||
} | ||
|
||
if (bid.nativeParams) { | ||
imp.native = { | ||
request: JSON.stringify({ | ||
assets: getNativeAssets(bid) | ||
}) | ||
} | ||
} else { | ||
imp.banner = { | ||
format: transformSizes(bid.sizes) | ||
} | ||
} | ||
|
||
return imp; | ||
}); | ||
|
||
const request = { | ||
id: bidderRequest.auctionId, | ||
site: { page, publisher }, | ||
device: { ua }, | ||
source: { fd: 1 }, | ||
cur: [cur], | ||
tmax: timeout, | ||
imp: imps | ||
}; | ||
|
||
if (test) { | ||
request.is_debug = !!test; | ||
request.test = 1; | ||
} | ||
|
||
if (utils.deepAccess(bidderRequest, 'gdprConsent.gdprApplies')) { | ||
utils.deepSetValue(request, 'user.ext.consent', bidderRequest.gdprConsent.consentString) | ||
utils.deepSetValue(request, 'regs.ext.gdpr', bidderRequest.gdprConsent.gdprApplies & 1) | ||
} | ||
if (bidderRequest.uspConsent) { | ||
utils.deepSetValue(request, 'regs.ext.us_privacy', bidderRequest.uspConsent) | ||
} | ||
if (config.getConfig('coppa') === true) { | ||
utils.deepSetValue(request, 'regs.coppa', config.getConfig('coppa') & 1) | ||
} | ||
|
||
return { | ||
method: 'POST', | ||
url: endpointUrl, | ||
data: JSON.stringify(request), | ||
bids: validBidRequests | ||
}; | ||
}, | ||
interpretResponse: (serverResponse, { bids }) => { | ||
if (!serverResponse.body) { | ||
return []; | ||
} | ||
const { seatbid, cur } = serverResponse.body; | ||
|
||
const bidResponses = flatten(seatbid.map(seat => seat.bid)).reduce((result, bid) => { | ||
result[bid.impid - 1] = bid; | ||
return result; | ||
}, []); | ||
|
||
return bids.map((bid, id) => { | ||
const bidResponse = bidResponses[id]; | ||
if (bidResponse) { | ||
const type = bid.nativeParams ? NATIVE : BANNER; | ||
const bidObject = { | ||
requestId: bid.bidId, | ||
cpm: bidResponse.price, | ||
creativeId: bidResponse.crid, | ||
ttl: 360, | ||
netRevenue: bid.netRevenue === 'net', | ||
currency: cur, | ||
mediaType: type, | ||
nurl: bidResponse.nurl, | ||
}; | ||
if (type === NATIVE) { | ||
bidObject.native = parseNative(bidResponse); | ||
} else { | ||
bidObject.ad = bidResponse.adm; | ||
bidObject.width = bidResponse.w; | ||
bidObject.height = bidResponse.h; | ||
} | ||
return bidObject; | ||
} | ||
}).filter(Boolean); | ||
}, | ||
getUserSyncs: (syncOptions) => { | ||
const syncs = []; | ||
const syncUrl = config.getConfig('zemanta.usersyncUrl'); | ||
if (syncOptions.pixelEnabled && syncUrl) { | ||
syncs.push({ | ||
type: 'image', | ||
url: syncUrl | ||
}); | ||
} | ||
return syncs; | ||
}, | ||
onBidWon: (bid) => { | ||
ajax(utils.replaceAuctionPrice(bid.nurl, bid.originalCpm)) | ||
} | ||
}; | ||
|
||
registerBidder(spec); | ||
|
||
function parseNative(bid) { | ||
const { assets, link, eventtrackers } = JSON.parse(bid.adm); | ||
const result = { | ||
clickUrl: link.url, | ||
clickTrackers: link.clicktrackers || undefined | ||
}; | ||
assets.forEach(asset => { | ||
const kind = NATIVE_ASSET_IDS[asset.id]; | ||
const content = kind && asset[NATIVE_PARAMS[kind].name]; | ||
if (content) { | ||
result[kind] = content.text || content.value || { url: content.url, width: content.w, height: content.h }; | ||
} | ||
}); | ||
if (eventtrackers) { | ||
result.impressionTrackers = []; | ||
eventtrackers.forEach(tracker => { | ||
if (tracker.event !== 1) return; | ||
switch (tracker.method) { | ||
case 1: // img | ||
result.impressionTrackers.push(tracker.url); | ||
break; | ||
case 2: // js | ||
result.javascriptTrackers = `<script src=\"${tracker.url}\"></script>`; | ||
break; | ||
} | ||
}); | ||
} | ||
return result; | ||
} | ||
|
||
function setOnAny(collection, key) { | ||
for (let i = 0, result; i < collection.length; i++) { | ||
result = utils.deepAccess(collection[i], key); | ||
if (result) { | ||
return result; | ||
} | ||
} | ||
} | ||
|
||
function flatten(arr) { | ||
return [].concat(...arr); | ||
} | ||
|
||
function getNativeAssets(bid) { | ||
return utils._map(bid.nativeParams, (bidParams, key) => { | ||
const props = NATIVE_PARAMS[key]; | ||
const asset = { | ||
required: bidParams.required & 1, | ||
}; | ||
if (props) { | ||
asset.id = props.id; | ||
let wmin, hmin, w, h; | ||
let aRatios = bidParams.aspect_ratios; | ||
|
||
if (aRatios && aRatios[0]) { | ||
aRatios = aRatios[0]; | ||
wmin = aRatios.min_width || 0; | ||
hmin = aRatios.ratio_height * wmin / aRatios.ratio_width | 0; | ||
} | ||
|
||
if (bidParams.sizes) { | ||
const sizes = flatten(bidParams.sizes); | ||
w = sizes[0]; | ||
h = sizes[1]; | ||
} | ||
|
||
asset[props.name] = { | ||
len: bidParams.len, | ||
type: props.type, | ||
wmin, | ||
hmin, | ||
w, | ||
h | ||
}; | ||
|
||
return asset; | ||
} | ||
}).filter(Boolean); | ||
} | ||
|
||
/* Turn bid request sizes into ut-compatible format */ | ||
function transformSizes(requestSizes) { | ||
if (!utils.isArray(requestSizes)) { | ||
return []; | ||
} | ||
|
||
if (requestSizes.length === 2 && !utils.isArray(requestSizes[0])) { | ||
return [{ | ||
w: parseInt(requestSizes[0], 10), | ||
h: parseInt(requestSizes[1], 10) | ||
}]; | ||
} else if (utils.isArray(requestSizes[0])) { | ||
return requestSizes.map(item => | ||
({ | ||
w: parseInt(item[0], 10), | ||
h: parseInt(item[1], 10) | ||
}) | ||
); | ||
} | ||
|
||
return []; | ||
} |
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,107 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: Zemanta Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: prog-ops-team@outbrain.com | ||
``` | ||
|
||
# Description | ||
|
||
Module that connects to zemanta bidder to fetch bids. | ||
Both native and display formats are supported but not at the same time. Using OpenRTB standard. | ||
|
||
# Configuration | ||
|
||
## Bidder and usersync URLs | ||
|
||
The Zemanta adapter does not work without setting the correct bidder and usersync URLs. | ||
You will receive the URLs when contacting us. | ||
|
||
``` | ||
pbjs.setConfig({ | ||
zemanta: { | ||
bidderUrl: 'https://bidder-url.com', | ||
usersyncUrl: 'https://usersync-url.com' | ||
} | ||
}); | ||
``` | ||
|
||
|
||
# Test Native Parameters | ||
``` | ||
var adUnits = [ | ||
code: '/19968336/prebid_native_example_1', | ||
mediaTypes: { | ||
native: { | ||
image: { | ||
required: false, | ||
sizes: [100, 50] | ||
}, | ||
title: { | ||
required: false, | ||
len: 140 | ||
}, | ||
sponsoredBy: { | ||
required: false | ||
}, | ||
clickUrl: { | ||
required: false | ||
}, | ||
body: { | ||
required: false | ||
}, | ||
icon: { | ||
required: false, | ||
sizes: [50, 50] | ||
} | ||
} | ||
}, | ||
bids: [{ | ||
bidder: 'zemanta', | ||
params: { | ||
publisher: { | ||
id: '2706', // required | ||
name: 'Publishers Name', | ||
domain: 'publisher.com' | ||
}, | ||
tagid: 'tag-id' | ||
} | ||
}] | ||
]; | ||
pbjs.setConfig({ | ||
zemanta: { | ||
bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' | ||
} | ||
}); | ||
``` | ||
|
||
# Test Display Parameters | ||
``` | ||
var adUnits = [ | ||
code: '/19968336/prebid_display_example_1', | ||
mediaTypes: { | ||
banner: { | ||
sizes: [[300, 250]] | ||
} | ||
}, | ||
bids: [{ | ||
bidder: 'zemanta', | ||
params: { | ||
publisher: { | ||
id: '2706', // required | ||
name: 'Publishers Name', | ||
domain: 'publisher.com' | ||
}, | ||
tagid: 'tag-id' | ||
}, | ||
}] | ||
]; | ||
pbjs.setConfig({ | ||
zemanta: { | ||
bidderUrl: 'https://prebidtest.zemanta.com/api/bidder/prebidtest/bid/' | ||
} | ||
}); | ||
``` |
Oops, something went wrong.