diff --git a/modules/smartadserverBidAdapter.js b/modules/smartadserverBidAdapter.js
new file mode 100644
index 00000000000..1e14195d38a
--- /dev/null
+++ b/modules/smartadserverBidAdapter.js
@@ -0,0 +1,153 @@
+import * as utils from '../src/utils';
+import {
+ BANNER,
+ VIDEO
+} from '../src/mediaTypes';
+import {
+ config
+} from '../src/config';
+import {
+ registerBidder
+} from '../src/adapters/bidderFactory';
+const BIDDER_CODE = 'smartadserver';
+export const spec = {
+ code: BIDDER_CODE,
+ aliases: ['smart'], // short code
+ supportedMediaTypes: [BANNER, VIDEO],
+ /**
+ * Determines whether or not the given bid request is valid.
+ *
+ * @param {BidRequest} bid The bid params to validate.
+ * @return boolean True if this is a valid bid, and false otherwise.
+ */
+ isBidRequestValid: function (bid) {
+ return !!(bid.params && bid.params.siteId && bid.params.pageId && bid.params.formatId);
+ },
+ /**
+ * Make a server request from the list of BidRequests.
+ *
+ * @param {validBidRequests[]} - an array of bids
+ * @param {bidderRequest} - bidder request object
+ * @return ServerRequest Info describing the request to the server.
+ */
+ buildRequests: function (validBidRequests, bidderRequest) {
+ // use bidderRequest.bids[] to get bidder-dependent request info
+
+ // if your bidder supports multiple currencies, use config.getConfig(currency)
+ // to find which one the ad server needs
+
+ // pull requested transaction ID from bidderRequest.bids[].transactionId
+ return validBidRequests.map(bid => {
+ // Common bid request attributes for banner, outstream and instream.
+ var payload = {
+ siteid: bid.params.siteId,
+ pageid: bid.params.pageId,
+ formatid: bid.params.formatId,
+ currencyCode: config.getConfig('currency.adServerCurrency'),
+ bidfloor: bid.params.bidfloor || 0.0,
+ targeting: bid.params.target && bid.params.target != '' ? bid.params.target : undefined,
+ buid: bid.params.buId && bid.params.buId != '' ? bid.params.buId : undefined,
+ appname: bid.params.appName && bid.params.appName != '' ? bid.params.appName : undefined,
+ ckid: bid.params.ckId || 0,
+ tagId: bid.adUnitCode,
+ pageDomain: bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer ? bidderRequest.refererInfo.referer : undefined,
+ transactionId: bid.transactionId,
+ timeout: config.getConfig('bidderTimeout'),
+ bidId: bid.bidId,
+ prebidVersion: '$prebid.version$'
+ };
+
+ const videoMediaType = utils.deepAccess(bid, 'mediaTypes.video');
+ if (!videoMediaType) {
+ const bannerMediaType = utils.deepAccess(bid, 'mediaTypes.banner');
+ payload.sizes = bannerMediaType.sizes.map(size => ({
+ w: size[0],
+ h: size[1]
+ }));
+ } else if (videoMediaType && videoMediaType.context === 'instream') {
+ // Specific attributes for instream.
+ var playerSize = videoMediaType.playerSize[0];
+ payload.isVideo = true;
+ payload.videoData = {
+ videoProtocol: bid.params.video.protocol,
+ playerWidth: playerSize[0],
+ playerHeight: playerSize[1],
+ adBreak: bid.params.video.startDelay || 0
+ };
+ } else {
+ return {};
+ }
+
+ if (bidderRequest && bidderRequest.gdprConsent) {
+ payload.gdpr_consent = bidderRequest.gdprConsent.consentString;
+ payload.gdpr = bidderRequest.gdprConsent.gdprApplies; // we're handling the undefined case server side
+ }
+
+ var payloadString = JSON.stringify(payload);
+ return {
+ method: 'POST',
+ url: (bid.params.domain !== undefined ? bid.params.domain : 'https://prg.smartadserver.com') + '/prebid/v1',
+ data: payloadString,
+ };
+ });
+ },
+ /**
+ * Unpack the response from the server into a list of bids.
+ *
+ * @param {*} serverResponse A successful response from the server.
+ * @return {Bid[]} An array of bids which were nested inside the server.
+ */
+ interpretResponse: function (serverResponse, bidRequestString) {
+ const bidResponses = [];
+ var response = serverResponse.body;
+ try {
+ if (response) {
+ const bidRequest = JSON.parse(bidRequestString.data);
+
+ var bidResponse = {
+ requestId: bidRequest.bidId,
+ cpm: response.cpm,
+ width: response.width,
+ height: response.height,
+ creativeId: response.creativeId,
+ dealId: response.dealId,
+ currency: response.currency,
+ netRevenue: response.isNetCpm,
+ ttl: response.ttl
+ };
+
+ if (bidRequest.isVideo) {
+ bidResponse.mediaType = VIDEO;
+ bidResponse.vastUrl = response.adUrl;
+ bidResponse.vastXml = response.ad;
+ } else {
+ bidResponse.adUrl = response.adUrl;
+ bidResponse.ad = response.ad;
+ }
+
+ bidResponses.push(bidResponse);
+ }
+ } catch (error) {
+ utils.logError('Error while parsing smart server response', error);
+ }
+ return bidResponses;
+ },
+ /**
+ * User syncs.
+ *
+ * @param {*} syncOptions Publisher prebid configuration.
+ * @param {*} serverResponses A successful response from the server.
+ * @return {Syncs[]} An array of syncs that should be executed.
+ */
+ getUserSyncs: function (syncOptions, serverResponses) {
+ const syncs = []
+ if (syncOptions.iframeEnabled && serverResponses.length > 0) {
+ syncs.push({
+ type: 'iframe',
+ url: serverResponses[0].body.cSyncUrl
+ });
+ }
+ return syncs;
+ }
+}
+registerBidder(spec);
diff --git a/modules/smartadserverBidAdapter.md b/modules/smartadserverBidAdapter.md
index c1fc0d819c5..84fe5ff8afc 100644
--- a/modules/smartadserverBidAdapter.md
+++ b/modules/smartadserverBidAdapter.md
@@ -20,12 +20,16 @@ Please reach out to your Technical account manager for more information.
var adUnits = [
{
code: 'test-div',
- sizes: [[300, 250]], // a display size
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
bids: [
{
bidder: "smart",
params: {
- domain: 'http://ww251.smartadserver.com',
+ domain: 'https://ww251.smartadserver.com',
siteId: 207435,
pageId: 896536,
formatId: 62913,
@@ -42,12 +46,16 @@ Please reach out to your Technical account manager for more information.
var adUnits = [
{
code: 'test-div',
- sizes: [[300, 250]], // a display size
+ mediaTypes: {
+ banner: {
+ sizes: [[300, 250]]
+ }
+ },
bids: [
{
bidder: "smart",
params: {
- domain: 'http://ww251.smartadserver.com',
+ domain: 'https://ww251.smartadserver.com',
siteId: 207435,
pageId: 896536,
formatId: 65906,
@@ -74,7 +82,7 @@ Please reach out to your Technical account manager for more information.
bids: [{
bidder: "smart",
params: {
- domain: 'http://ww251.smartadserver.com',
+ domain: 'https://ww251.smartadserver.com',
siteId: 326147,
pageId: 1153895,
formatId: 55710
diff --git a/test/spec/modules/smartadserverBidAdapter_spec.js b/test/spec/modules/smartadserverBidAdapter_spec.js
new file mode 100644
index 00000000000..caa806f9ccc
--- /dev/null
+++ b/test/spec/modules/smartadserverBidAdapter_spec.js
@@ -0,0 +1,407 @@
+import {
+ expect
+} from 'chai';
+import {
+ spec
+} from 'modules/smartadserverBidAdapter';
+import {
+ newBidder
+} from 'src/adapters/bidderFactory';
+import {
+ config
+} from 'src/config';
+import * as utils from 'src/utils';
+import { requestBidsHook } from 'modules/consentManagement';
+
+// Default params with optional ones
+describe('Smart bid adapter tests', function () {
+ var DEFAULT_PARAMS = [{
+ adUnitCode: 'sas_42',
+ bidId: 'abcd1234',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 200]
+ ]
+ }
+ },
+ bidder: 'smartadserver',
+ params: {
+ domain: 'https://prg.smartadserver.com',
+ siteId: '1234',
+ pageId: '5678',
+ formatId: '90',
+ target: 'test=prebid',
+ bidfloor: 0.420,
+ buId: '7569',
+ appName: 'Mozilla',
+ ckId: 42
+ },
+ requestId: 'efgh5678',
+ transactionId: 'zsfgzzg'
+ }];
+
+ // Default params without optional ones
+ var DEFAULT_PARAMS_WO_OPTIONAL = [{
+ adUnitCode: 'sas_42',
+ bidId: 'abcd1234',
+ mediaTypes: {
+ banner: {
+ sizes: [
+ [300, 250],
+ [300, 200]
+ ],
+ }
+ },
+
+ bidder: 'smartadserver',
+ params: {
+ domain: 'https://prg.smartadserver.com',
+ siteId: '1234',
+ pageId: '5678',
+ formatId: '90'
+ },
+ requestId: 'efgh5678'
+ }];
+
+ var BID_RESPONSE = {
+ body: {
+ cpm: 12,
+ width: 300,
+ height: 250,
+ creativeId: 'zioeufg',
+ currency: 'GBP',
+ isNetCpm: true,
+ ttl: 300,
+ adUrl: 'http://awesome.fake.url',
+ ad: '< --- awesome script --- >',
+ cSyncUrl: 'http://awesome.fake.csync.url'
+ }
+ };
+
+ it('Verify build request', function () {
+ config.setConfig({
+ 'currency': {
+ 'adServerCurrency': 'EUR'
+ }
+ });
+ const request = spec.buildRequests(DEFAULT_PARAMS);
+ expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
+ expect(request[0]).to.have.property('method').and.to.equal('POST');
+ const requestContent = JSON.parse(request[0].data);
+
+ expect(requestContent).to.have.property('siteid').and.to.equal('1234');
+ expect(requestContent).to.have.property('pageid').and.to.equal('5678');
+ expect(requestContent).to.have.property('formatid').and.to.equal('90');
+ expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
+ expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
+ expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
+ expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
+ expect(requestContent).to.have.property('sizes');
+ expect(requestContent.sizes[0]).to.have.property('w').and.to.equal(300);
+ expect(requestContent.sizes[0]).to.have.property('h').and.to.equal(250);
+ expect(requestContent.sizes[1]).to.have.property('w').and.to.equal(300);
+ expect(requestContent.sizes[1]).to.have.property('h').and.to.equal(200);
+ expect(requestContent).to.not.have.property('pageDomain');
+ expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
+ expect(requestContent).to.have.property('buid').and.to.equal('7569');
+ expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
+ expect(requestContent).to.have.property('ckid').and.to.equal(42);
+ });
+
+ it('Verify parse response', function () {
+ const request = spec.buildRequests(DEFAULT_PARAMS);
+ const bids = spec.interpretResponse(BID_RESPONSE, request[0]);
+ expect(bids).to.have.lengthOf(1);
+ const bid = bids[0];
+ expect(bid.cpm).to.equal(12);
+ expect(bid.adUrl).to.equal('http://awesome.fake.url');
+ expect(bid.ad).to.equal('< --- awesome script --- >');
+ expect(bid.width).to.equal(300);
+ expect(bid.height).to.equal(250);
+ expect(bid.creativeId).to.equal('zioeufg');
+ expect(bid.currency).to.equal('GBP');
+ expect(bid.netRevenue).to.equal(true);
+ expect(bid.ttl).to.equal(300);
+ expect(bid.requestId).to.equal(DEFAULT_PARAMS[0].bidId);
+
+ expect(function () {
+ spec.interpretResponse(BID_RESPONSE, {
+ data: 'invalid Json'
+ })
+ }).to.not.throw();
+ });
+
+ it('Verifies bidder code', function () {
+ expect(spec.code).to.equal('smartadserver');
+ });
+
+ it('Verifies bidder aliases', function () {
+ expect(spec.aliases).to.have.lengthOf(1);
+ expect(spec.aliases[0]).to.equal('smart');
+ });
+
+ it('Verifies if bid request valid', function () {
+ expect(spec.isBidRequestValid(DEFAULT_PARAMS[0])).to.equal(true);
+ expect(spec.isBidRequestValid(DEFAULT_PARAMS_WO_OPTIONAL[0])).to.equal(true);
+ expect(spec.isBidRequestValid({})).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {}
+ })).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {
+ pageId: 123
+ }
+ })).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {
+ siteId: 123
+ }
+ })).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {
+ formatId: 123,
+ pageId: 234
+ }
+ })).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {
+ domain: 'www.test.com',
+ pageId: 234
+ }
+ })).to.equal(false);
+ expect(spec.isBidRequestValid({
+ params: {
+ domain: 'www.test.com',
+ formatId: 123,
+ siteId: 456,
+ pageId: 234
+ }
+ })).to.equal(true);
+ expect(spec.isBidRequestValid({
+ params: {
+ domain: 'www.test.com',
+ formatId: 123,
+ siteId: 456,
+ pageId: 234,
+ buId: 789,
+ appName: 'Mozilla'
+ }
+ })).to.equal(true);
+ expect(spec.isBidRequestValid({
+ params: {
+ domain: 'www.test.com',
+ formatId: 123,
+ pageId: 234,
+ buId: 789,
+ appName: 'Mozilla'
+ }
+ })).to.equal(false);
+ });
+
+ it('Verifies user sync', function () {
+ var syncs = spec.getUserSyncs({
+ iframeEnabled: true
+ }, [BID_RESPONSE]);
+ expect(syncs).to.have.lengthOf(1);
+ expect(syncs[0].type).to.equal('iframe');
+ expect(syncs[0].url).to.equal('http://awesome.fake.csync.url');
+
+ syncs = spec.getUserSyncs({
+ iframeEnabled: false
+ }, [BID_RESPONSE]);
+ expect(syncs).to.have.lengthOf(0);
+
+ syncs = spec.getUserSyncs({
+ iframeEnabled: true
+ }, []);
+ expect(syncs).to.have.lengthOf(0);
+ });
+
+ describe('gdpr tests', function () {
+ afterEach(function () {
+ config.resetConfig();
+ $$PREBID_GLOBAL$$.requestBids.removeAll();
+ });
+
+ it('Verify build request with GDPR', function () {
+ config.setConfig({
+ 'currency': {
+ 'adServerCurrency': 'EUR'
+ },
+ consentManagement: {
+ cmp: 'iab',
+ consentRequired: true,
+ timeout: 1000,
+ allowAuctionWithoutConsent: true
+ }
+ });
+ const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, {
+ gdprConsent: {
+ consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A==',
+ gdprApplies: true
+ }
+ });
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.have.property('gdpr').and.to.equal(true);
+ expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A==');
+ });
+
+ it('Verify build request with GDPR without gdprApplies', function () {
+ config.setConfig({
+ 'currency': {
+ 'adServerCurrency': 'EUR'
+ },
+ consentManagement: {
+ cmp: 'iab',
+ consentRequired: true,
+ timeout: 1000,
+ allowAuctionWithoutConsent: true
+ }
+ });
+ const request = spec.buildRequests(DEFAULT_PARAMS_WO_OPTIONAL, {
+ gdprConsent: {
+ consentString: 'BOKAVy4OKAVy4ABAB8AAAAAZ+A=='
+ }
+ });
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.not.have.property('gdpr');
+ expect(requestContent).to.have.property('gdpr_consent').and.to.equal('BOKAVy4OKAVy4ABAB8AAAAAZ+A==');
+ });
+ });
+
+ describe('Instream video tests', function () {
+ afterEach(function () {
+ config.resetConfig();
+ $$PREBID_GLOBAL$$.requestBids.removeAll();
+ });
+
+ const INSTREAM_DEFAULT_PARAMS = [{
+ adUnitCode: 'sas_42',
+ bidId: 'abcd1234',
+ bidder: 'smartadserver',
+ mediaTypes: {
+ video: {
+ context: 'instream',
+ playerSize: [[640, 480]] // It seems prebid.js transforms the player size array into an array of array...
+ }
+ },
+ params: {
+ siteId: '1234',
+ pageId: '5678',
+ formatId: '90',
+ target: 'test=prebid',
+ bidfloor: 0.420,
+ buId: '7569',
+ appName: 'Mozilla',
+ ckId: 42,
+ video: {
+ protocol: 6
+ }
+ },
+ requestId: 'efgh5678',
+ transactionId: 'zsfgzzg'
+ }];
+
+ var INSTREAM_BID_RESPONSE = {
+ body: {
+ cpm: 12,
+ width: 640,
+ height: 480,
+ creativeId: 'zioeufg',
+ currency: 'GBP',
+ isNetCpm: true,
+ ttl: 300,
+ adUrl: 'http://awesome.fake-vast.url',
+ ad: '',
+ cSyncUrl: 'http://awesome.fake.csync.url'
+ }
+ };
+
+ it('Verify instream video build request', function () {
+ config.setConfig({
+ 'currency': {
+ 'adServerCurrency': 'EUR'
+ }
+ });
+ const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS);
+ expect(request[0]).to.have.property('url').and.to.equal('https://prg.smartadserver.com/prebid/v1');
+ expect(request[0]).to.have.property('method').and.to.equal('POST');
+ const requestContent = JSON.parse(request[0].data);
+ expect(requestContent).to.have.property('siteid').and.to.equal('1234');
+ expect(requestContent).to.have.property('pageid').and.to.equal('5678');
+ expect(requestContent).to.have.property('formatid').and.to.equal('90');
+ expect(requestContent).to.have.property('currencyCode').and.to.equal('EUR');
+ expect(requestContent).to.have.property('bidfloor').and.to.equal(0.42);
+ expect(requestContent).to.have.property('targeting').and.to.equal('test=prebid');
+ expect(requestContent).to.have.property('tagId').and.to.equal('sas_42');
+ expect(requestContent).to.not.have.property('pageDomain');
+ expect(requestContent).to.have.property('transactionId').and.to.not.equal(null).and.to.not.be.undefined;
+ expect(requestContent).to.have.property('buid').and.to.equal('7569');
+ expect(requestContent).to.have.property('appname').and.to.equal('Mozilla');
+ expect(requestContent).to.have.property('ckid').and.to.equal(42);
+ expect(requestContent).to.have.property('isVideo').and.to.equal(true);
+ expect(requestContent).to.have.property('videoData');
+ expect(requestContent.videoData).to.have.property('videoProtocol').and.to.equal(6);
+ expect(requestContent.videoData).to.have.property('playerWidth').and.to.equal(640);
+ expect(requestContent.videoData).to.have.property('playerHeight').and.to.equal(480);
+ });
+
+ it('Verify instream parse response', function () {
+ const request = spec.buildRequests(INSTREAM_DEFAULT_PARAMS);
+ const bids = spec.interpretResponse(INSTREAM_BID_RESPONSE, request[0]);
+ expect(bids).to.have.lengthOf(1);
+ const bid = bids[0];
+ expect(bid.cpm).to.equal(12);
+ expect(bid.mediaType).to.equal('video');
+ expect(bid.vastUrl).to.equal('http://awesome.fake-vast.url');
+ expect(bid.vastXml).to.equal('');
+ expect(bid.width).to.equal(640);
+ expect(bid.height).to.equal(480);
+ expect(bid.creativeId).to.equal('zioeufg');
+ expect(bid.currency).to.equal('GBP');
+ expect(bid.netRevenue).to.equal(true);
+ expect(bid.ttl).to.equal(300);
+ expect(bid.requestId).to.equal(INSTREAM_DEFAULT_PARAMS[0].bidId);
+
+ expect(function () {
+ spec.interpretResponse(INSTREAM_BID_RESPONSE, {
+ data: 'invalid Json'
+ })
+ }).to.not.throw();
+ });
+
+ it('Verify not handled media type return empty request', function () {
+ config.setConfig({
+ 'currency': {
+ 'adServerCurrency': 'EUR'
+ }
+ });
+ const request = spec.buildRequests([{
+ adUnitCode: 'sas_42',
+ bidder: 'smartadserver',
+ mediaTypes: {
+ video: {
+ context: 'badcontext'
+ }
+ },
+ params: {
+ domain: 'https://prg.smartadserver.com',
+ siteId: '1234',
+ pageId: '5678',
+ formatId: '90',
+ target: 'test=prebid',
+ bidfloor: 0.420,
+ buId: '7569',
+ appName: 'Mozilla',
+ ckId: 42
+ },
+ requestId: 'efgh5678',
+ transactionId: 'zsfgzzg'
+ }, INSTREAM_DEFAULT_PARAMS[0]]);
+ expect(request[0]).to.be.empty;
+ expect(request[1]).to.not.be.empty;
+ });
+ });
+});