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

[Internal Code Review] C1X adapter for Prebid v1.0 #3

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 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
192 changes: 192 additions & 0 deletions integrationExamples/gpt/c1x-prebid-cathytest.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<!DOCTYPE html>
<html>
<head>
<title>Prebid.js integration example</title>
<script>
var PREBID_TIMEOUT = 8000;
var MAX_RETRIES = 20;

var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
googletag.cmd.push(function () {
googletag.pubads().disableInitialLoad();
});

var pbjs = pbjs || {};
pbjs.cmd = pbjs.cmd || [];
pbjs.retries = 0;

/* pbjs.initAdserver will be called either when all bids are back, or
when the timeout is reached.
*/
function initAdserver() {
if (pbjs.initAdserverSet) return;
if(!googletag.pubadsReady && pbjs.retries <= MAX_RETRIES) {
setTimeout(initAdserver, 500); //poll ms can be adjusted as desired.
pbjs.retries++;
return;
}
googletag.cmd.push(function () {
pbjs.cmd.push(function () {
pbjs.setTargetingForGPTAsync();
});
googletag.pubads().refresh();
});
pbjs.initAdserverSet = true;
}

// Load GPT when timeout is reached.
setTimeout(initAdserver, PREBID_TIMEOUT);

// Load the Prebid Javascript Library Async. We recommend loading it immediately after the initAdserver() and setTimeout functions.
(function() {
var d = document, pbs = d.createElement('script'), pro = d.location.protocol;
pbs.type = 'text/javascript';
pbs.src = '../../build/dev/prebid.js';
var target = document.getElementsByTagName('head')[0];
target.insertBefore(pbs, target.firstChild);
})();

//load GPT library here
(function () {
var gads = document.createElement('script');
gads.async = true;
gads.type = 'text/javascript';
var useSSL = 'https:' == document.location.protocol;
gads.src = (useSSL ? 'https:' : 'http:') +
'//www.googletagservices.com/tag/js/gpt.js';
var node = document.getElementsByTagName('script')[0];
node.parentNode.insertBefore(gads, node);
})();

pbjs.cmd.push(function () {

/* 1. Register bidder tag Ids

Registers the bidder tags for your ad units. Once the prebid.js
library loads, it reads the pbjs.adUnits object and sends out
bid requests.

code: Your GPT slot’s ad unit path. If they don’t match, prebid.js
would not be able to set targeting correctly
sizes: All sizes your ad unit accepts. They should match with GPT.
*/
var adUnits = [
{
code: 'div-gpt-ad-1498193081995-0',
sizes: [[300, 600], [300, 250]],
bids: [
// 1 ad unit can be targeted by multiple bids.
{
bidder: 'c1x',
params: {
siteId: '9999',
pixelId: '12345',
floorPriceMap: {
'300x250': 0.20,
'300x600': 0.30
}, //optional
}
}
]
},
{
code: 'div-gpt-ad-test',
sizes: [[300, 600]],
bids: [
// 1 ad unit can be targeted by multiple bids.
{
bidder: 'c1x',
params: {
siteId: '9999',
placementID: "234234",
floorPriceMap: {
'300x600': 0.40
}
}
}
]
},
];

//add the adUnits
pbjs.addAdUnits(adUnits);

//register a callback handler
pbjs.onEvent('bidResponse', function (bidResponse) {
console.log('A bid response has arrived:');
});

/* Configure Ad Server Targeting
The below section defines what key value targeting will be sent to GPT.
*/
pbjs.bidderSettings = {
standard: {
adserverTargeting: [
{
key: "hb_bidder",
val: function (bidResponse) {
return bidResponse.bidderCode;
}
}, {
key: "hb_adid",
val: function (bidResponse) {
return bidResponse.adId;
}
}, {
key: "hb_pb",
val: function (bidResponse) {
return bidResponse.pbMg;
}
}, {
key: 'hb_size',
val: function (bidResponse) {
return bidResponse.size;
}
}
]
}
};


/* Request bids for the added ad units. If adUnits or adUnitCodes are
not specified, the function will request bids for all added ad units.
*/
pbjs.requestBids({

/* The bidsBack function will be called when either timeout is
reached, or when all bids come back, whichever happens sooner.
*/
bidsBackHandler: function (bidResponses) {
// c1x.logs.push([new Date().getTime(), 'Bids back handler called.']);
console.log('all the bid responses are back');
console.log(bidResponses);
initAdserver();
}
});

});

googletag.cmd.push(function() {
googletag.defineSlot('/188708772/cathy-test-unit', [[300, 250], [300, 600]], 'div-gpt-ad-1498193081995-0').addService(googletag.pubads());
googletag.pubads().enableSingleRequest();
googletag.enableServices();
});

</script>

</head>
<body>

<h2>Prebid.js Test</h2>

<!-- /188708772/cathy-test-unit -->
<div id='div-gpt-ad-1498193081995-0'>
<script type='text/javascript'>
googletag.cmd.push(function() { googletag.display('div-gpt-ad-1498193081995-0'); });
</script>
</div>

</body>

</html>
165 changes: 165 additions & 0 deletions modules/c1xBidAdapter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// import CONSTANTS from 'src/constants.json';
import { registerBidder } from 'src/adapters/bidderFactory';
import * as utils from 'src/utils';
import { userSync } from 'src/userSync';

const BIDDER_CODE = 'c1x';
const URL = 'http://13.58.47.152:8080/ht';
Copy link
Owner

