Untitled diff

생성일 비교 결과 만료 없음
2 삭제
419
27 추가
439
import Adapter from 'src/adapter';
import Adapter from 'src/adapter';
import adaptermanager from 'src/adaptermanager';
import adaptermanager from 'src/adaptermanager';
import { config } from 'src/config';
import { config } from 'src/config';
import bidfactory from 'src/bidfactory';
import bidfactory from 'src/bidfactory';
import { userSync } from 'src/userSync';
import { userSync } from 'src/userSync';
import { nativeBidIsValid } from 'src/native';
import { nativeBidIsValid } from 'src/native';
import { isValidVideoBid } from 'src/video';
import { isValidVideoBid } from 'src/video';
import CONSTANTS from 'src/constants.json';
import CONSTANTS from 'src/constants.json';
import events from 'src/events';
import events from 'src/events';
import includes from 'core-js/library/fn/array/includes';
import includes from 'core-js/library/fn/array/includes';


import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest } from 'src/utils';
import { logWarn, logError, parseQueryStringParameters, delayExecution, parseSizesInput, getBidderRequest, timestamp } from 'src/utils';


/**
/**
* This file aims to support Adapters during the Prebid 0.x -> 1.x transition.
* This file aims to support Adapters during the Prebid 0.x -> 1.x transition.
*
*
* Prebid 1.x and Prebid 0.x will be in separate branches--perhaps for a long time.
* Prebid 1.x and Prebid 0.x will be in separate branches--perhaps for a long time.
* This function defines an API for adapter construction which is compatible with both versions.
* This function defines an API for adapter construction which is compatible with both versions.
* Adapters which use it can maintain their code in master, and only this file will need to change
* Adapters which use it can maintain their code in master, and only this file will need to change
* in the 1.x branch.
* in the 1.x branch.
*
*
* Typical usage looks something like:
* Typical usage looks something like:
*
*
* const adapter = registerBidder({
* const adapter = registerBidder({
* code: 'myBidderCode',
* code: 'myBidderCode',
* aliases: ['alias1', 'alias2'],
* aliases: ['alias1', 'alias2'],
* supportedMediaTypes: ['video', 'native'],
* supportedMediaTypes: ['video', 'native'],
* isBidRequestValid: function(paramsObject) { return true/false },
* isBidRequestValid: function(paramsObject) { return true/false },
* buildRequests: function(bidRequests, bidderRequest) { return some ServerRequest(s) },
* buildRequests: function(bidRequests, bidderRequest) { return some ServerRequest(s) },
* interpretResponse: function(oneServerResponse) { return some Bids, or throw an error. }
* interpretResponse: function(oneServerResponse) { return some Bids, or throw an error. }
* });
* });
*
*
* @see BidderSpec for the full API and more thorough descriptions.
* @see BidderSpec for the full API and more thorough descriptions.
*/
*/


