Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add freewheel ssp bidder adapter for prebid 1.0 #1793

Merged
merged 52 commits into from
Dec 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
632c8c1
add stickyadsTV bidder adapter
Jan 5, 2017
a91ac2d
init unit test file
Jan 5, 2017
9e2d70e
ad some unit tests
Jan 5, 2017
9c92c3e
fix unit test on ad format with parameters
Jan 5, 2017
7ed68e9
add some unit tests
Jan 6, 2017
88a45ac
add unit tests on getBid method
Jan 6, 2017
7d6bf3c
add some test cases in unit tests
Jan 6, 2017
affdd67
minor fix on component id tag.
Jan 9, 2017
654a473
remove adapters-sticky.json test file
Jan 12, 2017
3b0760e
Merge remote-tracking branch 'Prebid/master'
Jan 12, 2017
8c0f143
use top most accessible window instead of window.top
Jan 26, 2017
fd877dc
Pass in the bid request in the createBid call.
Jan 26, 2017
09f7348
use top most accessible window instead of window.top
Feb 3, 2017
28a36a6
Merge remote-tracking branch 'Prebid/master'
Feb 3, 2017
90264e8
add unit tests
Feb 3, 2017
29453af
update unit tests
Feb 3, 2017
b664ee1
fix unit test.
Feb 3, 2017
57c31a1
fix CI build
Feb 6, 2017
2915576
Merge remote-tracking branch 'Prebid/master'
Feb 9, 2017
c5c41be
Merge remote-tracking branch 'Prebid/master'
Mar 1, 2017
475eee3
add alias freewheel-ssp
Mar 2, 2017
71bfaa1
Merge remote-tracking branch 'Prebid/master'
Mar 10, 2017
410ee00
update unit tests on bidderCode value
Mar 10, 2017
8f7c947
Merge branch 'master' into master
Mar 30, 2017
1d7be59
Merge remote-tracking branch 'Prebid/master'
Jun 7, 2017
b09dfdd
fix component id values and add unit tests
Jun 16, 2017
d5e6d79
Merge remote-tracking branch 'Prebid/master'
Jun 16, 2017
d52df6f
allws to use any outstream format.
Jun 20, 2017
9ad2b7f
fix ASLoader on futur outstream format versions
Jun 20, 2017
427b45c
minor: fix code format.
Jun 20, 2017
c65c62d
update unit tests
Jun 20, 2017
23639ff
minor fix code format
Jun 20, 2017
8c26777
Merge remote-tracking branch 'Prebid/master'
Jun 20, 2017
7b2ff67
minor: add missing new line at eof
Jun 20, 2017
cea7ebd
Merge remote-tracking branch 'Prebid/master'
Jun 30, 2017
025f361
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Oct 27, 2017
ecaff5e
replace StickyAdsTVAdapter by freewheel ssp bd adapter (for prebid 1.0)
guillaume-sticky Oct 27, 2017
584d8fd
remove old stickyadstv unittest spec.
guillaume-sticky Oct 30, 2017
545876d
fix server response parsing if sent as object with 'body' field
guillaume-sticky Oct 30, 2017
4735b30
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Oct 30, 2017
c6db605
use the vastXml field for video mediatype
guillaume-sticky Oct 30, 2017
6740fbf
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Oct 31, 2017
8aae6c6
add user sync pixel in freewheel ssp adapter
guillaume-sticky Oct 31, 2017
6e4d9df
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Nov 2, 2017
fc3e217
remove all console log calls (replaced using util helper)
guillaume-sticky Nov 23, 2017
8d6360b
remove useless bidderCode (automatically added by the bidderFactory)
guillaume-sticky Nov 23, 2017
4ccb39d
Return the SYNC pixel to be added in the page by Prebid.js
guillaume-sticky Nov 23, 2017
57f7c27
remove instance level properties to enable concurrent bids with the s…
guillaume-sticky Nov 23, 2017
efee22b
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Nov 23, 2017
91029d9
fix the request apss through and corresponding unit tests
guillaume-sticky Nov 23, 2017
cf55f76
fix 'freeheelssp' typo
guillaume-sticky Dec 7, 2017
10ce504
Merge remote-tracking branch 'Prebid/master'
guillaume-sticky Dec 8, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
314 changes: 314 additions & 0 deletions modules/freewheelSSPBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';
// import { config } from 'src/config';

const BIDDER_CODE = 'freewheel-ssp';

