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

HIRO Media: Remove batching mechanism and use AJAX instead of JSONP #1133

Merged
merged 3 commits into from
May 17, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
259 changes: 90 additions & 169 deletions src/adapters/hiromedia.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/*jslint white:true, browser:true, single: true*/
/*global $$PREBID_GLOBAL$$, require, module*/
/*global require, module*/

/**
* Adapter for HIRO Media
*
* @module HiroMediaAdapter
*
* @requires src/adloader
* @requires src/ajax
* @requires src/bidfactory
* @requires src/bidmanager
* @requires src/constants
* @requires src/utils
*/
var adloader = require('src/adloader');
var Ajax = require('src/ajax');
var bidfactory = require('src/bidfactory');
var bidmanager = require('src/bidmanager');
var utils = require('src/utils');
Expand Down Expand Up @@ -44,7 +44,7 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
* Default bid param values
*
* @memberof module:HiroMediaAdapter~
* @constant {module:HiroMediaAdapter~bidParams}
* @constant {array.<string>}
* @private
*/
var REQUIRED_BID_PARAMS = ['accountId'];
Expand All @@ -60,18 +60,6 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
bidUrl: 'https://hb-rtb.ktdpublishers.com/bid/get'
};

/**
* Storage for bid objects.
*
* Bids need to be stored between requests and response since the response
* is a global callback.
*
* @memberof module:HiroMediaAdapter~
* @var {array.<module:HiroMediaAdapter~bidInfo>}
* @private
*/
var _bidStorage = [];