/**
/**
* @typedef {object} BidderSpec An object containing the adapter-specific functions needed to
* @typedef {object} BidderSpec An object containing the adapter-specific functions needed to
* make a Bidder.
* make a Bidder.
*
*
* @property {string} code A code which will be used to uniquely identify this bidder. This should be the same
* @property {string} code A code which will be used to uniquely identify this bidder. This should be the same
* one as is used in the call to registerBidAdapter
* one as is used in the call to registerBidAdapter
* @property {string[]} [aliases] A list of aliases which should also resolve to this bidder.
* @property {string[]} [aliases] A list of aliases which should also resolve to this bidder.
* @property {MediaType[]} [supportedMediaTypes]: A list of Media Types which the adapter supports.
* @property {MediaType[]} [supportedMediaTypes]: A list of Media Types which the adapter supports.
* @property {function(object): boolean} isBidRequestValid Determines whether or not the given bid has all the params
* @property {function(object): boolean} isBidRequestValid Determines whether or not the given bid has all the params
* needed to make a valid request.
* needed to make a valid request.
* @property {function(BidRequest[], bidderRequest): ServerRequest|ServerRequest[]} buildRequests Build the request to the Server
* @property {function(BidRequest[], bidderRequest): ServerRequest|ServerRequest[]} buildRequests Build the request to the Server
* which requests Bids for the given array of Requests. Each BidRequest in the argument array is guaranteed to have
* which requests Bids for the given array of Requests. Each BidRequest in the argument array is guaranteed to have
* passed the isBidRequestValid() test.
* passed the isBidRequestValid() test.
* @property {function(ServerResponse, BidRequest): Bid[]} interpretResponse Given a successful response from the Server,
* @property {function(ServerResponse, BidRequest): Bid[]} interpretResponse Given a successful response from the Server,
* interpret it and return the Bid objects. This function will be run inside a try/catch.
* interpret it and return the Bid objects. This function will be run inside a try/catch.
* If it throws any errors, your bids will be discarded.
* If it throws any errors, your bids will be discarded.
* @property {function(SyncOptions, ServerResponse[]): UserSync[]} [getUserSyncs] Given an array of all the responses
* @property {function(SyncOptions, ServerResponse[]): UserSync[]} [getUserSyncs] Given an array of all the responses
* from the server, determine which user syncs should occur. The argument array will contain every element
* from the server, determine which user syncs should occur. The argument array will contain every element
* which has been sent through to interpretResponse. The order of syncs in this array matters. The most
* which has been sent through to interpretResponse. The order of syncs in this array matters. The most
* important ones should come first, since publishers may limit how many are dropped on their page.
* important ones should come first, since publishers may limit how many are dropped on their page.
*/
*/


/**
/**
* @typedef {object} BidRequest
* @typedef {object} BidRequest
*
*
* @property {string} bidId A string which uniquely identifies this BidRequest in the current Auction.
* @property {string} bidId A string which uniquely identifies this BidRequest in the current Auction.
* @property {object} params Any bidder-specific params which the publisher used in their bid request.
* @property {object} params Any bidder-specific params which the publisher used in their bid request.
*/
*/


/**
/**
* @typedef {object} ServerRequest
* @typedef {object} ServerRequest
*
*
* @property {('GET'|'POST')} method The type of request which this is.
* @property {('GET'|'POST')} method The type of request which this is.
* @property {string} url The endpoint for the request. For example, "//bids.example.com".
* @property {string} url The endpoint for the request. For example, "//bids.example.com".
* @property {string|object} data Data to be sent in the request.
* @property {string|object} data Data to be sent in the request.
* @property {object} options Content-Type set in the header of the bid request, overrides default 'text/plain'.
* @property {object} options Content-Type set in the header of the bid request, overrides default 'text/plain'.
* If this is a GET request, they'll become query params. If it's a POST request, they'll be added to the body.
* If this is a GET request, they'll become query params. If it's a POST request, they'll be added to the body.
* Strings will be added as-is. Objects will be unpacked into query params based on key/value mappings, or
* Strings will be added as-is. Objects will be unpacked into query params based on key/value mappings, or
* JSON-serialized into the Request body.
* JSON-serialized into the Request body.
*/
*/


/**
/**
* @typedef {object} ServerResponse
* @typedef {object} ServerResponse
*
*
* @property {*} body The response body. If this is legal JSON, then it will be parsed. Otherwise it'll be a
* @property {*} body The response body. If this is legal JSON, then it will be parsed. Otherwise it'll be a
* string with the body's content.
* string with the body's content.
* @property {{get: function(string): string} headers The response headers.
* @property {{get: function(string): string} headers The response headers.
* Call this like `ServerResponse.headers.get("Content-Type")`
* Call this like `ServerResponse.headers.get("Content-Type")`
*/
*/