const PROTOCOL = getProtocol();
const FREEWHEEL_ADSSETUP = PROTOCOL + '://ads.stickyadstv.com/www/delivery/swfIndex.php';
const MUSTANG_URL = PROTOCOL + '://cdn.stickyadstv.com/mustang/mustang.min.js';
const PRIMETIME_URL = PROTOCOL + '://cdn.stickyadstv.com/prime-time/';
const USER_SYNC_URL = PROTOCOL + '://ads.stickyadstv.com/auto-user-sync';

function getProtocol() {
if (location.protocol && location.protocol.indexOf('https') === 0) {
return 'https';
} else {
return 'http';
}
}

function isValidUrl(str) {
if (!str) {
return false;
}

// regExp for url validation
var pattern = /^(https?|ftp|file):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
return pattern.test(str);
}

function getBiggerSize(array) {
var result = [0, 0];
for (var i = 0; i < array.length; i++) {
if (array[i][0] * array[i][1] > result[0] * result[1]) {
result = array[i];
}
}
return result;
}

/*
* read the pricing extension with this format: <Extension type='StickyPricing'><Price currency="EUR">1.0000</Price></Extension>
* @return {object} pricing data in format: {currency: "EUR", price:"1.000"}
*/
function getPricing(xmlNode) {
var pricingExtNode;
var princingData = {};

var extensions = xmlNode.querySelectorAll('Extension');
extensions.forEach(function(node) {
if (node.getAttribute('type') === 'StickyPricing') {
pricingExtNode = node;
}
});

if (pricingExtNode) {
var priceNode = pricingExtNode.querySelector('Price');
princingData = {
currency: priceNode.getAttribute('currency'),
price: priceNode.textContent || priceNode.innerText
};
} else {
utils.logWarn('PREBID - ' + BIDDER_CODE + ': Can\'t get pricing data. Is price awareness enabled?');
}

return princingData;
}

function getCreativeId(xmlNode) {
var creaId = '';
var adNodes = xmlNode.querySelectorAll('Ad');

adNodes.forEach(function(el) {
creaId += '[' + el.getAttribute('id') + ']';
});

return creaId;
}

/**
* returns the top most accessible window
*/
function getTopMostWindow() {
var res = window;

try {
while (top !== res) {
if (res.parent.location.href.length) { res = res.parent; }
}
} catch (e) {}

return res;
}

function getComponentId(inputFormat) {
var component = 'mustang'; // default component id

if (inputFormat && inputFormat !== 'inbanner') {
// format identifiers are equals to their component ids.
component = inputFormat;
}

return component;
}

function getAPIName(componentId) {
componentId = componentId || '';

// remove dash in componentId to get API name
return componentId.replace('-', '');
}

function formatAdHTML(bid, size) {
var integrationType = bid.params.format;

var divHtml = '<div id="freewheelssp_prebid_target"></div>';

var script = '';
var libUrl = '';
if (integrationType && integrationType !== 'inbanner') {
libUrl = PRIMETIME_URL + getComponentId(bid.params.format) + '.min.js';
script = getOutstreamScript(bid, size);
} else {
libUrl = MUSTANG_URL;
script = getInBannerScript(bid, size);
}

return divHtml +
'<script type=\'text/javascript\'>' +
'(function() {' +
' var st = document.createElement(\'script\'); st.type = \'text/javascript\'; st.async = true;' +
' st.src = \'' + libUrl + '\';' +
' st.onload = function(){' +
' var vastLoader = new window.com.stickyadstv.vast.VastLoader();' +
' var vast = vastLoader.getVast();' +
// get the top most accessible window
' var topWindow = (function(){var res=window; try{while(top != res){if(res.parent.location.href.length)res=res.parent;}}catch(e){}return res;})();' +
// inject the xml in the Vast object as string
' vast.setXmlString(topWindow.freewheelssp_cache["' + bid.adUnitCode + '"]);' +
// force ad parsing on the given vast xml
' vastLoader.parseAds(vast, {' +
' onSuccess: function() {' + script + ' }' +
' });' +
' };' +
' document.head.appendChild(st);' +
'})();' +
'</script>';
}

var getInBannerScript = function(bid, size) {
return 'var config = {' +
' preloadedVast:vast,' +
' autoPlay:true' +
' };' +
' var ad = new window.com.stickyadstv.vpaid.Ad(document.getElementById("freewheelssp_prebid_target"),config);' +
' (new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')).registerEvents(ad);' +
' ad.initAd(' + size[0] + ',' + size[1] + ',"",0,"","");';
};

