forked from prebid/Prebid.js
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* implemented AerServ adapter * added unit tests, slight refactor/formatting of adapter code, added to adapters json * implemented AerServ adapter * added unit tests, slight refactor/formatting of adapter code, added to adapters json * feature necromancy! post-merge fixes to account for new structure * last minute addition of some video parameter logic + some name refactoring
- Loading branch information
1 parent
e0d8f96
commit 732b5a6
Showing
2 changed files
with
330 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,117 @@ | ||
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'; | ||
import adaptermanager from 'src/adaptermanager'; | ||
|
||
const BIDDER_CODE = 'aerserv'; | ||
|
||
const AerServAdapter = function AerServAdapter() { | ||
const ENVIRONMENTS = { | ||
local: '127.0.0.1:8080', | ||
dev: 'dev-ads.aerserv.com', | ||
stage: 'staging-ads.aerserv.com', | ||
prod: 'ads.aerserv.com' | ||
}; | ||
|
||
const BANNER_PATH = '/as/json/pbjs/v1?'; | ||
const VIDEO_PATH = '/as/json/pbjsvast/v1?'; | ||
const REQUIRED_PARAMS = ['plc']; | ||
|
||
function _isResponseValid(bidRequest, response) { | ||
return ((bidRequest.mediaType === 'video' && response.vastUrl) || (bidRequest.mediaType !== 'video' && response.adm)) && | ||
response.cpm && response.cpm > 0; | ||
} | ||
|
||
function _createBid(bidRequest, response) { | ||
if (_isResponseValid(bidRequest, response)) { | ||
let bid = bidfactory.createBid(1, bidRequest); | ||
bid.bidderCode = BIDDER_CODE; | ||
bid.cpm = response.cpm; | ||
bid.width = response.w; | ||
bid.height = response.h; | ||
if (bidRequest.mediaType === 'video') { | ||
bid.vastUrl = response.vastUrl; | ||
bid.descriptionUrl = response.vastUrl; | ||
bid.mediaType = 'video'; | ||
} else { | ||
bid.ad = response.adm; | ||
} | ||
bidmanager.addBidResponse(bidRequest.placementCode, bid); | ||
} else { | ||
bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest)); | ||
} | ||
} | ||
|
||
function _getFirstSize(sizes) { | ||
let sizeObj = {}; | ||
if (utils.isArray(sizes) && sizes.length > 0 && utils.isArray(sizes[0]) && sizes[0].length === 2) { | ||
sizeObj['vpw'] = sizes[0][0]; | ||
sizeObj['vph'] = sizes[0][1]; | ||
} | ||
return sizeObj; | ||
} | ||
|
||
function _buildQueryParameters(bid, requestParams) { | ||
Object.keys(bid.params).filter(param => param !== 'video') | ||
.forEach(param => requestParams[param] = bid.params[param]); | ||
|
||
if (bid.mediaType === 'video') { | ||
let videoDimensions = _getFirstSize(bid.sizes); | ||
Object.keys(videoDimensions).forEach(param => requestParams[param] = videoDimensions[param]); | ||
Object.keys(bid.params.video || {}).forEach(param => requestParams[param] = bid.params.video[param]); | ||
} | ||
|
||
return utils.parseQueryStringParameters(requestParams); | ||
} | ||
|
||
function _handleResponse(bidRequest) { | ||
return response => { | ||
if (!response && response.length <= 0) { | ||
bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest)); | ||
utils.logError('Empty response'); | ||
return; | ||
} | ||
|
||
try { | ||
response = JSON.parse(response); | ||
} catch (e) { | ||
bidmanager.addBidResponse(bidRequest.placementCode, bidfactory.createBid(STATUS.NO_BID, bidRequest)); | ||
utils.logError('Invalid JSON in response'); | ||
return; | ||
} | ||
|
||
_createBid(bidRequest, response); | ||
}; | ||
} | ||
|
||
function _callBids(bidRequests) { | ||
let currentUrl = (window.parent !== window) ? document.referrer : window.location.href; | ||
currentUrl = currentUrl && encodeURIComponent(currentUrl); | ||
|
||
let bids = bidRequests.bids || []; | ||
bids.forEach(bid => { | ||
if (utils.hasValidBidRequest(bid.params, REQUIRED_PARAMS, BIDDER_CODE)) { | ||
let env = ENVIRONMENTS[bid.params['env']] || ENVIRONMENTS['prod']; | ||
let requestPath = bid.mediaType === 'video' ? VIDEO_PATH : BANNER_PATH; | ||
let pageParameters = {url: currentUrl}; | ||
let parameterStr = _buildQueryParameters(bid, pageParameters); | ||
|
||
let url = `//${env}${requestPath}${parameterStr}`; | ||
utils.logMessage('sending request to: ' + url); | ||
ajax(url, _handleResponse(bid), null, {withCredentials: true}); | ||
} else { | ||
bidmanager.addBidResponse(bid.placementCode, bidfactory.createBid(STATUS.NO_BID, bid)); | ||
} | ||
}); | ||
} | ||
|
||
return { | ||
callBids: _callBids | ||
} | ||
}; | ||
|
||
adaptermanager.registerBidAdapter(new AerServAdapter(), BIDDER_CODE, {supportedMediaTypes: ['video']}); | ||
|
||
module.exports = AerServAdapter; |
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,213 @@ | ||
import {expect} from 'chai'; | ||
import AerServAdapter from 'modules/aerservBidAdapter'; | ||
import bidmanager from 'src/bidmanager'; | ||
|
||
const BASE_REQUEST = JSON.stringify({ | ||
bidderCode: 'aerserv', | ||
requestId: 'a595eff7-d5a3-40f8-971c-5b4ef244ec53', | ||
bidderRequestId: '1f8c8c03de01f9', | ||
bids: [ | ||
{ | ||
bidder: 'aerserv', | ||
params: { | ||
plc: '480', | ||
}, | ||
placementCode: 'adunit-1', | ||
transactionId: 'a0e033af-f50c-4a7e-aeed-c01c5f709848', | ||
sizes: [[300, 250], [300, 600]], | ||
bidId: '2f4a69463b3bc9', | ||
bidderRequestId: '1f8c8c03de01f9', | ||
requestId: 'a595eff7-d5a3-40f8-971c-5b4ef244ec53' | ||
} | ||
] | ||
}); | ||
|
||
describe('AerServ Adapter', () => { | ||
let adapter; | ||
let bidmanagerStub; | ||
|
||
beforeEach(() => { | ||
adapter = new AerServAdapter(); | ||
bidmanagerStub = sinon.stub(bidmanager, 'addBidResponse'); | ||
}); | ||
|
||
afterEach(() => { | ||
bidmanager.addBidResponse.restore(); | ||
}); | ||
|
||
describe('callBids()', () => { | ||
let xhr; | ||
let requests; | ||
|
||
beforeEach(() => { | ||
xhr = sinon.useFakeXMLHttpRequest(); | ||
requests = []; | ||
xhr.onCreate = request => requests.push(request); | ||
}); | ||
|
||
afterEach(() => { | ||
xhr.restore(); | ||
}); | ||
|
||
it('exists and is a function', () => { | ||
expect(adapter.callBids).to.exist.and.to.be.a('function'); | ||
}); | ||
|
||
it('should not add bid responses with no bids to call', () => { | ||
adapter.callBids({}); | ||
|
||
sinon.assert.notCalled(bidmanager.addBidResponse); | ||
}); | ||
|
||
it('requires plc parameter to make request', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0].params = {}; | ||
adapter.callBids(bidRequest); | ||
expect(requests).to.be.empty; | ||
}); | ||
|
||
it('sends requests to normal endpoint for non-video requests', () => { | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
expect(requests.length).to.equal(1); | ||
expect(requests[0].url).to.include('/as/json/pbjs/v1'); | ||
}); | ||
|
||
it('sends requests to video endpoint for video requests', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0]['mediaType'] = 'video'; | ||
bidRequest.bids[0]['video'] = {}; | ||
adapter.callBids(bidRequest); | ||
expect(requests[0].url).to.include('/as/json/pbjsvast/v1'); | ||
}); | ||
|
||
it('properly adds video parameters to the request', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0]['mediaType'] = 'video'; | ||
bidRequest.bids[0].params['video'] = { videoParam: 'videoValue' }; | ||
adapter.callBids(bidRequest); | ||
expect(requests[0].url).to.include('videoParam=videoValue'); | ||
}); | ||
|
||
it('parses the first size for video requests', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0]['mediaType'] = 'video'; | ||
adapter.callBids(bidRequest); | ||
expect(requests[0].url).to.include('vpw=300'); | ||
expect(requests[0].url).to.include('vph=250'); | ||
}); | ||
|
||
it('sends requests to production by default', () => { | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
expect(requests[0].url).to.include('//ads.aerserv.com'); | ||
}); | ||
|
||
it('sends requests to the specified endpoint when \'env\' is provided', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0].params['env'] = 'dev'; | ||
adapter.callBids(bidRequest); | ||
expect(requests[0].url).to.include('//dev-ads.aerserv.com'); | ||
}); | ||
}); | ||
|
||
describe('response handling', () => { | ||
let server; | ||
|
||
beforeEach(() => { | ||
server = sinon.fakeServer.create(); | ||
}); | ||
|
||
afterEach(() => { | ||
server.restore(); | ||
}); | ||
|
||
it('responds with an empty bid without required parameters', () => { | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0].params = {}; | ||
adapter.callBids(bidRequest); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
it('responds with an empty bid on empty response', () => { | ||
server.respondWith(''); | ||
|
||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
it('responds with an empty bid on un-parseable JSON response', () => { | ||
server.respondWith('{\"bad\":\"json}'); | ||
|
||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
it('responds with a valid bid returned ad', () => { | ||
server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50, adm: 'sweet ad markup'})); | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(1); | ||
}); | ||
|
||
it('responds with a valid bid from returned ad', () => { | ||
server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50, vastUrl: 'sweet URL where VAST is at'})); | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0]['mediaType'] = 'video'; | ||
bidRequest.bids[0]['video'] = {}; | ||
adapter.callBids(bidRequest); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(1); | ||
}); | ||
|
||
it('responds with empty bid if response has no ad', () => { | ||
server.respondWith(JSON.stringify({error: 'no ads'})); | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
// things that should never occur | ||
it('responds with empty bid if response has 0 or below cpm', () => { | ||
server.respondWith(JSON.stringify({cpm: 0, w: 320, h: 50, adm: 'sweet ad markup'})); | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
it('responds with empty bid if response has no markup', () => { | ||
server.respondWith(JSON.stringify({cpm: 5.0, w: 320, h: 50})); | ||
adapter.callBids(JSON.parse(BASE_REQUEST)); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
|
||
it('responds with an empty bid if response has no video markup', () => { | ||
server.respondWith(JSON.stringify({cpm: 5, w: 320, h: 50})); | ||
let bidRequest = JSON.parse(BASE_REQUEST); | ||
bidRequest.bids[0]['mediaType'] = 'video'; | ||
bidRequest.bids[0]['video'] = {}; | ||
adapter.callBids(bidRequest); | ||
server.respond(); | ||
let bid = bidmanagerStub.getCall(0).args[1]; | ||
sinon.assert.calledOnce(bidmanager.addBidResponse); | ||
expect(bid.getStatusCode()).to.equal(2); | ||
}); | ||
}); | ||
}); |