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

Upgrade Quantcast adapter for Prebid 1.0 #1753

Merged
merged 24 commits into from Oct 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
255 changes: 146 additions & 109 deletions modules/quantcastBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,128 +1,165 @@
const utils = require('src/utils.js');
const bidfactory = require('src/bidfactory.js');
const bidmanager = require('src/bidmanager.js');
const ajax = require('src/ajax.js');
const CONSTANTS = require('src/constants.json');
const adaptermanager = require('src/adaptermanager');
const QUANTCAST_CALLBACK_URL = 'http://global.qc.rtb.quantserve.com:8080/qchb';

var QuantcastAdapter = function QuantcastAdapter() {
const BIDDER_CODE = 'quantcast';

const DEFAULT_BID_FLOOR = 0.0000000001;
let bidRequests = {};

let returnEmptyBid = function(bidId) {
var bidRequested = utils.getBidRequest(bidId);
if (!utils.isEmpty(bidRequested)) {
let bid = bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bidRequested);
bid.bidderCode = BIDDER_CODE;
bidmanager.addBidResponse(bidRequested.placementCode, bid);
import * as utils from 'src/utils';
import { registerBidder } from 'src/adapters/bidderFactory';

const BIDDER_CODE = 'quantcast';
const DEFAULT_BID_FLOOR = 0.0000000001;

export const QUANTCAST_CALLBACK_URL = 'global.qc.rtb.quantserve.com';
export const QUANTCAST_CALLBACK_URL_TEST = 's2s-canary.quantserve.com';
export const QUANTCAST_NET_REVENUE = true;
export const QUANTCAST_TEST_PUBLISHER = 'test-publisher';
export const QUANTCAST_TTL = 4;

/**
* The documentation for Prebid.js Adapter 1.0 can be found at link below,
* http://prebid.org/dev-docs/bidder-adapter-1.html
*/
export const spec = {
code: BIDDER_CODE,

/**
* Verify the `AdUnits.bids` response with `true` for valid request and `false`
* for invalid request.
*
* @param {object} bid
* @return boolean `true` is this is a valid bid, and `false` otherwise
*/
isBidRequestValid(bid) {
if (!bid) {
return false;
}
};

// expose the callback to the global object:
$$PREBID_GLOBAL$$.handleQuantcastCB = function (responseText) {
if (utils.isEmpty(responseText)) {
return;
}
let response = null;
try {
response = JSON.parse(responseText);
} catch (e) {
// Malformed JSON
utils.logError("Malformed JSON received from server - can't do anything here");
return;
}

if (response === null || !response.hasOwnProperty('bids') || utils.isEmpty(response.bids)) {
utils.logError("Sub-optimal JSON received from server - can't do anything here");
return;
if (bid.mediaType === 'video') {
return false;
}

for (let i = 0; i < response.bids.length; i++) {
let seatbid = response.bids[i];
let key = seatbid.placementCode;
var request = bidRequests[key];
if (request === null || request === undefined) {
return returnEmptyBid(seatbid.placementCode);
}
// This line is required since this is the field
// that bidfactory.createBid looks for
request.bidId = request.imp[0].placementCode;
let responseBid = bidfactory.createBid(CONSTANTS.STATUS.GOOD, request);

responseBid.cpm = seatbid.cpm;
responseBid.ad = seatbid.ad;
responseBid.height = seatbid.height;
responseBid.width = seatbid.width;
responseBid.bidderCode = response.bidderCode;
responseBid.requestId = request.requestId;
responseBid.bidderCode = BIDDER_CODE;

bidmanager.addBidResponse(request.bidId, responseBid);
return true;
},

/**
* Make a server request when the page asks Prebid.js for bids from a list of
* `BidRequests`.
*
* @param {BidRequest[]} bidRequests A non-empty list of bid requests which should be send to Quantcast server
* @return ServerRequest information describing the request to the server.
*/
buildRequests(bidRequests) {
const bids = bidRequests || [];

const referrer = utils.getTopWindowUrl();
const loc = utils.getTopWindowLocation();
const domain = loc.hostname;

let publisherTagURL;
let publisherTagURLTest;

// Switch the callback URL to Quantcast Canary Endpoint for testing purpose
// `//` is not used because we have different port setting at our end
switch (window.location.protocol) {
case 'https:':
publisherTagURL = `https://${QUANTCAST_CALLBACK_URL}:8443/qchb`;
publisherTagURLTest = `https://${QUANTCAST_CALLBACK_URL_TEST}:8443/qchb`;
break;
default:
publisherTagURL = `http://${QUANTCAST_CALLBACK_URL}:8080/qchb`;
publisherTagURLTest = `http://${QUANTCAST_CALLBACK_URL_TEST}:8080/qchb`;
}
};

function callBids(params) {
let bids = params.bids || [];
if (bids.length === 0) {
return;
}

let referrer = utils.getTopWindowUrl();
let loc = utils.getTopWindowLocation();
let domain = loc.hostname;
let publisherId = 0;
const bidRequestsList = bids.map(bid => {
const bidSizes = [];

publisherId = '' + bids[0].params.publisherId;
utils._each(bids, function(bid) {
let key = bid.placementCode;
var bidSizes = [];
utils._each(bid.sizes, function (size) {
bid.sizes.forEach(size => {
bidSizes.push({
'width': size[0],
'height': size[1]
width: size[0],
height: size[1]
});
});

bidRequests[key] = bidRequests[key] || {
'publisherId': publisherId,
'requestId': bid.bidId,
'bidId': bid.bidId,
'site': {
'page': loc.href,
'referrer': referrer,
'domain': domain,
// Request Data Format can be found at https://wiki.corp.qc/display/adinf/QCX
const requestData = {
publisherId: bid.params.publisherId,
requestId: bid.bidId,
imp: [
{
banner: {
battr: bid.params.battr,
sizes: bidSizes
},
placementCode: bid.placementCode,
bidFloor: bid.params.bidFloor || DEFAULT_BID_FLOOR
}
],
site: {
page: loc.href,
referrer,
domain
},
'imp': [{

'banner': {
'battr': bid.params.battr,
'sizes': bidSizes,
},
'placementCode': bid.placementCode,
'bidFloor': bid.params.bidFloor || DEFAULT_BID_FLOOR,
}]
bidId: bid.bidId
};
});

utils._each(bidRequests, function (bidRequest) {
ajax.ajax(QUANTCAST_CALLBACK_URL, $$PREBID_GLOBAL$$.handleQuantcastCB, JSON.stringify(bidRequest), {
const data = JSON.stringify(requestData);

const url =
bid.params.publisherId === QUANTCAST_TEST_PUBLISHER
? publisherTagURLTest
: publisherTagURL;

return {
data,
method: 'POST',
withCredentials: true
});
url
};
});
}

// Export the `callBids` function, so that Prebid.js can execute
// this function when the page asks to send out bid requests.
return {
callBids: callBids,
QUANTCAST_CALLBACK_URL: QUANTCAST_CALLBACK_URL
};
};
return bidRequestsList;
},

/**
* Function get called when the browser has received the response from Quantcast server.
* The function parse the response and create a `bidResponse` object containing one/more bids.
* Returns an empty array if no valid bids
*
* Response Data Format can be found at https://wiki.corp.qc/display/adinf/QCX
*
* @param {*} serverResponse A successful response from Quantcast server.
* @return {Bid[]} An array of bids which were nested inside the server.
*
*/
interpretResponse(serverResponse) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

#1742 got merged recently.

The first argument to interpretResponse now looks like this:

{
body: responseBody,
headers: {
get: function(header) { /* returns a header from the HTTP response */ }
}
}
You'll have to pull master and update the spec so that it looks digs into that object as well now.

Copy link
Author

Choose a reason for hiding this comment

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

Updated with bee2344 and 417852e

if (serverResponse === undefined) {
utils.logError('Server Response is undefined');
return [];
}

const response = serverResponse['body'];

if (
response === undefined ||
!response.hasOwnProperty('bids') ||
utils.isEmpty(response.bids)
) {
utils.logError('Sub-optimal JSON received from Quantcast server');
return [];
}

adaptermanager.registerBidAdapter(new QuantcastAdapter(), 'quantcast');
const bidResponsesList = response.bids.map(bid => {
const { ad, cpm, width, height, creativeId, currency } = bid;

return {
requestId: response.requestId,
cpm,
width,
height,
ad,
ttl: QUANTCAST_TTL,
creativeId,
netRevenue: QUANTCAST_NET_REVENUE,
currency
};
});

return bidResponsesList;
}
};

module.exports = QuantcastAdapter;
registerBidder(spec);
31 changes: 31 additions & 0 deletions modules/quantcastBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Overview

```
Module Name: Quantcast Bidder Adapter
Module Type: Bidder Adapter
Maintainer: xli@quantcast.com
```

# Description

Module that connects to Quantcast demand sources to fetch bids.

# Test Parameters

```js
const adUnits = [{
code: 'banner',
sizes: [
[300, 250]
],
bids: [
{
bidder: 'quantcast',
params: {
publisherId: 'test-publisher', // REQUIRED - Publisher ID provided by Quantcast
battr: [1, 2] // OPTIONAL - Array of blocked creative attributes as per OpenRTB Spec List 5.3
}
}
]
}];
```
Loading