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

Feature/widespace adapter #846

Merged
merged 10 commits into from
Dec 16, 2016
1 change: 1 addition & 0 deletions adapters.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"centro",
"roxot",
"vertoz",
"widespace",
{
"appnexus": {
"alias": "brealtime"
Expand Down
9 changes: 8 additions & 1 deletion integrationExamples/gpt/pbjs_example_gpt.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
var adUnits = [
{
code: 'div-gpt-ad-12345678-0',
sizes: [[300, 250], [300, 600]],
sizes: [[300, 250], [300, 300], [300, 600]],
bids: [
// 1 ad unit can be targeted by multiple bids.
{
Expand Down Expand Up @@ -224,6 +224,13 @@
zoneId : '30164',
host : 'cpm.metaadserving.com'
}
},
{
bidder: 'widespace',
params: {
sid: '7b6589bf-95c8-4656-90b9-af9737bb9ad3',
cur: 'EUR'
}
}
]
}, {
Expand Down
103 changes: 103 additions & 0 deletions src/adapters/widespace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@

import { getBidRequest } from '../utils.js';

var utils = require('../utils.js');
var adloader = require('../adloader.js');
var bidmanager = require('../bidmanager.js');
var bidfactory = require('../bidfactory.js');


function WidespaceAdapter() {
let useSSL = 'https:' === document.location.protocol,
baseURL = (useSSL ? 'https:' : 'http:') + '//engine.widespace.com/map/engine/hb/dynamic?',
callbackName = '$$PREBID_GLOBAL$$.widespaceHandleCB';

function _callBids(params) {
let bids = params && params.bids || [];

for (var i = 0; i < bids.length; i++) {
const bid = bids[i],
callbackUid = bid.bidId,
sid = bid.params.sid,
currency = bid.params.currency;

//Handle Sizes string
let sizeQueryString = '';
let parsedSizes = utils.parseSizesInput(bid.sizes);

sizeQueryString = parsedSizes.reduce((prev, curr) => {
return prev ? `${prev},${curr}` : curr;
}, sizeQueryString);

var requestURL = baseURL;
requestURL = utils.tryAppendQueryString(requestURL, 'hb.name', 'prebidjs');
requestURL = utils.tryAppendQueryString(requestURL, 'hb.callback', callbackName);
requestURL = utils.tryAppendQueryString(requestURL, 'hb.callbackUid', callbackUid);
requestURL = utils.tryAppendQueryString(requestURL, 'hb.sizes', sizeQueryString);
requestURL = utils.tryAppendQueryString(requestURL, 'sid', sid);
requestURL = utils.tryAppendQueryString(requestURL, 'hb.currency', currency);


// Expose the callback
$$PREBID_GLOBAL$$.widespaceHandleCB = window[callbackName] = handleCallback;

adloader.loadScript(requestURL);
}
}

//Handle our callback
var handleCallback = function handleCallback(bidsArray) {
if (!bidsArray) { return; }

var bidObject,
bidCode = 'widespace';

for (var i = 0, l = bidsArray.length; i < l; i++) {
var bid = bidsArray[i],
placementCode = '',
validSizes = [];

bid.sizes = {height: bid.height, width: bid.height};

var inBid = getBidRequest(bid.callbackUid);

if (inBid) {
bidCode = inBid.bidder;
placementCode = inBid.placementCode;
validSizes = inBid.sizes;
}
if (bid && bid.callbackUid && bid.status !=='noad' && verifySize(bid.sizes, validSizes)) {
bidObject = bidfactory.createBid(1);
bidObject.bidderCode = bidCode;
bidObject.cpm = bid.cpm;
bidObject.cur = bid.currency;
bidObject.creative_id = bid.adId;
bidObject.ad = bid.code;
bidObject.width = bid.width;
bidObject.height = bid.height;
bidmanager.addBidResponse(placementCode, bidObject);
} else {
bidObject = bidfactory.createBid(2);
bidObject.bidderCode = bidCode;
bidmanager.addBidResponse(placementCode, bidObject);
}
}


function verifySize(bid, validSizes) {
for (var j = 0, k = validSizes.length; j < k; j++) {
if (bid.width === validSizes[j][0] &&
bid.height === validSizes[j][1]) {
return true;
}
}
return false;
}
};

return {
callBids: _callBids
};
}

module.exports = WidespaceAdapter;
192 changes: 192 additions & 0 deletions test/spec/adapters/widespace_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { expect } from 'chai';
import adLoader from '../../../src/adloader';
import bidManager from '../../../src/bidmanager';
import Adapter from '../../../src/adapters/widespace';


const ENDPOINT = '//engine.widespace.com/map/engine/hb/dynamic';

const TEST = {
BIDDER_CODE: 'widespace',
CPM: 2.0,
PLACEMENT_CODE: 'aPlacementCode',
SID: 'f666bfaf-69cf-4ed9-9262-08247bb274e4',
CUR: 'EUR'
};

const BID_REQUEST = {
"bidderCode": TEST.BIDDER_CODE,
"requestId": "e155185b-3eac-4f3c-8182-cdb57a69df3c",
"bidderRequestId": "38993e482321e7",
"bids": [
{
"bidder": TEST.BIDDER_CODE,
"params": {
"sid": TEST.SID,
"cur": TEST.CUR
},
"placementCode": TEST.PLACEMENT_CODE,
"sizes": [
[320, 320],
[320, 250]
],
"bidId": "45c7f5afb996c1",
"bidderRequestId": "7101db09af0db3",
"requestId": "e155185b-3eac-4f3c-8182-cdb57a69df3d"
}
],
"start": 1479664180396,
"timeout": 5000
};


const BID_RESPONSE = [{
"status": "ok",
"reqId": "140590112507",
"adId": 13963,
"width": 320,
"height": 320,
"cpm": 2.0,
"currency": "EUR",
"code": "<p>This is a banner</p>",
"callbackUid": "45c7f5afb996c1",
"callback": "pbjs.widespaceHandleCB"
}];

const BID_NOAD_RESPONSE = [{
"status": "noad",
"reqId": "143509454349",
"adId": 22,
"width": 1,
"height": 1,
"cpm": 0.0,
"currency": "EUR",
"code": "",
"callbackUid": "45c7f5afb996c1",
"callback": "pbjs.widespaceHandleCB"
}]


describe('WidespaceAdapter', () => {

let adapter;
let sandbox;

beforeEach(() => {
adapter = new Adapter();
sandbox = sinon.sandbox.create();
});

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


describe('callBids', () => {
it('should exists and be a function', () => {
expect(adapter.callBids).to.exist.and.to.be.a('function');
});


describe('with valid request parameters', () => {
beforeEach(() => {
sandbox.stub(adLoader, 'loadScript');
adapter.callBids(BID_REQUEST);
});

it('should call the endpoint once per valid bid', () => {
sinon.assert.callCount(adLoader.loadScript, 1);
});

it('should include required request parameters', () => {
const endpointRequest = expect(adLoader.loadScript.firstCall.args[0]);
endpointRequest.to.include('sid');
endpointRequest.to.include('hb.callbackUid');
endpointRequest.to.include('hb.callback');
endpointRequest.to.include('hb.sizes');
endpointRequest.to.include('hb.name');
});
});

describe('with unvalid request parameters', () => {
beforeEach(() => {
sandbox.stub(adLoader, 'loadScript');
});

it('should not call the endpoint with if there is no request parameters', () => {
adapter.callBids({});
sinon.assert.callCount(adLoader.loadScript, 0);
});
});
});


describe('widespaceHandleCB', () => {
it('should exist and be a function', () => {
expect(pbjs.widespaceHandleCB).to.exist.and.to.be.a('function');
});
});

describe('respond with a successful bid', () => {
let successfulBid,
placementCode;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This variable is never used, please remove if you don't need it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed, thank you


beforeEach(() => {
sandbox.stub(bidManager, 'addBidResponse');
sandbox.stub(adLoader, 'loadScript');

adapter.callBids(BID_REQUEST);
pbjs._bidsRequested.push(BID_REQUEST);
pbjs.widespaceHandleCB(BID_RESPONSE);

successfulBid = bidManager.addBidResponse.firstCall.args[1];
placementCode = bidManager.addBidResponse.firstCall.args[0];

});

it('should add one bid', () => {
sinon.assert.calledOnce(bidManager.addBidResponse);
});

it('should use the CPM returned by the server', () => {
expect(successfulBid).to.have.property('cpm', TEST.CPM);
});

it('should have an OK statusCode', () => {
expect(successfulBid.getStatusCode()).to.eql(1);
});

it('should have a valid size', () => {
const bidSize = [successfulBid.width,successfulBid.height]
expect(bidSize).to.eql(BID_REQUEST.bids[0].sizes[0]);

});

it('should recive right placementCode', () => {
expect(placementCode).to.eql(TEST.PLACEMENT_CODE);
});
});


describe('respond with a no-ad', () => {
let noadBid;

beforeEach(() => {
sandbox.stub(bidManager, 'addBidResponse');
sandbox.stub(adLoader, 'loadScript');

adapter.callBids(BID_REQUEST);
pbjs._bidsRequested.push(BID_REQUEST);
pbjs.widespaceHandleCB(BID_NOAD_RESPONSE);

noadBid = bidManager.addBidResponse.firstCall.args[1];
});

it('should have an error statusCode', () => {
expect(noadBid.getStatusCode()).to.eql(2);
});
});



});