-
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.
* added adxcg adapter and associated tests * Removed unnecessary string concatenations and similar utilitiy functions and replaced them with those from prebid utils.js and url.js. Enabled credentials in the ajax call. Replaced string concatenations with template literals where possible. Escaped native click url in order to correctly handle redirects.
- Loading branch information
1 parent
dc4736a
commit 34fbe9e
Showing
2 changed files
with
351 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,139 @@ | ||
import bidfactory from 'src/bidfactory'; | ||
import bidmanager from 'src/bidmanager'; | ||
import * as utils from 'src/utils'; | ||
import { STATUS } from 'src/constants'; | ||
import adaptermanager from 'src/adaptermanager'; | ||
import { ajax } from 'src/ajax'; | ||
import * as url from 'src/url'; | ||
|
||
/** | ||
* Adapter for requesting bids from Adxcg | ||
* updated from latest prebid repo on 2017.08.30 | ||
*/ | ||
function AdxcgAdapter() { | ||
let bidRequests = {}; | ||
|
||
function _callBids(params) { | ||
if (params.bids && params.bids.length > 0) { | ||
let adZoneIds = []; | ||
let prebidBidIds = []; | ||
let sizes = []; | ||
|
||
params.bids.forEach(bid => { | ||
bidRequests[bid.bidId] = bid; | ||
adZoneIds.push(utils.getBidIdParameter('adzoneid', bid.params)); | ||
prebidBidIds.push(bid.bidId); | ||
sizes.push(utils.parseSizesInput(bid.sizes).join('|')); | ||
}); | ||
|
||
let location = utils.getTopWindowLocation(); | ||
let secure = location.protocol == 'https:'; | ||
|
||
let requestUrl = url.parse(location.href); | ||
requestUrl.search = null; | ||
requestUrl.hash = null; | ||
|
||
let adxcgRequestUrl = url.format({ | ||
protocol: secure ? 'https' : 'http', | ||
hostname: secure ? 'ad-emea-secure.adxcg.net' : 'ad-emea.adxcg.net', | ||
pathname: '/get/adi', | ||
search: { | ||
renderformat: 'javascript', | ||
ver: 'r20141124', | ||
adzoneid: adZoneIds.join(','), | ||
format: sizes.join(','), | ||
prebidBidIds: prebidBidIds.join(','), | ||
url: escape(url.format(requestUrl)), | ||
secure: secure ? '1' : '0' | ||
} | ||
}); | ||
|
||
utils.logMessage(`submitting request: ${adxcgRequestUrl}`); | ||
ajax(adxcgRequestUrl, handleResponse, null, { | ||
withCredentials: true | ||
}); | ||
} | ||
} | ||
|
||
function handleResponse(response) { | ||
let adxcgBidReponseList; | ||
|
||
try { | ||
adxcgBidReponseList = JSON.parse(response); | ||
utils.logMessage(`adxcgBidReponseList: ${JSON.stringify(adxcgBidReponseList)}`); | ||
} catch (error) { | ||
adxcgBidReponseList = []; | ||
utils.logError(error); | ||
} | ||
|
||
adxcgBidReponseList.forEach(adxcgBidReponse => { | ||
let bidRequest = bidRequests[adxcgBidReponse.bidId]; | ||
delete bidRequests[adxcgBidReponse.bidId]; | ||
|
||
let bid = bidfactory.createBid(STATUS.GOOD, bidRequest); | ||
|
||
bid.creative_id = adxcgBidReponse.creativeId; | ||
bid.code = 'adxcg'; | ||
bid.bidderCode = 'adxcg'; | ||
bid.cpm = adxcgBidReponse.cpm; | ||
|
||
if (adxcgBidReponse.ad) { | ||
bid.ad = adxcgBidReponse.ad; | ||
} else if (adxcgBidReponse.vastUrl) { | ||
bid.vastUrl = adxcgBidReponse.vastUrl; | ||
bid.descriptionUrl = adxcgBidReponse.vastUrl; | ||
bid.mediaType = 'video'; | ||
} else if (adxcgBidReponse.nativeResponse) { | ||
bid.mediaType = 'native'; | ||
|
||
let nativeResponse = adxcgBidReponse.nativeResponse; | ||
|
||
bid.native = { | ||
clickUrl: escape(nativeResponse.link.url), | ||
impressionTrackers: nativeResponse.imptrackers | ||
}; | ||
|
||
nativeResponse.assets.forEach(asset => { | ||
if (asset.title && asset.title.text) { | ||
bid.native.title = asset.title.text; | ||
} | ||
|
||
if (asset.img && asset.img.url) { | ||
bid.native.image = asset.img.url; | ||
} | ||
|
||
if (asset.data && asset.data.label == 'DESC' && asset.data.value) { | ||
bid.native.body = asset.data.value; | ||
} | ||
|
||
if (asset.data && asset.data.label == 'SPONSORED' && asset.data.value) { | ||
bid.native.sponsoredBy = asset.data.value; | ||
} | ||
}); | ||
} | ||
|
||
bid.width = adxcgBidReponse.width; | ||
bid.height = adxcgBidReponse.height; | ||
|
||
utils.logMessage(`submitting bid[${bidRequest.placementCode}]: ${JSON.stringify(bid)}`); | ||
bidmanager.addBidResponse(bidRequest.placementCode, bid); | ||
}); | ||
|
||
Object.keys(bidRequests) | ||
.map(bidId => bidRequests[bidId].placementCode) | ||
.forEach(placementCode => { | ||
utils.logMessage(`creating no_bid bid for: ${placementCode}`); | ||
bidmanager.addBidResponse(placementCode, bidfactory.createBid(STATUS.NO_BID)); | ||
}); | ||
}; | ||
|
||
return { | ||
callBids: _callBids | ||
}; | ||
}; | ||
|
||
adaptermanager.registerBidAdapter(new AdxcgAdapter(), 'adxcg', { | ||
supportedMediaTypes: ['video', 'native'] | ||
}); | ||
|
||
module.exports = AdxcgAdapter; |
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,212 @@ | ||
import { expect } from 'chai'; | ||
import Adapter from 'modules/adxcgBidAdapter'; | ||
import bidmanager from 'src/bidmanager'; | ||
import * as url from 'src/url'; | ||
|
||
const REQUEST = { | ||
'bidderCode': 'adxcg', | ||
'bids': [ | ||
{ | ||
'bidder': 'adxcg', | ||
'params': { | ||
'adzoneid': '1', | ||
}, | ||
'sizes': [ | ||
[300, 250], | ||
[640, 360], | ||
[1, 1] | ||
], | ||
'bidId': '84ab500420319d', | ||
'bidderRequestId': '7101db09af0db2' | ||
} | ||
] | ||
}; | ||
|
||
const RESPONSE = [{ | ||
'bidId': '84ab500420319d', | ||
'width': 300, | ||
'height': 250, | ||
'creativeId': '42', | ||
'cpm': 0.45, | ||
'ad': '<!-- adContent -->' | ||
}] | ||
|
||
const VIDEO_RESPONSE = [{ | ||
'bidId': '84ab500420319d', | ||
'width': 640, | ||
'height': 360, | ||
'creativeId': '42', | ||
'cpm': 0.45, | ||
'vastUrl': 'vastContentUrl' | ||
}] | ||
|
||
const NATIVE_RESPONSE = [{ | ||
'bidId': '84ab500420319d', | ||
'width': 0, | ||
'height': 0, | ||
'creativeId': '42', | ||
'cpm': 0.45, | ||
'nativeResponse': { | ||
'assets': [{ | ||
'id': 1, | ||
'required': 0, | ||
'title': { | ||
'text': 'titleContent' | ||
} | ||
}, { | ||
'id': 2, | ||
'required': 0, | ||
'img': { | ||
'url': 'imageContent', | ||
'w': 600, | ||
'h': 600 | ||
} | ||
}, { | ||
'id': 3, | ||
'required': 0, | ||
'data': { | ||
'label': 'DESC', | ||
'value': 'descriptionContent' | ||
} | ||
}, { | ||
'id': 0, | ||
'required': 0, | ||
'data': { | ||
'label': 'SPONSORED', | ||
'value': 'sponsoredByContent' | ||
} | ||
}], | ||
'link': { | ||
'url': 'linkContent' | ||
}, | ||
'imptrackers': ['impressionTracker1', 'impressionTracker2'] | ||
} | ||
}] | ||
|
||
describe('AdxcgAdapter', () => { | ||
let adapter; | ||
|
||
beforeEach(() => adapter = new Adapter()); | ||
|
||
describe('request function', () => { | ||
let xhr; | ||
let requests; | ||
|
||
beforeEach(() => { | ||
xhr = sinon.useFakeXMLHttpRequest(); | ||
requests = []; | ||
xhr.onCreate = request => requests.push(request); | ||
}); | ||
|
||
afterEach(() => xhr.restore()); | ||
|
||
it('creates a valid adxcg request url', () => { | ||
adapter.callBids(REQUEST); | ||
|
||
let parsedRequestUrl = url.parse(requests[0].url); | ||
|
||
expect(parsedRequestUrl.hostname).to.equal('ad-emea.adxcg.net'); | ||
expect(parsedRequestUrl.pathname).to.equal('/get/adi'); | ||
|
||
let query = parsedRequestUrl.search; | ||
expect(query.renderformat).to.equal('javascript'); | ||
expect(query.ver).to.equal('r20141124'); | ||
expect(query.adzoneid).to.equal('1'); | ||
expect(query.format).to.equal('300x250|640x360|1x1'); | ||
expect(query.jsonp).to.be.empty; | ||
expect(query.prebidBidIds).to.equal('84ab500420319d'); | ||
}); | ||
}); | ||
|
||
describe('response handler', () => { | ||
let server; | ||
|
||
beforeEach(() => { | ||
server = sinon.fakeServer.create(); | ||
sinon.stub(bidmanager, 'addBidResponse'); | ||
}); | ||
|
||
afterEach(() => { | ||
server.restore() | ||
bidmanager.addBidResponse.restore(); | ||
}); | ||
|
||
it('handles regular responses', () => { | ||
server.respondWith(JSON.stringify(RESPONSE)); | ||
|
||
adapter.callBids(REQUEST); | ||
server.respond(); | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
|
||
const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; | ||
expect(bidResponse.bidderCode).to.equal('adxcg'); | ||
expect(bidResponse.width).to.equal(300); | ||
expect(bidResponse.height).to.equal(250); | ||
expect(bidResponse.statusMessage).to.equal('Bid available'); | ||
expect(bidResponse.adId).to.equal('84ab500420319d'); | ||
expect(bidResponse.mediaType).to.equal('banner'); | ||
expect(bidResponse.creative_id).to.equal('42'); | ||
expect(bidResponse.code).to.equal('adxcg'); | ||
expect(bidResponse.cpm).to.equal(0.45); | ||
expect(bidResponse.ad).to.equal('<!-- adContent -->'); | ||
}); | ||
|
||
it('handles video responses', () => { | ||
server.respondWith(JSON.stringify(VIDEO_RESPONSE)); | ||
|
||
adapter.callBids(REQUEST); | ||
server.respond(); | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
|
||
const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; | ||
expect(bidResponse.bidderCode).to.equal('adxcg'); | ||
expect(bidResponse.width).to.equal(640); | ||
expect(bidResponse.height).to.equal(360); | ||
expect(bidResponse.statusMessage).to.equal('Bid available'); | ||
expect(bidResponse.adId).to.equal('84ab500420319d'); | ||
expect(bidResponse.mediaType).to.equal('video'); | ||
expect(bidResponse.creative_id).to.equal('42'); | ||
expect(bidResponse.code).to.equal('adxcg'); | ||
expect(bidResponse.cpm).to.equal(0.45); | ||
expect(bidResponse.vastUrl).to.equal('vastContentUrl'); | ||
expect(bidResponse.descriptionUrl).to.equal('vastContentUrl'); | ||
}); | ||
|
||
it('handles native responses', () => { | ||
server.respondWith(JSON.stringify(NATIVE_RESPONSE)); | ||
|
||
adapter.callBids(REQUEST); | ||
server.respond(); | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
|
||
const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; | ||
expect(bidResponse.bidderCode).to.equal('adxcg'); | ||
expect(bidResponse.width).to.equal(0); | ||
expect(bidResponse.height).to.equal(0); | ||
expect(bidResponse.statusMessage).to.equal('Bid available'); | ||
expect(bidResponse.adId).to.equal('84ab500420319d'); | ||
expect(bidResponse.mediaType).to.equal('native'); | ||
expect(bidResponse.creative_id).to.equal('42'); | ||
expect(bidResponse.code).to.equal('adxcg'); | ||
expect(bidResponse.cpm).to.equal(0.45); | ||
|
||
expect(bidResponse.native.clickUrl).to.equal('linkContent'); | ||
expect(bidResponse.native.impressionTrackers).to.deep.equal(['impressionTracker1', 'impressionTracker2']); | ||
expect(bidResponse.native.title).to.equal('titleContent'); | ||
expect(bidResponse.native.image).to.equal('imageContent'); | ||
expect(bidResponse.native.body).to.equal('descriptionContent'); | ||
expect(bidResponse.native.sponsoredBy).to.equal('sponsoredByContent'); | ||
}); | ||
|
||
it('handles nobid responses', () => { | ||
server.respondWith('[]'); | ||
|
||
adapter.callBids(REQUEST); | ||
server.respond(); | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
|
||
const bidResponse = bidmanager.addBidResponse.firstCall.args[1]; | ||
expect(bidResponse.statusMessage).to.equal('Bid returned empty or error response'); | ||
}); | ||
}); | ||
}); |