/**
* Call bidmanager.addBidResponse
*
Expand All @@ -81,15 +69,15 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
* @memberof module:HiroMediaAdapter~
* @private
*
* @param {module:HiroMediaAdapter~bidInfo} bidInfo bid object wrapper to respond for
* @param {object} bid bid object connected to the response
* @param {object|boolean} [bidResponse] response object for bid, if not
* set the response will be an empty bid response.
*/
function addBidResponse(bidInfo, bidResponse) {
function addBidResponse(bid, bidResponse) {

var placementCode = bidInfo.bid.placementCode;
var placementCode = bid.placementCode;
var bidStatus = bidResponse ? STATUS.GOOD : STATUS.NO_BID;
var bidObject = bidfactory.createBid(bidStatus, bidInfo.bid);
var bidObject = bidfactory.createBid(bidStatus, bid);

bidObject.bidderCode = BIDDER_CODE;

Expand Down Expand Up @@ -124,40 +112,20 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
* @memberof module:HiroMediaAdapter~
* @private
*
* @param {object} response [description]
* @param {object} response bid response object
* @param {object} bid bid object connected to response
*/
function handleResponse(response) {

_bidStorage.filter(function (bidInfo) {
return bidInfo.batchKey === response.batchKey;
}).forEach(function (bidInfo) {

// Sample the bid responses according to `response.chance`,
// if `response.chance` is not provided, sample at 100%.
if (response.chance === undefined || checkChance(response.chance)) {
addBidResponse(bidInfo, response);
} else {
addBidResponse(bidInfo, false);
}
function handleResponse(response, bid) {

});

}

/**
* Call {@linkcode module:HiroMediaAdapter~handleResponse} for valid responses
*
* @global
*
* @param {object} [response] the response from the server
*/
$$PREBID_GLOBAL$$.hiromedia_callback = function (response) {

if (response && response.batchKey) {
handleResponse(response);
// Sample the bid responses according to `response.chance`,
// if `response.chance` is not provided, sample at 100%.
if (response.chance === undefined || checkChance(response.chance)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should use typeof here, in case someone re-defines undefined on their page.

See (6) from the top answer here: https://www.quora.com/What-is-the-best-way-to-check-if-a-property-or-variable-is-undefined

addBidResponse(bid, response);
} else {
addBidResponse(bid, false);
}

};
}

/**
* Find browser name and version
Expand Down Expand Up @@ -250,108 +218,52 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
}

/**
* Calculate and return a batchKey key for a bid
*
* Bid of similar placement can have similar responses,
* we can calculate a key based on the variant properties
* of a bid which can share the same response
* Build a {@linkcode module:HiroMediaAdapter~bidInfo|bidInfo} object based on a
* bid sent to {@linkcode module:HiroMediaAdapter#callBids|callBids}
*
* @memberof module:HiroMediaAdapter~
* @private
*
* @param {module:HiroMediaAdapter~bidInfo} bidInfo bid information
* @return {string} batch key for bid
* @param {object} bid bid from `Prebid.js`
* @return {module:HiroMediaAdapter~bidInfo} information for bid request
*/
function getBatchKey(bidInfo) {

var bidParams = bidInfo.bidParams;
var batchParams = [
bidParams.bidUrl,
bidParams.accountId,
bidInfo.selectedSize,
bidInfo.additionalSizes
];

return batchParams.join('-');

}

/**
* Build a set of {@linkcode module:HiroMediaAdapter~bidInfo|bidInfo} objects based on the
* bids sent to {@linkcode module:HiroMediaAdapter#callBids|callBids}
*
* @memberof module:HiroMediaAdapter~
* @private
*
* @param {object} bids bids sent from `Prebid.js`
* @return {array.<module:HiroMediaAdapter~bidInfo>} wrapped bids
*/
function processBids(bids) {

var result = [];

if (bids) {

utils.logMessage('hiromedia.processBids, processing ' + bids.length + ' bids');

bids.forEach(function (bid) {

var sizes = utils.parseSizesInput(bid.sizes);
var bidParams = defaultParams(bid.params);
var hasValidBidRequest = utils.hasValidBidRequest(bidParams, REQUIRED_BID_PARAMS, BIDDER_CODE);
var shouldBid = hasValidBidRequest;
var bidInfo = {
bid: bid,
bidParams: bidParams,
shouldBid: shouldBid,
selectedSize: sizes[0],
additionalSizes: sizes.slice(1).join(',')
};

if (shouldBid) {
bidInfo.batchKey = getBatchKey(bidInfo);
}

result.push(bidInfo);

});

}
function processBid(bid) {

var sizes = utils.parseSizesInput(bid.sizes);
var bidParams = defaultParams(bid.params);
var hasValidBidRequest = utils.hasValidBidRequest(bidParams, REQUIRED_BID_PARAMS, BIDDER_CODE);
var shouldBid = hasValidBidRequest;
var bidInfo = {
bidParams: bidParams,
shouldBid: shouldBid,
selectedSize: sizes[0],
additionalSizes: sizes.slice(1).join(',')
};

return result;
return bidInfo;

}

/**
* Send a bid request to the bid server endpoint
*
* Calls `adLoader.loadScript`
* Wrapper around `JSON.parse()` that returns false on error
*
* @memberof module:HiroMediaAdapter~
* @private
*
* @param {string} url base url, can already contain query parameters
* @param {object} requestParams parameters to add to query
* @param {string} text potential JSON string to convert to object
* @return {object|boolean} object parsed from text or `false` in case of and error
*/
function sendBidRequest(url,requestParams) {

if (requestParams) {
function tryJson(text) {

if (url.indexOf('?') !== -1) {
url = url + '&';
} else {
url = url + '?';
}

Object.keys(requestParams).forEach(function (key) {
url = utils.tryAppendQueryString(url, key, requestParams[key]);
});
var object = false;

try {
object = JSON.parse(text);
} catch (ignore) {
// Ignored
}

utils.logMessage('hiromedia.callBids, url:' + url);

adloader.loadScript(url);
return object;

}

Expand All @@ -366,61 +278,72 @@ var HiroMediaAdapter = function HiroMediaAdapter() {

var browser = getBrowser();
var domain = getDomain();
var bidsRequested = {};
var bids = params && params.bids;
var ajaxOptions = {
method: 'GET',
withCredentials: true
};

// Fixed data, shared by all requests
var fixedRequest = {
adapterVersion: ADAPTER_VERSION,
browser: browser.name,
browserVersion: browser.version,
domain: domain
};

utils.logMessage('hiromedia.callBids');

if (params) {
if (bids && bids.length) {

// Processed bids are stored in the adapter scope
_bidStorage = processBids(params.bids);
bids.forEach(function (bid) {

} else {
var bidInfo = processBid(bid);

// Ensure we don't run on stale data
_bidStorage = [];
var bidParams = bidInfo.bidParams;
utils.logMessage('hiromedia.callBids, bidInfo ' + bid.placementCode + ' ' + bidInfo.shouldBid);

}
if (bidInfo.shouldBid) {

if (_bidStorage.length) {
var url = bidParams.bidUrl;
var requestParams = Object.assign({}, fixedRequest, bidInfo.bidParams, {
placementCode: bid.placementCode,
selectedSize: bidInfo.selectedSize,
additionalSizes: bidInfo.additionalSizes
});

// Loop over processed bids and send a request if a request for the bid
// batchKey has not been sent.
_bidStorage.forEach(function (bidInfo) {
Object.keys(requestParams).forEach(function (key){
if (requestParams[key] === '' || requestParams[key] === undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

delete requestParams[key];
}
});

var bid = bidInfo.bid;
var batchKey = bidInfo.batchKey;
var bidParams = bidInfo.bidParams;
utils.logMessage('hiromedia.callBids, bid request ' + url + ' ' + JSON.stringify(bidInfo.bidRequest));

utils.logMessage('hiromedia.callBids, bidInfo ' + bid.placementCode + ' ' + bidInfo.shouldBid);
Ajax.ajax(url, {

if (bidInfo.shouldBid) {
success: function(responseText) {

var url = bidParams.bidUrl;
var response = tryJson(responseText);

handleResponse(response, bid);

},

error: function(err, xhr) {

if (!bidsRequested[batchKey]) {
utils.logError('hiromedia.callBids, bid request error', xhr.status, err);

bidsRequested[batchKey] = true;
addBidResponse(bid, false);

sendBidRequest(url,{
adapterVersion: ADAPTER_VERSION,
callback: '$$PREBID_GLOBAL$$.hiromedia_callback',
batchKey: batchKey,
placementCode: bid.placementCode,
accountId: bidParams.accountId,
browser: browser.name,
browserVersion: browser.version,
domain: domain,
selectedSize: bidInfo.selectedSize,
additionalSizes: bidInfo.additionalSizes
});
}

}
}, requestParams, ajaxOptions);

} else {

// No bid
addBidResponse(bidInfo, false);
addBidResponse(bid, false);

}

Expand Down Expand Up @@ -452,10 +375,8 @@ var HiroMediaAdapter = function HiroMediaAdapter() {
* @typedef {object} module:HiroMediaAdapter~bidInfo
* @private
*
* @property {object} bid original bid passed to #callBids
* @property {string} selectedSize the first size in the the placement sizes array
* @property {string} additionalSizes list of sizes in the placement sizes array besides the first
* @property {string} batchKey key used for batching requests which have the same basic properties
* @property {module:HiroMediaAdapter~bidParams} bidParams original params passed for bid in #callBids
* @property {boolean} shouldBid flag to determine if the bid is valid for bidding or not
*/
Expand Down
Loading