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

Quantcast adaptor #1063

Merged
merged 39 commits into from
May 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2fca458
Initial code
ShreeniwasIyer Mar 8, 2017
8b47e0f
First cut of the adapter
ShreeniwasIyer Mar 9, 2017
470e5c1
Merge branch 'master' of https://github.com/prebid/Prebid.js into qcx…
ShreeniwasIyer Mar 9, 2017
5292542
Renaming for consistency
ShreeniwasIyer Mar 9, 2017
456ed26
Indendation and syntax changes
ShreeniwasIyer Mar 10, 2017
58b294a
request processing has been completed
ShreeniwasIyer Mar 10, 2017
bb6a711
Merge branch 'master' of github.corp.qc:qc/INVENTORY-prebid.js into q…
ShreeniwasIyer Mar 10, 2017
584d8f0
Further testing
ShreeniwasIyer Mar 13, 2017
6459e39
Merge branch 'master' of github.corp.qc:qc/INVENTORY-prebid.js into q…
ShreeniwasIyer Mar 13, 2017
70edd91
Switched to using Placement Code
ShreeniwasIyer Mar 13, 2017
881fb06
Renamed the fields of request and response to be more consistent with…
ShreeniwasIyer Mar 14, 2017
b79c82b
Merge branch 'master' of github.corp.qc:qc/INVENTORY-prebid.js into q…
ShreeniwasIyer Mar 14, 2017
2c5f386
Added Unit tests
ShreeniwasIyer Mar 15, 2017
3534d42
Merging with Public master
ShreeniwasIyer Mar 22, 2017
1ea9bce
Changed the URL
ShreeniwasIyer Mar 22, 2017
d05db9e
Fixed nit:spacing issues
ShreeniwasIyer Mar 22, 2017
1a0e9a6
Added fix for the case when request is not found for whatever reason …
ShreeniwasIyer Mar 22, 2017
a7f11c9
nit being resolved
ShreeniwasIyer Mar 22, 2017
4c2c8c7
Addressing comments
ShreeniwasIyer Mar 22, 2017
22b6741
Fixed the indentation as per jshint errors on travis-ci build
ShreeniwasIyer Mar 22, 2017
35e6574
Merge branch 'master' of github.com:prebid/Prebid.js into qcx_adaptor
ShreeniwasIyer Mar 24, 2017
6885f2a
Replacing qcx with Quantcast and changing URL
ShreeniwasIyer Mar 24, 2017
43b41fa
removed use of internal constants
ShreeniwasIyer Apr 20, 2017
9be543b
Use getBidRequest
ShreeniwasIyer Apr 20, 2017
b87ab34
Pass bidRequest as second param
ShreeniwasIyer Apr 20, 2017
4395bf3
Wrapping the JSON parse in try/catch
ShreeniwasIyer Apr 20, 2017
bbb5f16
Replacing pbjs use to 681PREBID_GLOBAL681
ShreeniwasIyer Apr 20, 2017
5049ef1
Fixing JSHint errors
ShreeniwasIyer Apr 20, 2017
0d92aed
Replacing array wrapping with parseSizesInput usage to make it cleaner
ShreeniwasIyer Apr 20, 2017
8442e44
Fixed the error, not caught in gulp test
ShreeniwasIyer Apr 20, 2017
a36db04
No need to compare to undefined since as per new code, we only need t…
ShreeniwasIyer Apr 20, 2017
2b28a4e
Fixed the warning
ShreeniwasIyer Apr 20, 2017
c8a401c
integration testing and adding quantcast to the demo html page
ShreeniwasIyer Apr 21, 2017
ce6118d
Merge branch 'master' into qcx_adaptor
ShreeniwasIyer Apr 21, 2017
35f328a
Converting sizes into an object and removing the single size object
ShreeniwasIyer Apr 27, 2017
64bbbdc
Merge branch 'qcx_adaptor' of github.com:ShreeniwasIyer/Prebid.js int…
ShreeniwasIyer Apr 27, 2017
f8f0baa
Added withCredentials variable to AJAX calls
ShreeniwasIyer Apr 28, 2017
fae67ac
using placement code as the key
ShreeniwasIyer Apr 28, 2017
8e3a19a
Adding the bidder code and request id to responseBid
ShreeniwasIyer Apr 28, 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
1 change: 1 addition & 0 deletions adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"pubmatic",
"pulsepoint",
"pulsepointLite",
"quantcast",
"rhythmone",
"rubicon",
"smartyads",
Expand Down
14 changes: 14 additions & 0 deletions integrationExamples/gpt/pbjs_example_gpt.html
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@
zone: '2eb6bd58-865c-47ce-af7f-a918108c3fd2' // REQUIRED zone oid
}
},
{
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
}
},
{
bidder: 'atomx',
params: {
Expand Down Expand Up @@ -363,6 +370,13 @@
pId: 123456 // REQUIRED Placement Id
}
},
{
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
}
},
{
bidder: 'huddledmasses',
params: {
Expand Down
135 changes: 135 additions & 0 deletions src/adapters/quantcast.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const utils = require('../utils.js');
const bidfactory = require('../bidfactory.js');
const bidmanager = require('../bidmanager.js');
const ajax = require('../ajax.js');
const CONSTANTS = require('../constants.json');
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);
}
return;
};


