forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add UT endpoint adapter (prebid#507)
* Add UT endpoint adapter Make one request to /ut for all adslots on page * Build minimal request object * Correctly format size object * Implement inital response handler function * Create bid based on response status * Test response handling function * Extend base adapter to allow for alias bidding * Handle nobid response * Check for required parameters * Validate bid parameters * Store bid reference in request-response map * Configure XHR options * Add NO_BID to each placement for response error * Update adapter name * Update endpoint to /prebid and to use protocol-relative scheme
- Loading branch information
1 parent
575f17e
commit 7a011fa
Showing
5 changed files
with
346 additions
and
96 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 |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
"admedia", | ||
"aol", | ||
"appnexus", | ||
"appnexusAst", | ||
"fan", | ||
"indexExchange", | ||
"kruxlink", | ||
|
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
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 |
---|---|---|
@@ -1,104 +1,157 @@ | ||
import { BaseAdapter } from './baseAdapter'; | ||
const utils = require('../utils'); | ||
const adloader = require('../adloader.js'); | ||
const bidmanager = require('../bidmanager'); | ||
const bidfactory = require('../bidfactory'); | ||
const CONSTANTS = require('../constants.json'); | ||
|
||
const AST_URL = 'https://acdn.adnxs.com/ast/alpha/ast.js'; | ||
|
||
export class AppnexusAst extends BaseAdapter { | ||
constructor(code) { | ||
super(code); | ||
this._bidRequests = null; | ||
} | ||
|
||
callBids(params) { | ||
window.apntag = window.apntag || {}; | ||
window.apntag.anq = window.apntag.anq || []; | ||
this._bidRequests = params.bids; | ||
adloader.loadScript(AST_URL, () => { | ||
this._requestAds(this.code); | ||
}, true); | ||
} | ||
|
||
_requestAds(code) { | ||
if (utils.debugTurnedOn()) { | ||
window.apntag.debug = true; | ||
} | ||
|
||
window.apntag.clearRequest(); | ||
|
||
for (let bidRequest of this._bidRequests) { | ||
const astTag = this._buildTag(bidRequest); | ||
const requestTag = window.apntag.defineTag(astTag); | ||
const placementCode = bidRequest.placementCode; | ||
|
||
//successful bid | ||
/*jshint -W083 */ | ||
requestTag.on('adAvailable', function(ad) { | ||
const bid = bidfactory.createBid(CONSTANTS.STATUS.GOOD); | ||
bid.code = code; | ||
bid.bidderCode = code; | ||
bid.creative_id = ad.creativeId; | ||
bid.cpm = ad.cpm; | ||
bid.ad = ad.banner.content; | ||
try { | ||
const url = ad.banner.trackers[0].impression_urls[0]; | ||
const tracker = utils.createTrackPixelHtml(url); | ||
bid.ad += tracker; | ||
} catch (e) { | ||
utils.logError('Error appending tracking pixel', 'appnexusAst.js:_requestAds', e); | ||
} | ||
|
||
bid.width = ad.banner.width; | ||
bid.height = ad.banner.height; | ||
bidmanager.addBidResponse(placementCode, bid); | ||
import Adapter from 'src/adapters/adapter'; | ||
import bidfactory from 'src/bidfactory'; | ||
import bidmanager from 'src/bidmanager'; | ||
import * as utils from 'src/utils'; | ||
import { ajax } from 'src/ajax'; | ||
import { STATUS } from 'src/constants'; | ||
|
||
const ENDPOINT = '//ib.adnxs.com/ut/v2/prebid'; | ||
|
||
/** | ||
* Bidder adapter for /ut endpoint. Given the list of all ad unit tag IDs, | ||
* sends out a bid request. When a bid response is back, registers the bid | ||
* to Prebid.js. This adapter supports alias bidding. | ||
*/ | ||
function AppnexusAstAdapter() { | ||
|
||
let baseAdapter = Adapter.createNew('appnexusAst'); | ||
let adUnitCodes = {}; | ||
|
||
/* Prebid executes this function when the page asks to send out bid requests */ | ||
baseAdapter.callBids = function(bidRequest) { | ||
const bids = bidRequest.bids || []; | ||
const tags = bids | ||
.filter(bid => valid(bid)) | ||
.map(bid => { | ||
// map request id to bid object to retrieve adUnit code in callback | ||
adUnitCodes[bid.bidId] = bid; | ||
|
||
let tag = {}; | ||
tag.sizes = getSizes(bid.sizes); | ||
tag.primary_size = tag.sizes[0]; | ||
tag.uuid = bid.bidId; | ||
tag.id = Number.parseInt(bid.params.placementId); | ||
tag.allow_smaller_sizes = bid.params.allowSmallerSizes || false; | ||
tag.prebid = true; | ||
tag.disable_psa = true; | ||
|
||
return tag; | ||
}); | ||
|
||
//no bid | ||
requestTag.on('adNoBid', function() { | ||
const bid = bidfactory.createBid(CONSTANTS.STATUS.NO_BID); | ||
bid.code = code; | ||
bid.bidderCode = code; | ||
bidmanager.addBidResponse(placementCode, bid); | ||
if (!utils.isEmpty(tags)) { | ||
const payload = JSON.stringify({tags: [...tags]}); | ||
ajax(ENDPOINT, handleResponse, payload, { | ||
contentType: 'text/plain', | ||
preflight: false, | ||
}); | ||
} | ||
}; | ||
|
||
window.apntag.loadTags(); | ||
} | ||
/* Notify Prebid of bid responses so bids can get in the auction */ | ||
function handleResponse(response) { | ||
let parsed; | ||
|
||
_buildTag(bid) { | ||
let tag = {}; | ||
const uuid = utils.getUniqueIdentifierStr(); | ||
try { | ||
parsed = JSON.parse(response); | ||
} catch (error) { | ||
utils.logError(error); | ||
} | ||
|
||
//clone bid.params to tag | ||
const jsonBid = JSON.stringify(bid.params); | ||
tag = JSON.parse(jsonBid); | ||
if (!parsed || parsed.error) { | ||
utils.logError(`Bad response for ${baseAdapter.getBidderCode()} adapter`); | ||
|
||
//append member if available. Should not use multiple member IDs. | ||
utils._each(tag, function(value, key) { | ||
if (key === 'member') { | ||
window.apntag.setPageOpts({ | ||
member: value | ||
// signal this response is complete | ||
Object.keys(adUnitCodes) | ||
.map(bidId => adUnitCodes[bidId].placementCode) | ||
.forEach(placementCode => { | ||
bidmanager.addBidResponse(placementCode, createBid(STATUS.NO_BID)); | ||
}); | ||
return; | ||
} | ||
|
||
parsed.tags.forEach(tag => { | ||
const cpm = tag.ads && tag.ads[0].cpm; | ||
const type = tag.ads && tag.ads[0].ad_type; | ||
|
||
if (type !== 'banner') { | ||
utils.logError(`${type} ad type not supported`); | ||
} | ||
|
||
if (key === 'keywords') { | ||
window.apntag.setPageOpts({ | ||
keywords: value | ||
}); | ||
let bid; | ||
if (cpm !== 0 && type === 'banner') { | ||
bid = createBid(STATUS.GOOD, tag); | ||
} else { | ||
bid = createBid(STATUS.NO_BID); | ||
} | ||
|
||
if (!utils.isEmpty(bid)) { | ||
bidmanager.addBidResponse(adUnitCodes[tag.uuid].placementCode, bid); | ||
} | ||
}); | ||
} | ||
|
||
tag.targetId = uuid; | ||
tag.prebid = true; | ||
if (!tag.sizes) { | ||
tag.sizes = bid.sizes; | ||
/* Check that a bid has required paramters */ | ||
function valid(bid) { | ||
if (bid.params.placementId || bid.params.memberId && bid.params.invCode) { | ||
return bid; | ||
} else { | ||
utils.logError('bid requires placementId or (memberId and invCode) params'); | ||
} | ||
} | ||
|
||
tag.tagId = Number.parseInt(bid.params.placementId); | ||
tag.disablePsa = true; | ||
return tag; | ||
/* Turn bid request sizes into ut-compatible format */ | ||
function getSizes(requestSizes) { | ||
let sizes = []; | ||
let sizeObj = {}; | ||
|
||
if (utils.isArray(requestSizes) && requestSizes.length === 2 && | ||
!utils.isArray(requestSizes[0])) { | ||
sizeObj.width = parseInt(requestSizes[0], 10); | ||
sizeObj.height = parseInt(requestSizes[1], 10); | ||
sizes.push(sizeObj); | ||
} else if (typeof requestSizes === 'object') { | ||
for (let i = 0; i < requestSizes.length; i++) { | ||
let size = requestSizes[i]; | ||
sizeObj = {}; | ||
sizeObj.width = parseInt(size[0], 10); | ||
sizeObj.height = parseInt(size[1], 10); | ||
sizes.push(sizeObj); | ||
} | ||
} | ||
|
||
return sizes; | ||
} | ||
|
||
/* Create and return a bid object based on status and tag */ | ||
function createBid(status, tag) { | ||
let bid = bidfactory.createBid(status); | ||
bid.code = baseAdapter.getBidderCode(); | ||
bid.bidderCode = baseAdapter.getBidderCode(); | ||
|
||
if (status === STATUS.GOOD) { | ||
bid.cpm = tag.ads[0].cpm; | ||
bid.creative_id = tag.ads[0].creativeId; | ||
bid.width = tag.ads[0].rtb.banner.width; | ||
bid.height = tag.ads[0].rtb.banner.height; | ||
bid.ad = tag.ads[0].rtb.banner.content; | ||
try { | ||
const url = tag.ads[0].rtb.trackers[0].impression_urls[0]; | ||
const tracker = utils.createTrackPixelHtml(url); | ||
bid.ad += tracker; | ||
} catch (error) { | ||
utils.logError('Error appending tracking pixel', error); | ||
} | ||
} | ||
|
||
return bid; | ||
} | ||
|
||
return { | ||
createNew: exports.createNew, | ||
callBids: baseAdapter.callBids, | ||
setBidderCode: baseAdapter.setBidderCode, | ||
}; | ||
|
||
} | ||
|
||
exports.createNew = () => new AppnexusAstAdapter(); |
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
Oops, something went wrong.