/**
/**
* @typedef {object} Bid
* @typedef {object} Bid
*
*
* @property {string} requestId The specific BidRequest which this bid is aimed at.
* @property {string} requestId The specific BidRequest which this bid is aimed at.
* This should match the BidRequest.bidId which this Bid targets.
* This should match the BidRequest.bidId which this Bid targets.
* @property {string} ad A URL which can be used to load this ad, if it's chosen by the publisher.
* @property {string} ad A URL which can be used to load this ad, if it's chosen by the publisher.
* @property {string} currency The currency code for the cpm value
* @property {string} currency The currency code for the cpm value
* @property {number} cpm The bid price, in US cents per thousand impressions.
* @property {number} cpm The bid price, in US cents per thousand impressions.
* @property {number} ttl Time-to-live - how long (in seconds) Prebid can use this bid.
* @property {number} ttl Time-to-live - how long (in seconds) Prebid can use this bid.
* @property {boolean} netRevenue Boolean defining whether the bid is Net or Gross. The default is true (Net).
* @property {boolean} netRevenue Boolean defining whether the bid is Net or Gross. The default is true (Net).
* @property {number} height The height of the ad, in pixels.
* @property {number} height The height of the ad, in pixels.
* @property {number} width The width of the ad, in pixels.
* @property {number} width The width of the ad, in pixels.
*
*
* @property [Renderer] renderer A Renderer which can be used as a default for this bid,
* @property [Renderer] renderer A Renderer which can be used as a default for this bid,
* if the publisher doesn't override it. This is only relevant for Outstream Video bids.
* if the publisher doesn't override it. This is only relevant for Outstream Video bids.
*/
*/


/**
/**
* @typedef {Object} SyncOptions
* @typedef {Object} SyncOptions
*
*
* An object containing information about usersyncs which the adapter should obey.
* An object containing information about usersyncs which the adapter should obey.
*
*
* @property {boolean} iframeEnabled True if iframe usersyncs are allowed, and false otherwise
* @property {boolean} iframeEnabled True if iframe usersyncs are allowed, and false otherwise
* @property {boolean} pixelEnabled True if image usersyncs are allowed, and false otherwise
* @property {boolean} pixelEnabled True if image usersyncs are allowed, and false otherwise
*/
*/


/**
/**
* TODO: Move this to the UserSync module after that PR is merged.
* TODO: Move this to the UserSync module after that PR is merged.
*
*
* @typedef {object} UserSync
* @typedef {object} UserSync
*
*
* @property {('image'|'iframe')} type The type of user sync to be done.
* @property {('image'|'iframe')} type The type of user sync to be done.
* @property {string} url The URL which makes the sync happen.
* @property {string} url The URL which makes the sync happen.
*/
*/


// common params for all mediaTypes
// common params for all mediaTypes
const COMMON_BID_RESPONSE_KEYS = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency'];
const COMMON_BID_RESPONSE_KEYS = ['requestId'];
const GOOD_BID_RESPONSE_KEYS = ['requestId', 'cpm', 'ttl', 'creativeId', 'netRevenue', 'currency'];


/**
/**
* Register a bidder with prebid, using the given spec.
* Register a bidder with prebid, using the given spec.
*
*
* If possible, Adapter modules should use this function instead of adaptermanager.registerBidAdapter().
* If possible, Adapter modules should use this function instead of adaptermanager.registerBidAdapter().
*
*
* @param {BidderSpec} spec An object containing the bare-bones functions we need to make a Bidder.
* @param {BidderSpec} spec An object containing the bare-bones functions we need to make a Bidder.
*/
*/
export function registerBidder(spec) {
export function registerBidder(spec) {
const mediaTypes = Array.isArray(spec.supportedMediaTypes)
const mediaTypes = Array.isArray(spec.supportedMediaTypes)
? { supportedMediaTypes: spec.supportedMediaTypes }
? { supportedMediaTypes: spec.supportedMediaTypes }
: undefined;
: undefined;
function putBidder(spec) {
function putBidder(spec) {
const bidder = newBidder(spec);
const bidder = newBidder(spec);
adaptermanager.registerBidAdapter(bidder, spec.code, mediaTypes);
adaptermanager.registerBidAdapter(bidder, spec.code, mediaTypes);
}
}


putBidder(spec);
putBidder(spec);
if (Array.isArray(spec.aliases)) {
if (Array.isArray(spec.aliases)) {
spec.aliases.forEach(alias => {
spec.aliases.forEach(alias => {
adaptermanager.aliasRegistry[alias] = spec.code;
adaptermanager.aliasRegistry[alias] = spec.code;
putBidder(Object.assign({}, spec, { code: alias }));
putBidder(Object.assign({}, spec, { code: alias }));
});
});
}
}
}
}


