-
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.
Initial implementation of synacormedia adapter. (#3425)
- Loading branch information
Showing
3 changed files
with
362 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,121 @@ | ||
'use strict'; | ||
|
||
import { getAdUnitSizes, logWarn } from 'src/utils'; | ||
import { registerBidder } from 'src/adapters/bidderFactory'; | ||
import { BANNER } from 'src/mediaTypes'; | ||
import { REPO_AND_VERSION } from 'src/constants'; | ||
|
||
const SYNACOR_URL = '//prebid.technoratimedia.com'; | ||
export const spec = { | ||
code: 'synacormedia', | ||
supportedMediaTypes: [ BANNER ], | ||
sizeMap: {}, | ||
|
||
isBidRequestValid: function(bid) { | ||
return !!(bid && bid.params && bid.params.placementId && bid.params.seatId); | ||
}, | ||
buildRequests: function(validBidReqs, bidderRequest) { | ||
if (!validBidReqs || !validBidReqs.length || !bidderRequest) { | ||
return; | ||
} | ||
let refererInfo = bidderRequest.refererInfo; | ||
let openRtbBidRequest = { | ||
id: bidderRequest.auctionId, | ||
site: { | ||
domain: location.hostname, | ||
page: refererInfo.referer, | ||
ref: document.referrer | ||
}, | ||
device: { | ||
ua: navigator.userAgent | ||
}, | ||
imp: [] | ||
}; | ||
let seatId = null; | ||
validBidReqs.forEach((bid, i) => { | ||
if (seatId && seatId !== bid.params.seatId) { | ||
logWarn(`Synacormedia: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`); | ||
return; | ||
} else { | ||
seatId = bid.params.seatId; | ||
} | ||
let placementId = bid.params.placementId; | ||
let size = getAdUnitSizes(bid)[0]; | ||
this.sizeMap[bid.bidId] = size; | ||
openRtbBidRequest.imp.push({ | ||
id: bid.bidId, | ||
tagid: placementId, | ||
banner: { | ||
w: size[0], | ||
h: size[1], | ||
pos: 0 | ||
} | ||
}); | ||
}); | ||
if (openRtbBidRequest.imp.length && seatId) { | ||
return { | ||
method: 'POST', | ||
url: `${SYNACOR_URL}/openrtb/bids/${seatId}?src=${REPO_AND_VERSION}`, | ||
data: openRtbBidRequest, | ||
options: { | ||
contentType: 'application/json', | ||
withCredentials: true | ||
} | ||
}; | ||
} | ||
}, | ||
interpretResponse: function(serverResponse) { | ||
if (!serverResponse.body || typeof serverResponse.body != 'object') { | ||
logWarn('Synacormedia: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body)); | ||
return; | ||
} | ||
const {id, seatbid: seatbids} = serverResponse.body; | ||
let bids = []; | ||
if (id && seatbids) { | ||
seatbids.forEach(seatbid => { | ||
seatbid.bid.forEach(bid => { | ||
let size = this.sizeMap[bid.impid] || [0, 0]; | ||
let price = parseFloat(bid.price); | ||
let creative = bid.adm.replace(/\${([^}]*)}/g, (match, key) => { | ||
switch (key) { | ||
case 'AUCTION_SEAT_ID': return seatbid.seat; | ||
case 'AUCTION_ID': return id; | ||
case 'AUCTION_BID_ID': return bid.id; | ||
case 'AUCTION_IMP_ID': return bid.impid; | ||
case 'AUCTION_PRICE': return price; | ||
case 'AUCTION_CURRENCY': return 'USD'; | ||
} | ||
return match; | ||
}); | ||
bids.push({ | ||
requestId: bid.impid, | ||
cpm: price, | ||
width: size[0], | ||
height: size[1], | ||
creativeId: seatbid.seat + '~' + bid.crid, | ||
currency: 'USD', | ||
netRevenue: true, | ||
mediaType: BANNER, | ||
ad: creative, | ||
ttl: 60 | ||
}); | ||
}); | ||
}); | ||
} | ||
return bids; | ||
}, | ||
getUserSyncs: function (syncOptions, serverResponses) { | ||
const syncs = []; | ||
if (syncOptions.iframeEnabled) { | ||
syncs.push({ | ||
type: 'iframe', | ||
url: `${SYNACOR_URL}/usersync/html?src=${REPO_AND_VERSION}` | ||
}); | ||
} else { | ||
logWarn('Synacormedia: Please enable iframe based user sync.'); | ||
} | ||
return syncs; | ||
} | ||
}; | ||
|
||
registerBidder(spec); |
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,43 @@ | ||
# Overview | ||
|
||
``` | ||
Module Name: Synacor Media Bidder Adapter | ||
Module Type: Bidder Adapter | ||
Maintainer: eng-demand@synacor.com | ||
``` | ||
|
||
# Description | ||
|
||
The Synacor Media adapter requires setup and approval from Synacor. | ||
Please reach out to your account manager for more information. | ||
|
||
# Test Parameters | ||
|
||
## Web | ||
``` | ||
var adUnits = [{ | ||
code: 'test-div', | ||
sizes: [ | ||
[300, 250] | ||
], | ||
bids: [{ | ||
bidder: "synacormedia", | ||
params: { | ||
seatId: "prebid", | ||
placementId: "81416" | ||
} | ||
}] | ||
},{ | ||
code: 'test-div2', | ||
sizes: [ | ||
[300, 250] | ||
], | ||
bids: [{ | ||
bidder: "synacormedia", | ||
params: { | ||
seatId: "prebid", | ||
placementId: "demo2" | ||
} | ||
}] | ||
}]; | ||
``` |
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,198 @@ | ||
import { assert, expect } from 'chai'; | ||
import { BANNER } from 'src/mediaTypes'; | ||
import { spec } from 'modules/synacormediaBidAdapter'; | ||
|
||
describe('synacormediaBidAdapter ', function () { | ||
describe('isBidRequestValid', function () { | ||
let bid; | ||
beforeEach(function () { | ||
bid = { | ||
params: { | ||
seatId: 'prebid', | ||
placementId: '1234' | ||
} | ||
}; | ||
}); | ||
|
||
it('should return true when params placementId and seatId are truthy', function () { | ||
assert(spec.isBidRequestValid(bid)); | ||
}); | ||
|
||
it('should return false when seatId param is missing', function () { | ||
delete bid.params.seatId; | ||
assert.isFalse(spec.isBidRequestValid(bid)); | ||
}); | ||
it('should return false when placementId param is missing', function () { | ||
delete bid.params.placementId; | ||
assert.isFalse(spec.isBidRequestValid(bid)); | ||
}); | ||
it('should return false when params is missing or null', function () { | ||
assert.isFalse(spec.isBidRequestValid({ params: null })); | ||
assert.isFalse(spec.isBidRequestValid({})); | ||
assert.isFalse(spec.isBidRequestValid(null)); | ||
}); | ||
}); | ||
describe('buildRequests', function () { | ||
let validBidRequest = { | ||
bidId: '9876abcd', | ||
sizes: [[300, 250]], | ||
params: { | ||
seatId: 'prebid', | ||
placementId: '1234' | ||
} | ||
}; | ||
|
||
let bidderRequest = { | ||
auctionId: 'xyz123', | ||
refererInfo: { | ||
referer: 'https://test.com/foo/bar' | ||
} | ||
}; | ||
|
||
let expectedDataImp = { | ||
banner: { | ||
h: 250, | ||
pos: 0, | ||
w: 300, | ||
}, | ||
id: '9876abcd', | ||
tagid: '1234' | ||
}; | ||
|
||
it('should return valid request when valid bids are used', function () { | ||
let req = spec.buildRequests([validBidRequest], bidderRequest); | ||
expect(req).be.an('object'); | ||
expect(req).to.have.property('method', 'POST'); | ||
expect(req).to.have.property('url'); | ||
expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?'); | ||
expect(req.data).to.exist.and.to.be.an('object'); | ||
expect(req.data.id).to.equal('xyz123'); | ||
expect(req.data.imp).to.eql([expectedDataImp]); | ||
}); | ||
|
||
it('should return multiple bids when multiple valid requests with the same seatId are used', function () { | ||
let secondBidRequest = { | ||
bidId: 'foobar', | ||
sizes: [[300, 600]], | ||
params: { | ||
seatId: validBidRequest.params.seatId, | ||
placementId: '5678' | ||
} | ||
}; | ||
let req = spec.buildRequests([validBidRequest, secondBidRequest], bidderRequest); | ||
expect(req).to.exist.and.be.an('object'); | ||
expect(req).to.have.property('method', 'POST'); | ||
expect(req).to.have.property('url'); | ||
expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/prebid?'); | ||
expect(req.data.id).to.equal('xyz123'); | ||
expect(req.data.imp).to.eql([expectedDataImp, { | ||
banner: { | ||
h: 600, | ||
pos: 0, | ||
w: 300, | ||
}, | ||
id: 'foobar', | ||
tagid: '5678' | ||
}]); | ||
}); | ||
|
||
it('should return only first bid when different seatIds are used', function () { | ||
let mismatchedSeatBidRequest = { | ||
bidId: 'foobar', | ||
sizes: [[300, 250]], | ||
params: { | ||
seatId: 'somethingelse', | ||
placementId: '5678' | ||
} | ||
}; | ||
let req = spec.buildRequests([mismatchedSeatBidRequest, validBidRequest], bidderRequest); | ||
expect(req).to.have.property('method', 'POST'); | ||
expect(req).to.have.property('url'); | ||
expect(req.url).to.contain('//prebid.technoratimedia.com/openrtb/bids/somethingelse?'); | ||
expect(req.data.id).to.equal('xyz123'); | ||
expect(req.data.imp).to.eql([ | ||
{ | ||
banner: { | ||
h: 250, | ||
pos: 0, | ||
w: 300, | ||
}, | ||
id: 'foobar', | ||
tagid: '5678' | ||
} | ||
]); | ||
}); | ||
|
||
it('should not return a request when no valid bid request used', function () { | ||
expect(spec.buildRequests([], bidderRequest)).to.be.undefined; | ||
expect(spec.buildRequests([validBidRequest], null)).to.be.undefined; | ||
}); | ||
}); | ||
|
||
describe('interpretResponse', function () { | ||
let bidResponse = { | ||
id: '10865933907263896~9998~0', | ||
impid: '9876abcd', | ||
price: 0.13, | ||
crid: '1022-250', | ||
adm: '<script src=\"//uat-net.technoratimedia.com/openrtb/tags?AUCTION_ID=${AUCTION_ID}&AUCTION_BID_ID=${AUCTION_BID_ID}&AUCTION_SEAT_ID=${AUCTION_SEAT_ID}&AUCTION_PRICE=${AUCTION_PRICE}&AUCTION_IMP_ID=${AUCTION_IMP_ID}&SOMETHING=${UNSUPPORTED_MACRO}&AUCTION_CURRENCY=${AUCTION_CURRENCY}\"></script>' | ||
}; | ||
spec.sizeMap['9876abcd'] = [300, 250]; | ||
|
||
let serverResponse; | ||
beforeEach(function() { | ||
serverResponse = { | ||
body: { | ||
id: 'abc123', | ||
seatbid: [{ | ||
seat: '9998', | ||
bid: [], | ||
}] | ||
} | ||
}; | ||
}); | ||
it('should return a bid when bid is in the response', function () { | ||
serverResponse.body.seatbid[0].bid.push(bidResponse); | ||
let resp = spec.interpretResponse(serverResponse); | ||
expect(resp).to.be.an('array').that.is.not.empty; | ||
expect(resp[0]).to.eql({ | ||
requestId: '9876abcd', | ||
cpm: 0.13, | ||
width: 300, | ||
height: 250, | ||
creativeId: '9998~1022-250', | ||
currency: 'USD', | ||
netRevenue: true, | ||
mediaType: BANNER, | ||
ad: '<script src=\"//uat-net.technoratimedia.com/openrtb/tags?AUCTION_ID=abc123&AUCTION_BID_ID=10865933907263896~9998~0&AUCTION_SEAT_ID=9998&AUCTION_PRICE=0.13&AUCTION_IMP_ID=9876abcd&SOMETHING=${UNSUPPORTED_MACRO}&AUCTION_CURRENCY=USD\"></script>', | ||
ttl: 60 | ||
}); | ||
}); | ||
it('should not return a bid when no bid is in the response', function () { | ||
let resp = spec.interpretResponse(serverResponse); | ||
expect(resp).to.be.an('array').that.is.empty; | ||
}); | ||
it('should not return a bid when there is no response body', function () { | ||
expect(spec.interpretResponse({ body: null })).to.not.exist; | ||
expect(spec.interpretResponse({ body: 'some error text' })).to.not.exist; | ||
}); | ||
}); | ||
describe('getUserSyncs', function () { | ||
it('should return a usersync when iframes is enabled', function () { | ||
let usersyncs = spec.getUserSyncs({ | ||
iframeEnabled: true | ||
}, null); | ||
expect(usersyncs).to.be.an('array').that.is.not.empty; | ||
expect(usersyncs[0]).to.have.property('type', 'iframe'); | ||
expect(usersyncs[0]).to.have.property('url'); | ||
expect(usersyncs[0].url).to.contain('//prebid.technoratimedia.com/usersync/html'); | ||
}); | ||
|
||
it('should not return a usersync when iframes are not enabled', function () { | ||
let usersyncs = spec.getUserSyncs({ | ||
pixelEnabled: true | ||
}, null); | ||
expect(usersyncs).to.be.an('array').that.is.empty; | ||
}); | ||
}); | ||
}); |