Choose a reason for hiding this comment

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

Latest IP: 18.217.214.190

const PIXEL_ENDPOINT = '//px.c1exchange.com/pubpixel/';
const LOG_MSG = {
invalidBid: 'C1X: [ERROR] bidder returns an invalid bid',
noSite: 'C1X: [ERROR] no site id supplied',
noBid: 'C1X: [INFO] creating a NO bid for Adunit: ',
bidWin: 'C1X: [INFO] creating a bid for Adunit: '
};

/**
* Adapter for requesting bids from C1X header tag server.
* v3.0 (c) C1X Inc., 2017
*/

export const c1xAdapter = {
code: BIDDER_CODE,

// check the bids sent to c1x bidder
isBidRequestValid: function(bid) {
const siteId = bid.params.siteId || '';
if (!siteId) {
utils.logError(LOG_MSG.noSite);
}
return !!(bid.adUnitCode && siteId);
},

buildRequests: function(bidRequests) {
let payload = {};
let tagObj = {};
const adunits = bidRequests.length;
const rnd = new Date().getTime();
const c1xTags = bidRequests.map(bidToTag);
Copy link
Collaborator

Choose a reason for hiding this comment

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

bidToTag is defined?

const bidIdTags = bidRequests.map(bidToShortTag); // include only adUnitCode and bidId from request obj

// flattened tags in a tag object
tagObj = c1xTags.reduce((current, next) => Object.assign(current, next));
const pixelId = tagObj.pixelId;
const useSSL = document.location.protocol;

payload = {
adunits: adunits.toString(),
rnd: rnd.toString(),
response: 'json',
compress: 'gzip'
}
Object.assign(payload, tagObj);

let payloadString = stringifyPayload(payload);

if (pixelId) {
const pixelUrl = (useSSL ? 'https:' : 'http:') + PIXEL_ENDPOINT + pixelId;
userSync.registerSync('image', BIDDER_CODE, pixelUrl);
}

// ServerRequest object
return {
method: 'GET',
url: URL,
data: payloadString,
bids: bidIdTags
};
},

interpretResponse: function(serverResponse, requests) {
serverResponse = serverResponse.body;
requests = requests.bids || [];
const currency = 'USD';
const bidResponses = [];

if (!serverResponse || serverResponse.error) {
let errorMessage = serverResponse.error;
utils.logError(LOG_MSG.invalidBid + errorMessage);
return bidResponses;
} else {
console.log(serverResponse);
serverResponse.forEach(bid => {
if (bid.bid) {
const curBid = {
width: bid.width,
height: bid.height,
cpm: bid.cpm,
ad: bid.ad,
creativeId: bid.crid,
currency: currency,
ttl: 300,
netRevenue: false, // net or gross?
Copy link
Collaborator

Choose a reason for hiding this comment

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

Net / Gross both are same for Prebid.

};

for (let i = 0; i < requests.length; i++) {
if (bid.adId === requests[i].adUnitCode) {
curBid.requestId = requests[i].bidId;
}
}
utils.logInfo(LOG_MSG.bidWin + bid.adId + ' size: ' + curBid.width + 'x' + curBid.height);
bidResponses.push(curBid);
} else {
// no bid
utils.logInfo(LOG_MSG.noBid + bid.adId);
}
});
}

return bidResponses;
}
}

function bidToTag(bid, index) {
const tag = {};
const adIndex = 'a' + (index + 1).toString(); // ad unit id for c1x
const sizeKey = adIndex + 's';
const priceKey = adIndex + 'p';
// TODO: Multiple Floor Prices

const sizesArr = bid.sizes;
const floorPriceMap = bid.params.floorPriceMap || '';
tag['site'] = bid.params.siteId || '';

// prevent pixelId becoming undefined when publishers don't fill this param in ad units they have on the same page
if (bid.params.pixelId) {
tag['pixelId'] = bid.params.pixelId
}

tag[adIndex] = bid.adUnitCode;
tag[sizeKey] = sizesArr.reduce((prev, current) => prev + (prev === '' ? '' : ',') + current.join('x'), '');

const newSizeArr = tag[sizeKey].split(',');
if (floorPriceMap) {
newSizeArr.forEach(size => {
if (size in floorPriceMap) {
tag[priceKey] = floorPriceMap[size].toString();
} // we only accept one cpm price in floorPriceMap
});
}
if (bid.params.pageurl) {
tag['pageurl'] = bid.params.pageurl;
}

return tag;
}

function bidToShortTag(bid) {
const tag = {};
tag.adUnitCode = bid.adUnitCode;
tag.bidId = bid.bidId;

return tag;
}

function stringifyPayload(payload) {
let payloadString = '';
payloadString = JSON.stringify(payload).replace(/":"|","|{"|"}/g, (foundChar) => {
if (foundChar == '":"') return '=';
else if (foundChar == '","') return '&';
else return '';
});
return payloadString;
}

registerBidder(c1xAdapter);
27 changes: 27 additions & 0 deletions modules/c1xBidAdapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Overview

Module Name: C1X Bidder Adapter
Module Type: Bidder Adapter
Maintainer: cathy@c1exchange.com

# Description

Module that connects to Example's demand sources

# Test Parameters
```
var adUnits = [
{
code: 'test-div',
sizes: [[300, 250]], // a display size
bids: [
{
bidder: 'c1x',
params: {
placement: '12345'
}
}
]
},
];
```
Loading