/**
/**
* Make a new bidder from the given spec. This is exported mainly for testing.
* Make a new bidder from the given spec. This is exported mainly for testing.
* Adapters will probably find it more convenient to use registerBidder instead.
* Adapters will probably find it more convenient to use registerBidder instead.
*
*
* @param {BidderSpec} spec
* @param {BidderSpec} spec
*/
*/
export function newBidder(spec) {
export function newBidder(spec) {
return Object.assign(new Adapter(spec.code), {
return Object.assign(new Adapter(spec.code), {
getSpec: function() {
getSpec: function() {
return Object.freeze(spec);
return Object.freeze(spec);
},
},
registerSyncs,
registerSyncs,
callBids: function(bidderRequest, addBidResponse, done, ajax) {
callBids: function(bidderRequest, addBidResponse, done, ajax) {
if (!Array.isArray(bidderRequest.bids)) {
if (!Array.isArray(bidderRequest.bids)) {
return;
return;
}
}


const _bids = [];
const adUnitCodesHandled = {};
const adUnitCodesHandled = {};
function addBidWithCode(adUnitCode, bid) {
function addBidWithCode(adUnitCode, bid) {
adUnitCodesHandled[adUnitCode] = true;
adUnitCodesHandled[adUnitCode] = true;
if (isValid(adUnitCode, bid, [bidderRequest])) {
if (isValid(adUnitCode, bid, [bidderRequest])) {
addBidResponse(adUnitCode, bid);
addBidResponse(adUnitCode, bid);
}
}
}
}


// After all the responses have come back, call done() and
// After all the responses have come back, call done() and
// register any required usersync pixels.
// register any required usersync pixels.
const responses = [];
const responses = [];
function afterAllResponses(bids) {
function afterAllResponses() {
const bidsArray = bids ? (bids[0] ? bids : [bids]) : [];
bidderRequest.end = timestamp();
let bidIds = (_bids || []).map(bid => {
return bid.requestId;
});
let missingBids = bidderRequest.bids.filter(bid => {
return bidIds.indexOf(bid.bidId) === -1;
});
missingBids.forEach(bid => {
_bids.push(bid);
const bidRequest = bidRequestMap[bid.bidId];
bid.requestId = bid.bidId;
const prebidNoBid = Object.assign(bidfactory.createBid(CONSTANTS.STATUS.NO_BID, bidRequest), bid);
addBidWithCode(bid.adUnitCode, prebidNoBid);
});
const bidsArray = _bids ? (_bids[0] ? _bids : [_bids]) : [];


const videoBid = bidsArray.some(bid => bid.mediaType === 'video');
const videoBid = bidsArray.some(bid => bid.mediaType === 'video');
const cacheEnabled = config.getConfig('cache.url');
const cacheEnabled = config.getConfig('cache.url');


// video bids with cache enabled need to be cached first before they are considered done
// video bids with cache enabled need to be cached first before they are considered done
if (!(videoBid && cacheEnabled)) {
if (!(videoBid && cacheEnabled)) {
done();
done();
}
}


// TODO: the code above needs to be refactored. We should always call done when we're done. if the auction
// TODO: the code above needs to be refactored. We should always call done when we're done. if the auction
// needs to do cleanup before _it_ can be done it should handle that itself in the auction. It should _not_
// needs to do cleanup before _it_ can be done it should handle that itself in the auction. It should _not_
// require us, the bidders, to conditionally call done. That makes the whole done API very flaky.
// require us, the bidders, to conditionally call done. That makes the whole done API very flaky.
// As soon as that is refactored, we can move this emit event where it should be, within the done function.
// As soon as that is refactored, we can move this emit event where it should be, within the done function.
events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest);
events.emit(CONSTANTS.EVENTS.BIDDER_DONE, bidderRequest);


registerSyncs(responses, bidderRequest.gdprConsent);
registerSyncs(responses, bidderRequest.gdprConsent);
}
}


const validBidRequests = bidderRequest.bids.filter(filterAndWarn);
const validBidRequests = bidderRequest.bids.filter(filterAndWarn);
if (validBidRequests.length === 0) {
if (validBidRequests.length === 0) {
afterAllResponses();
afterAllResponses();
return;
return;
}
}
const bidRequestMap = {};
const bidRequestMap = {};
validBidRequests.forEach(bid => {
validBidRequests.forEach(bid => {
bidRequestMap[bid.bidId] = bid;
bidRequestMap[bid.bidId] = bid;
// Delete this once we are 1.0
// Delete this once we are 1.0
if (!bid.adUnitCode) {
if (!bid.adUnitCode) {
bid.adUnitCode = bid.placementCode
bid.adUnitCode = bid.placementCode
}
}
});
});


let requests = spec.buildRequests(validBidRequests, bidderRequest);
let requests = spec.buildRequests(validBidRequests, bidderRequest);
if (!requests || requests.length === 0) {
if (!requests || requests.length === 0) {
afterAllResponses();
afterAllResponses();
return;
return;
}
}
if (!Array.isArray(requests)) {
if (!Array.isArray(requests)) {
requests = [requests];
requests = [requests];
}
}


// Callbacks don't compose as nicely as Promises. We should call done() once _all_ the
// Callbacks don't compose as nicely as Promises. We should call done() once _all_ the
// Server requests have returned and been processed. Since `ajax` accepts a single callback,
// Server requests have returned and been processed. Since `ajax` accepts a single callback,
// we need to rig up a function which only executes after all the requests have been responded.
// we need to rig up a function which only executes after all the requests have been responded.
const onResponse = delayExecution(afterAllResponses, requests.length)
const onResponse = delayExecution(afterAllResponses, requests.length)
requests.forEach(processRequest);
requests.forEach(processRequest);


function formatGetParameters(data) {
function formatGetParameters(data) {
if (data) {
if (data) {
return `?${typeof data === 'object' ? parseQueryStringParameters(data) : data}`;
return `?${typeof data === 'object' ? parseQueryStringParameters(data) : data}`;
}
}


return '';
return '';
}
}


function processRequest(request) {
function processRequest(request) {
switch (request.method) {
switch (request.method) {
case 'GET':
case 'GET':
ajax(
ajax(
`${request.url}${formatGetParameters(request.data)}`,
`${request.url}${formatGetParameters(request.data)}`,
{
{
success: onSuccess,
success: onSuccess,
error: onFailure
error: onFailure
},
},
undefined,
undefined,
Object.assign({
Object.assign({
method: 'GET',
method: 'GET',
withCredentials: true
withCredentials: true
}, request.options)
}, request.options)
);
);
break;
break;
case 'POST':
case 'POST':
ajax(
ajax(
request.url,
request.url,
{
{
success: onSuccess,
success: onSuccess,
error: onFailure
error: onFailure
},
},
typeof request.data === 'string' ? request.data : JSON.stringify(request.data),
typeof request.data === 'string' ? request.data : JSON.stringify(request.data),
Object.assign({
Object.assign({
method: 'POST',
method: 'POST',
contentType: 'text/plain',
contentType: 'text/plain',
withCredentials: true
withCredentials: true
}, request.options)
}, request.options)
);
);
break;
break;
default:
default:
logWarn(`Skipping invalid request from ${spec.code}. Request type ${request.type} must be GET or POST`);
logWarn(`Skipping invalid request from ${spec.code}. Request type ${request.type} must be GET or POST`);
onResponse();
onResponse();
}
}


// If the server responds successfully, use the adapter code to unpack the Bids from it.
// If the server responds successfully, use the adapter code to unpack the Bids from it.
// If the adapter code fails, no bids should be added. After all the bids have been added, make
// If the adapter code fails, no bids should be added. After all the bids have been added, make
// sure to call the `onResponse` function so that we're one step closer to calling done().
// sure to call the `onResponse` function so that we're one step closer to calling done().
function onSuccess(response, responseObj) {
function onSuccess(response, responseObj) {
try {
try {
response = JSON.parse(response);
response = JSON.parse(response);
} catch (e) { /* response might not be JSON... that's ok. */ }
} catch (e) { /* response might not be JSON... that's ok. */ }


// Make response headers available for #1742. These are lazy-loaded because most adapters won't need them.
// Make response headers available for #1742. These are lazy-loaded because most adapters won't need them.
response = {
response = {
body: response,
body: response,
headers: headerParser(responseObj)
headers: headerParser(responseObj)
};
};
responses.push(response);
responses.push(response);


let bids;
let bids;
try {
try {
bids = spec.interpretResponse(response, request);
bids = spec.interpretResponse(response, request);
} catch (err) {
} catch (err) {
logError(`Bidder ${spec.code} failed to interpret the server's response. Continuing without bids`, null, err);
logError(`Bidder ${spec.code} failed to interpret the server's response. Continuing without bids`, null, err);
onResponse();
onResponse();
return;
return;
}
}


if (bids) {
if (bids) {
if (bids.forEach) {
if (bids.forEach) {
bids.forEach(addBidUsingRequestMap);
bids.forEach(addBidUsingRequestMap);
} else {
} else {
addBidUsingRequestMap(bids);
addBidUsingRequestMap(bids);
}
}
}
}
onResponse(bids);
onResponse();


function addBidUsingRequestMap(bid) {
function addBidUsingRequestMap(bid) {
_bids.push(bid);
const bidRequest = bidRequestMap[bid.requestId];
const bidRequest = bidRequestMap[bid.requestId];
if (bidRequest) {
if (bidRequest) {
const prebidBid = Object.assign(bidfactory.createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid);
const prebidBid = Object.assign(bidfactory.createBid(CONSTANTS.STATUS.GOOD, bidRequest), bid);
addBidWithCode(bidRequest.adUnitCode, prebidBid);
addBidWithCode(bidRequest.adUnitCode, prebidBid);
} else {
} else {
logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`);
logWarn(`Bidder ${spec.code} made bid for unknown request ID: ${bid.requestId}. Ignoring.`);
}
}
}
}


function headerParser(xmlHttpResponse) {
function headerParser(xmlHttpResponse) {
return {
return {
get: responseObj.getResponseHeader.bind(responseObj)
get: responseObj.getResponseHeader.bind(responseObj)
};
};
}
}
}
}


// If the server responds with an error, there's not much we can do. Log it, and make sure to
// If the server responds with an error, there's not much we can do. Log it, and make sure to
// call onResponse() so that we're one step closer to calling done().
// call onResponse() so that we're one step closer to calling done().
function onFailure(err) {
function onFailure(err) {
logError(`Server call for ${spec.code} failed: ${err}. Continuing without bids.`);
logError(`Server call for ${spec.code} failed: ${err}. Continuing without bids.`);
onResponse();
onResponse();
}
}
}
}
}
}
});
});


function registerSyncs(responses, gdprConsent) {
function registerSyncs(responses, gdprConsent) {
if (spec.getUserSyncs) {
if (spec.getUserSyncs) {
let syncs = spec.getUserSyncs({
let syncs = spec.getUserSyncs({
iframeEnabled: config.getConfig('userSync.iframeEnabled'),
iframeEnabled: config.getConfig('userSync.iframeEnabled'),
pixelEnabled: config.getConfig('userSync.pixelEnabled'),
pixelEnabled: config.getConfig('userSync.pixelEnabled'),
}, responses, gdprConsent);
}, responses, gdprConsent);
if (syncs) {
if (syncs) {
if (!Array.isArray(syncs)) {
if (!Array.isArray(syncs)) {
syncs = [syncs];
syncs = [syncs];
}
}
syncs.forEach((sync) => {
syncs.forEach((sync) => {
userSync.registerSync(sync.type, spec.code, sync.url)
userSync.registerSync(sync.type, spec.code, sync.url)
});
});
}
}
}
}
}
}


function filterAndWarn(bid) {
function filterAndWarn(bid) {
if (!spec.isBidRequestValid(bid)) {
if (!spec.isBidRequestValid(bid)) {
logWarn(`Invalid bid sent to bidder ${spec.code}: ${JSON.stringify(bid)}`);
logWarn(`Invalid bid sent to bidder ${spec.code}: ${JSON.stringify(bid)}`);
return false;
return false;
}
}
return true;
return true;
}
}
}
}


// check that the bid has a width and height set
// check that the bid has a width and height set
function validBidSize(adUnitCode, bid, bidRequests) {
function validBidSize(adUnitCode, bid, bidRequests) {
if ((bid.width || bid.width === 0) && (bid.height || bid.height === 0)) {
if ((bid.width || bid.width === 0) && (bid.height || bid.height === 0)) {
return true;
return true;
}
}


const adUnit = getBidderRequest(bidRequests, bid.bidderCode, adUnitCode);
const adUnit = getBidderRequest(bidRequests, bid.bidderCode, adUnitCode);


const sizes = adUnit && adUnit.bids && adUnit.bids[0] && adUnit.bids[0].sizes;
const sizes = adUnit && adUnit.bids && adUnit.bids[0] && adUnit.bids[0].sizes;
const parsedSizes = parseSizesInput(sizes);
const parsedSizes = parseSizesInput(sizes);


// if a banner impression has one valid size, we assign that size to any bid
// if a banner impression has one valid size, we assign that size to any bid
// response that does not explicitly set width or height
// response that does not explicitly set width or height
if (parsedSizes.length === 1) {
if (parsedSizes.length === 1) {
const [ width, height ] = parsedSizes[0].split('x');
const [ width, height ] = parsedSizes[0].split('x');
bid.width = width;
bid.width = width;
bid.height = height;
bid.height = height;
return true;
return true;
}
}


return false;
return false;
}
}


// Validate the arguments sent to us by the adapter. If this returns false, the bid should be totally ignored.
// Validate the arguments sent to us by the adapter. If this returns false, the bid should be totally ignored.
export function isValid(adUnitCode, bid, bidRequests) {
export function isValid(adUnitCode, bid, bidRequests) {
function hasValidKeys() {
function hasValidKeys() {
let bidKeys = Object.keys(bid);
let bidKeys = Object.keys(bid);
if(bid.getStatusCode() === CONSTANTS.STATUS.GOOD) {
return GOOD_BID_RESPONSE_KEYS.every(key => includes(bidKeys, key));
}
return COMMON_BID_RESPONSE_KEYS.every(key => includes(bidKeys, key));
return COMMON_BID_RESPONSE_KEYS.every(key => includes(bidKeys, key));
}
}


function errorMessage(msg) {
function errorMessage(msg) {
return `Invalid bid from ${bid.bidderCode}. Ignoring bid: ${msg}`;
return `Invalid bid from ${bid.bidderCode}. Ignoring bid: ${msg}`;
}
}


if (!adUnitCode) {
if (!adUnitCode) {
logWarn('No adUnitCode was supplied to addBidResponse.');
logWarn('No adUnitCode was supplied to addBidResponse.');
return false;
return false;
}
}


if (!bid) {
if (!bid) {
logWarn(`Some adapter tried to add an undefined bid for ${adUnitCode}.`);
logWarn(`Some adapter tried to add an undefined bid for ${adUnitCode}.`);
return false;
return false;
}
}


if (!hasValidKeys()) {
if (!hasValidKeys()) {
logError(errorMessage(`Bidder ${bid.bidderCode} is missing required params. Check http://prebid.org/dev-docs/bidder-adapter-1.html for list of params.`));
logError(errorMessage(`Bidder ${bid.bidderCode} is missing required params. Check http://prebid.org/dev-docs/bidder-adapter-1.html for list of params.`));
return false;
return false;
}
}


if (bid.mediaType === 'native' && !nativeBidIsValid(bid, bidRequests)) {
if (bid.mediaType === 'native' && !nativeBidIsValid(bid, bidRequests)) {
logError(errorMessage('Native bid missing some required properties.'));
logError(errorMessage('Native bid missing some required properties.'));
return false;
return false;
}
}
if (bid.mediaType === 'video' && !isValidVideoBid(bid, bidRequests)) {
if (bid.mediaType === 'video' && !isValidVideoBid(bid, bidRequests)) {
logError(errorMessage(`Video bid does not have required vastUrl or renderer property`));
logError(errorMessage(`Video bid does not have required vastUrl or renderer property`));
return false;
return false;
}
}
if (bid.mediaType === 'banner' && !validBidSize(adUnitCode, bid, bidRequests)) {
if (bid.mediaType === 'banner' && !validBidSize(adUnitCode, bid, bidRequests)) {
logError(errorMessage(`Banner bids require a width and height`));
logError(errorMessage(`Banner bids require a width and height`));
return false;
return false;
}
}


return true;
return true;
}
}