Skip to content

Commit

Permalink
Initial implementation of synacormedia adapter. (#3425)
Browse files Browse the repository at this point in the history
  • Loading branch information
amuraco authored and idettman committed Jan 11, 2019
1 parent 13982d8 commit 99c4bd7
Show file tree
Hide file tree
Showing 3 changed files with 362 additions and 0 deletions.
121 changes: 121 additions & 0 deletions modules/synacormediaBidAdapter.js
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);
43 changes: 43 additions & 0 deletions modules/synacormediaBidAdapter.md
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"
}
}]
}];
```
198 changes: 198 additions & 0 deletions test/spec/modules/synacormediaBidAdapter_spec.js
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;
});
});
});

0 comments on commit 99c4bd7

Please sign in to comment.