Skip to content

Commit

Permalink
Quantum Advertising Adapter (#2051)
Browse files Browse the repository at this point in the history
* quantumBidAdapter initial commit

* eslint errors fixed

* updating quantumBidAdapter for reviews, fixed tests to work with native

* set new prebid location for testing and some fixing

* added supportedMediaTypes

* Tests fixed

* Fixed issues with image assets. Tested with the example provided
  • Loading branch information
sami-elasticad authored and mike-chowla committed Feb 22, 2018
1 parent 1d76429 commit 853a762
Show file tree
Hide file tree
Showing 3 changed files with 662 additions and 0 deletions.
301 changes: 301 additions & 0 deletions modules/quantumBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
import * as utils from 'src/utils';
import { BANNER, NATIVE } from 'src/mediaTypes';
import {registerBidder} from 'src/adapters/bidderFactory';

const BIDDER_CODE = 'quantum';
const ENDPOINT_URL = '//s.sspqns.com/hb';
export const spec = {
code: BIDDER_CODE,
aliases: ['quantx', 'qtx'], // short code
supportedMediaTypes: [BANNER, NATIVE],

/**
* 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.placementId);
},
/**
* Make a server request from the list of BidRequests.
*
* @param {validBidRequests[]} - an array of bids
* @return ServerRequest Info describing the request to the server.
*/
buildRequests: function (bidRequests) {
return bidRequests.map(bid => {
const qtxRequest = {};
let bidId = '';

const params = bid.params;
let placementId = params.placementId;

let devEnpoint = false;
if (params.useDev && params.useDev === '1') {
devEnpoint = '//sdev.sspqns.com/hb';
}
let renderMode = 'native';
for (let i = 0; i < bid.sizes.length; i++) {
if (bid.sizes[i][0] > 1 && bid.sizes[i][1] > 1) {
renderMode = 'banner';
break;
}
}

let mediaType = (bid.mediaType === 'native' || utils.deepAccess(bid, 'mediaTypes.native')) ? 'native' : 'banner';

if (mediaType === 'native') {
renderMode = 'native';
}

if (!bidId) {
bidId = bid.bidId;
}
qtxRequest.auid = placementId;
const url = devEnpoint || ENDPOINT_URL;

return {
method: 'GET',
bidId: bidId,
sizes: bid.sizes,
mediaType: mediaType,
renderMode: renderMode,
url: url,
'data': qtxRequest
};
});
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {ServerResponse} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function (serverResponse, bidRequest) {
const serverBody = serverResponse.body;
const bidResponses = [];
let responseCPM;
let bid = {};
let id = bidRequest.bidId;

if (serverBody.price && serverBody.price !== 0) {
responseCPM = parseFloat(serverBody.price);

bid.creativeId = serverBody.creative_id || '0';
bid.cpm = responseCPM;
bid.requestId = bidRequest.bidId;
bid.width = 1;
bid.height = 1;
bid.ttl = 200;
bid.netRevenue = true;
bid.currency = 'USD';

if (serverBody.native) {
bid.native = serverBody.native;
}
if (serverBody.cobj) {
bid.cobj = serverBody.cobj;
}

bid.nurl = serverBody.nurl;
bid.sync = serverBody.sync;
if (bidRequest.renderMode && bidRequest.renderMode === 'banner') {
bid.width = 300;
bid.height = 225;
if (serverBody.native) {
const adAssetsUrl = '//cdn.elasticad.net/native/serve/js/quantx/quantumAd/';
let assets = serverBody.native.assets;
let link = serverBody.native.link;

let trackers = [];
if (serverBody.native.imptrackers) {
trackers = serverBody.native.imptrackers;
}

let jstracker = '';
if (serverBody.native.jstracker) {
jstracker = serverBody.native.jstracker;
}

if (serverBody.nurl) {
trackers.push(serverBody.nurl);
}

let ad = {};
ad['trackers'] = trackers;
ad['jstrackers'] = jstracker;

for (let i = 0; i < assets.length; i++) {
let asset = assets[i];
switch (asset['id']) {
case 1:
ad['title'] = asset['title']['text'];
break;
case 2:
ad['sponsor_logo'] = asset['img']['url'];
break;
case 3:
ad['content'] = asset['data']['value'];
break;
case 4:
ad['main_image'] = asset['img']['url'];
break;
case 6:
ad['teaser_type'] = 'vast';
ad['video_url'] = asset['video']['vasttag'];
break;
case 10:
ad['sponsor_name'] = asset['data']['value'];
break;
case 2001:
ad['expanded_content_type'] = 'embed';
ad['expanded_summary'] = asset['data']['value'];
break;
case 2002:
ad['expanded_content_type'] = 'vast';
ad['expanded_summary'] = asset['data']['value'];
break;
case 2003:
ad['sponsor_url'] = asset['data']['value'];
break;
case 2004: // prism
ad['content_type'] = 'prism';
break;
case 2005: // internal_landing_page
ad['content_type'] = 'internal_landing_page';
ad['internal_content_link'] = asset['data']['value'];
break;
case 2006: // teaser as vast
ad['teaser_type'] = 'vast';
ad['video_url'] = asset['data']['value'];
break;
case 2007:
ad['autoexpand_content_type'] = asset['data']['value'];
break;
case 2022: // content page
ad['content_type'] = 'full_text';
ad['full_text'] = asset['data']['value'];
break;
}
}

ad['action_url'] = link.url;

if (!ad['sponsor_url']) {
ad['sponsor_url'] = ad['action_url'];
}

ad['clicktrackers'] = [];
if (link.clicktrackers) {
ad['clicktrackers'] = link.clicktrackers;
}

ad['main_image'] = '//resize-ssp.elasticad.net/scalecrop-290x130/' + window.btoa(ad['main_image']) + '/external';

bid.ad = '<div id="ead_' + id + '\">' +
'<div class="ad_container ead_' + id + '" style="clear: both; display:inline-block;width:100%">' +
' <div class="image_content">' +
' <a href="' + ad['action_url'] + '" class="ea_expand" target="_blank"><img src="' + ad['main_image'] + '" class="ea_image ead_image">' +
' </a>' +
' </div>' +
' <div class="ead_content"><a href="' + ad['action_url'] + '" class="ea_expand" style="text-decoration: none" target="_blank"><h2 style="margin:0px;">' + ad['title'] + '</h2></a>' +
' <p class="ea_summary">' + ad['content'] + '&nbsp;</p></div>' +
' <div style="text-align:right;" class="ea_hide_brand_logo ea_hide_brand_name">' +
' <p style="margin:0;"><span class="ea_creative_var_label">Sponsored by</span>' +
' <a href="' + ad['sponsor_url'] + '" class="ea_link" target="_blank" style="display:inline;" target="_blank"><img src="' + ad['sponsor_logo'] + '" class="ea_image" style="vertical-align:middle;"></a>' +
' </p>' +
' </div>' +
'</div>' +
'<script type="application/javascript">var eanAD = ' + JSON.stringify(ad) + ';</script>' +
'<script src="' + adAssetsUrl + 'qad.js" type="application/javascript"></script>' +
'<link rel="stylesheet" href="' + adAssetsUrl + 'qad.css">' +
'</div>';
}
} else {
// native
if (bidRequest.mediaType === 'native') {
if (serverBody.native) {
let assets = serverBody.native.assets;
let link = serverBody.native.link;

let trackers = [];
if (serverBody.native.imptrackers) {
trackers = serverBody.native.imptrackers;
}

if (serverBody.nurl) {
trackers.push(serverBody.nurl);
}

let native = {};

for (let i = 0; i < assets.length; i++) {
let asset = assets[i];
switch (asset['id']) {
case 1:
native.title = asset['title']['text'];
break;
case 2:
native.icon = asset['img'];
break;
case 3:
native.body = asset['data']['value'];
break;
case 4:
native.image = asset['img'];
break;
case 10:
native.sponsoredBy = asset['data']['value'];
break;
}
}
native.cta = 'read more';
if (serverBody.language) {
native.cta = 'read more';
}

native.clickUrl = link.url;
native.impressionTrackers = trackers;
if (link.clicktrackers) {
native.clickTrackers = link.clicktrackers;
}

bid.qtx_native = utils.deepClone(serverBody.native);
bid.native = native;
}
}
}
bidResponses.push(bid);
}

return bidResponses;
},

/**
* Register the user sync pixels which should be dropped after the auction.
*
* @param {SyncOptions} syncOptions Which user syncs are allowed?
* @param {ServerResponse[]} serverResponses List of server's responses.
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs: function (syncOptions, serverResponses) {
const syncs = []
if (syncOptions.iframeEnabled) {
syncs.push({
type: 'iframe',
url: '//acdn.adnxs.com/ib/static/usersync/v3/async_usersync.html'
});
}
if (syncOptions.pixelEnabled && serverResponses.length > 0) {
syncs.push({
type: 'image',
url: serverResponses[0].body.sync[0]
});
}
return syncs;
}
}
registerBidder(spec);
94 changes: 94 additions & 0 deletions modules/quantumBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Overview

```
Module Name: Quantum Advertising Bid Adapter
Module Type: Bidder Adapter
Maintainer: sami@elasticad.com
```

# Description

Connects to Quantum's ssp for bids.

# Sample Ad Unit: For Publishers
```
var adUnits = [{
code: 'quantum-adUnit-id-1',
sizes: [[300, 250]],
bids: [{
bidder: 'quantum',
params: {
placementId: 21546 //quantum adUnit id
}
}]
},{
code: 'quantum-native-adUnit-id-1',
sizes: [[0, 0]],
mediaTypes: 'native',
bids: [{
bidder: 'quantum',
params: {
placementId: 21546 //quantum adUnit id
}
}]
}];
```

# Ad Unit and Setup: For Testing

```
<html>
<body style="margin:0;padding:0">
<script>
var pbjs = eanpbjs = pbjs || {};
pbjs.que = pbjs.que || [];
pbjs.logging = true;
(function() {
var pbjsEl = document.createElement("script");
pbjsEl.type = "text/javascript";
pbjsEl.async = true;
var isHttps = 'https:' === document.location.protocol;
pbjsEl.src = "//cdn.elasticad.net/native/serve/js/quantx/quantumPrebidAdapter/prebid.js";
var pbjsTargetEl = document.getElementsByTagName("head")[0];
pbjsTargetEl.insertBefore(pbjsEl, pbjsTargetEl.firstChild);
})();
var adUnitID = 'pl-100';
pbjs.que.push(function() {
var adUnits = [{
code: adUnitID,
sizes: [[300, 250]],
bids: [{
bidder: 'quantum',
params: {
placementId: 21546 //quantum adUnit id
}
}]
}];
pbjs.addAdUnits(adUnits);
pbjs.requestBids({
timeout: 800,
bidsBackHandler: function() {
var adUnitData = pbjs.getAdserverTargetingForAdUnitCode(adUnitID);
//console.log('adUnitData', adUnitData);
if(adUnitData['hb_adid']){
var iframe = document.getElementById('placement');
var iframeDoc = iframe.contentWindow.document;
pbjs.renderAd(iframeDoc, adUnitData['hb_adid']);
}
}
});
});
</script>
<iframe id='placement' FRAMEBORDER="0" SCROLLING="no" MARGINHEIGHT="0" MARGINWIDTH="0" TOPMARGIN="0" LEFTMARGIN="0" ALLOWTRANSPARENCY="true" WIDTH="0" HEIGHT="0"></iframe>
</body>
</html>
```
Loading

0 comments on commit 853a762

Please sign in to comment.