//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;
Copy link
Member

Choose a reason for hiding this comment

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

Should really try to add a error bid here if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mkendall07 I tried to, but at that point in code we don't have the placement code for which the empty text response came and hence returning an error bid isn't possible..

}

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

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);
}

};

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;

publisherId = '' + bids[0].params.publisherId;
utils._each(bids, function(bid) {
let key = bid.placementCode;
var bidSizes = [];
utils._each(bid.sizes, function (size) {
bidSizes.push({
'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,
},
'imp' : [{

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

utils._each(bidRequests, function (bidRequest) {
ajax.ajax(QUANTCAST_CALLBACK_URL, $$PREBID_GLOBAL$$.handleQuantcastCB, JSON.stringify(bidRequest), {
method : 'POST',
withCredentials: true
});
});
});
}


// 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
};
};

exports.createNew = function() {
return new QuantcastAdapter();
};


module.exports = QuantcastAdapter;
194 changes: 194 additions & 0 deletions test/spec/adapters/quantcast_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import {expect} from 'chai';
import Adapter from '../../../src/adapters/quantcast';
import * as ajax from 'src/ajax';
import bidManager from '../../../src/bidmanager';
import adLoader from '../../../src/adloader';

describe('quantcast adapter', () => {

let bidsRequestedOriginal;
let adapter;
let sandbox;
let ajaxStub;

const bidderRequest = {
bidderCode: 'quantcast',
requestId : "595ffa73-d78a-46c9-b18e-f99548a5be6b",
bidderRequestId:"1cc026909c24c8",
bids: [
{
bidId: '2f7b179d443f14',
bidder: 'quantcast',
placementCode: 'div-gpt-ad-1438287399331-0',
sizes: [[300,250],[300,600]],
params: {
publisherId: 'test-publisher',
battr : [1,2],
}
}
]
};

beforeEach(() => {
bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested;
$$PREBID_GLOBAL$$._bidsRequested = [];

adapter = new Adapter();
sandbox = sinon.sandbox.create();
ajaxStub = sandbox.stub(ajax, 'ajax');
});

afterEach(() => {
sandbox.restore();

$$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal;
});

describe('sizes', () => {
let bidderRequest = {
bidderCode: 'quantcast',
requestId : "595ffa73-d78a-46c9-b18e-f99548a5be6b",
bidderRequestId:"1cc026909c24c8",
bids: [
{
bidId: '2f7b179d443f14',
bidder: 'quantcast',
placementCode: 'div-gpt-ad-1438287399331-0',
sizes: [[300,250],[300,600]],
params: {
publisherId: 'test-publisher',
battr : [1,2],
}
}
]
};

it('should not call server when empty input is provided', () => {
adapter.callBids({});
sinon.assert.notCalled(ajaxStub);
});

it('should call server once even when multiple sizes are passed', () => {
adapter.callBids(bidderRequest);
sinon.assert.calledOnce(ajaxStub);

expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL);
expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function');
expect(ajaxStub.firstCall.args[2]).to.include('div-gpt-ad-1438287399331-0');
expect(ajaxStub.firstCall.args[2]).to.include('test-publisher');
expect(ajaxStub.firstCall.args[2]).to.include('2f7b179d443f14');
expect(ajaxStub.firstCall.args[3]).to.eql({method : 'POST', withCredentials: true});
});

it('should call server once when one size is passed', () => {
bidderRequest.bids[0].sizes = [728, 90];
adapter.callBids(bidderRequest);
sinon.assert.calledOnce(ajaxStub);

expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL);
expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function');
expect(ajaxStub.firstCall.args[3]).to.eql({method : 'POST', withCredentials: true});
});

it('should call server once when size is passed as string', () => {
bidderRequest.bids[0].sizes = "728x90";
adapter.callBids(bidderRequest);
sinon.assert.calledOnce(ajaxStub);

expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL);
expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function');
expect(ajaxStub.firstCall.args[3]).to.eql({method : 'POST', withCredentials: true});
});

it('should call server once when sizes are passed as a comma-separated string', () => {
bidderRequest.bids[0].sizes = "728x90,360x240";
adapter.callBids(bidderRequest);
sinon.assert.calledOnce(ajaxStub);

expect(ajaxStub.firstCall.args[0]).to.eql(adapter.QUANTCAST_CALLBACK_URL);
expect(ajaxStub.firstCall.args[1]).to.exist.and.to.be.a('function');
expect(ajaxStub.firstCall.args[3]).to.eql({method : 'POST', withCredentials: true});
});


});

describe('handleQuantcastCB add bids to the manager', () => {

let firstBid;
let addBidReponseStub;
let bidsRequestedOriginal;
// respond
let bidderReponse = {
"bidderCode": "quantcast",
"requestId" : bidderRequest.requestId,
"bids" : [
{
"statusCode" : 1,
"placementCode" : bidderRequest.bids[0].bidId,
"cpm": 4.5,
"ad": "<!DOCTYPE html>\n\n\n<div style=\"height: 250; width: 300; display: table-cell; vertical-align: middle;\">\n<div style=\"width: 300px; margin-left: auto; margin-right: auto;\"> \n\n <script src=\"https://adserver.adtechus.com/addyn/3.0/5399.1/2394397/0/-1/QUANTCAST;size=300x250;target=_blank;alias=;kvp36=;sub1=;kvl=;kvc=;kvs=300x250;kvi=;kva=;sub2=;rdclick=http://exch.quantserve.com/r?a=;labels=_qc.clk,_click.adserver.rtb,_click.rand.;rtbip=;rtbdata2=;redirecturl2=\" type=\"text/javascript\"></script>\n\n<img src=\"https://exch.quantserve.com/pixel/p_12345.gif?media=ad&p=&r=&rand=&labels=_qc.imp,_imp.adserver.rtb&rtbip=&rtbdata2=\" style=\"display: none;\" border=\"0\" height=\"1\" width=\"1\" alt=\"Quantcast\"/>\n\n</div>\n</div>",
"width": 300,
"height": 250
}
]
};

beforeEach(() => {
bidsRequestedOriginal = $$PREBID_GLOBAL$$._bidsRequested;
addBidReponseStub = sandbox.stub(bidManager, 'addBidResponse');
$$PREBID_GLOBAL$$._bidsRequested.push(bidderRequest);
});

afterEach(() => {
sandbox.restore();
$$PREBID_GLOBAL$$._bidsRequested = bidsRequestedOriginal;
});

it('should exist and be a function', () => {
expect($$PREBID_GLOBAL$$.handleQuantcastCB).to.exist.and.to.be.a('function');
});

it('should not add bid when empty text response comes', () => {
$$PREBID_GLOBAL$$.handleQuantcastCB();
sinon.assert.notCalled(addBidReponseStub);
});

it('should not add bid when empty json response comes', () => {
$$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify({}));
sinon.assert.notCalled(addBidReponseStub);
});

it('should not add bid when malformed json response comes', () => {
$$PREBID_GLOBAL$$.handleQuantcastCB('non json text');
sinon.assert.notCalled(addBidReponseStub);
});

it('should add a bid object for each bid', () => {
// You need the following call so that the in-memory storage of the bidRequest is carried out. Without this the callback won't work correctly.
adapter.callBids(bidderRequest);
$$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify(bidderReponse));
sinon.assert.calledOnce(addBidReponseStub);
expect(addBidReponseStub.firstCall.args[0]).to.eql("div-gpt-ad-1438287399331-0");
});

it('should return no bid even when requestId and sizes are missing', () =>{
let bidderReponse = {
"bidderCode": "quantcast",
"bids" : [
{
"statusCode" : 0,
"placementCode" : bidderRequest.bids[0].bidId,
}
]
};

// You need the following call so that the in-memory storage of the bidRequest is carried out. Without this the callback won't work correctly.
adapter.callBids(bidderRequest);
$$PREBID_GLOBAL$$.handleQuantcastCB(JSON.stringify(bidderReponse));
//sinon.assert.calledOnce(addBidReponseStub);
//expect(addBidReponseStub.firstCall.args[0]).to.eql("div-gpt-ad-1438287399331-0");
});
});

});