var getOutstreamScript = function(bid) {
var placementCode = bid.adUnitCode;

var config = bid.params;

// default placement if no placement is set
if (!config.hasOwnProperty('domId') && !config.hasOwnProperty('auto') && !config.hasOwnProperty('p') && !config.hasOwnProperty('article')) {
config.domId = placementCode;
}

var script = 'var config = {' +
' preloadedVast:vast,' +
' ASLoader:new window.com.stickyadstv.tools.ASLoader(' + bid.params.zoneId + ', \'' + getComponentId(bid.params.format) + '\')';

for (var key in config) {
// dont' send format parameter
// neither zone nor vastUrlParams value as Vast is already loaded
if (config.hasOwnProperty(key) && key !== 'format' && key !== 'zone' && key !== 'zoneId' && key !== 'vastUrlParams') {
script += ',' + key + ':"' + config[key] + '"';
}
}
script += '};' +

'window.com.stickyadstv.' + getAPIName(bid.params.format) + '.start(config);';

return script;
};

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: ['video'],
aliases: ['stickyadstv'], // former name for freewheel-ssp
/**
* Determines whether or not the given bid request is valid.
*
* @param {object} bid The bid to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid: function(bid) {
return !!(bid.params.zoneId);
},

/**
* Make a server request from the list of BidRequests.
*
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be sent to the Server.
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function(bidRequests) {
// var currency = config.getConfig(currency);

var currentBidRequest = bidRequests[0];
if (bidRequests.length > 1) {
utils.logMessage('Prebid.JS - freewheel bid adapter: only one ad unit is required.');
}

var requestParams = {
reqType: 'AdsSetup',
protocolVersion: '2.0',
zoneId: currentBidRequest.params.zoneId,
componentId: getComponentId(currentBidRequest.params.format)
};

var location = utils.getTopWindowUrl();
if (isValidUrl(location)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you validating that location.href is a valid URL? That seems unnecessary....

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to receive invalid urls. If loaded inside some iframe we could retreive location : "about:blank" for instance.

requestParams.loc = location;
}

var playerSize = getBiggerSize(currentBidRequest.sizes);
if (playerSize[0] > 0 || playerSize[1] > 0) {
requestParams.playerSize = playerSize[0] + 'x' + playerSize[1];
}

return {
method: 'GET',
url: FREEWHEEL_ADSSETUP,
data: requestParams,
bidRequest: currentBidRequest
};
},

/**
* Unpack the response from the server into a list of bids.
*
* @param {*} serverResponse A successful response from the server.
* @param {object} request: the built request object containing the initial bidRequest.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(serverResponse, request) {
var bidrequest = request.bidRequest;
var playerSize = getBiggerSize(bidrequest.sizes);

if (typeof serverResponse == 'object' && typeof serverResponse.body == 'string') {
serverResponse = serverResponse.body;
}

var xmlDoc;
try {
var parser = new DOMParser();
xmlDoc = parser.parseFromString(serverResponse, 'application/xml');
} catch (err) {
utils.logWarn('Prebid.js - ' + BIDDER_CODE + ' : ' + err);
return;
}

const princingData = getPricing(xmlDoc);
const creativeId = getCreativeId(xmlDoc);

const topWin = getTopMostWindow();
if (!topWin.freewheelssp_cache) {
topWin.freewheelssp_cache = {};
}
topWin.freewheelssp_cache[bidrequest.adUnitCode] = serverResponse;

const bidResponses = [];

if (princingData.price) {
const bidResponse = {
requestId: bidrequest.bidId,
cpm: princingData.price,
width: playerSize[0],
height: playerSize[1],
creativeId: creativeId,
currency: princingData.currency,
netRevenue: true,
ttl: 360
};

var mediaTypes = bidrequest.mediaTypes || {};
if (mediaTypes.video) {
// bidResponse.vastXml = serverResponse;
bidResponse.mediaType = 'video';

var blob = new Blob([serverResponse], {type: 'application/xml'});
bidResponse.vastUrl = window.URL.createObjectURL(blob);
} else {
bidResponse.ad = formatAdHTML(bidrequest, playerSize);
}

bidResponses.push(bidResponse);
}

return bidResponses;
},

getUserSyncs: function(syncOptions) {
if (syncOptions.pixelEnabled) {
return [{
type: 'image',
url: USER_SYNC_URL
}];
}
}
}
registerBidder(spec);
27 changes: 27 additions & 0 deletions modules/freewheelSSPBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Overview

Module Name: Freewheel SSP Bidder Adapter
Module Type: Bidder Adapter
Maintainer: clientsidesdk@freewheel.tv

# Description

Module that connects to Freewheel ssp's demand sources

# Test Parameters
```
var adUnits = [
{
code: 'test-div',
sizes: [[300, 250]], // a display size
bids: [
{
bidder: "freewheel-ssp",
params: {
zoneId : '277225'
}
}
]
}
];
```
Loading