mk9-prebid
Version:
Header Bidding Management Library
260 lines (232 loc) • 7.58 kB
JavaScript
import * as utils from '../src/utils.js'
import { registerBidder } from '../src/adapters/bidderFactory.js'
import { VIDEO, BANNER } from '../src/mediaTypes.js'
const BIDDER_CODE = 'seedtag';
const SEEDTAG_ALIAS = 'st';
const SEEDTAG_SSP_ENDPOINT = 'https://s.seedtag.com/c/hb/bid';
const SEEDTAG_SSP_ONTIMEOUT_ENDPOINT = 'https://s.seedtag.com/se/hb/timeout';
const ALLOWED_PLACEMENTS = {
inImage: true,
inScreen: true,
inArticle: true,
banner: true,
video: true
}
const mediaTypesMap = {
[BANNER]: 'display',
[VIDEO]: 'video'
};
const deviceConnection = {
FIXED: 'fixed',
MOBILE: 'mobile',
UNKNOWN: 'unknown'
};
const getConnectionType = () => {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || {}
switch (connection.type || connection.effectiveType) {
case 'wifi':
case 'ethernet':
return deviceConnection.FIXED
case 'cellular':
case 'wimax':
return deviceConnection.MOBILE
default:
const isMobile = /iPad|iPhone|iPod/.test(navigator.userAgent) || /android/i.test(navigator.userAgent)
return isMobile ? deviceConnection.UNKNOWN : deviceConnection.FIXED
}
};
function mapMediaType(seedtagMediaType) {
if (seedtagMediaType === 'display') return BANNER;
if (seedtagMediaType === 'video') return VIDEO;
else return seedtagMediaType;
}
function hasVideoMediaType(bid) {
return (!!bid.mediaTypes && !!bid.mediaTypes.video) || (!!bid.params && !!bid.params.video)
}
function hasMandatoryParams(params) {
return (
!!params.publisherId &&
!!params.adUnitId &&
!!params.placement &&
!!ALLOWED_PLACEMENTS[params.placement]
);
}
function hasMandatoryVideoParams(bid) {
const videoParams = getVideoParams(bid)
return hasVideoMediaType(bid) && !!videoParams.playerSize &&
utils.isArray(videoParams.playerSize) &&
videoParams.playerSize.length > 0;
}
function buildBidRequest(validBidRequest) {
const params = validBidRequest.params;
const mediaTypes = utils._map(
Object.keys(validBidRequest.mediaTypes),
function (pbjsType) {
return mediaTypesMap[pbjsType];
}
);
const bidRequest = {
id: validBidRequest.bidId,
transactionId: validBidRequest.transactionId,
sizes: validBidRequest.sizes,
supplyTypes: mediaTypes,
adUnitId: params.adUnitId,
adUnitCode: validBidRequest.adUnitCode,
placement: params.placement,
requestCount: validBidRequest.bidderRequestsCount || 1 // FIXME : in unit test the parameter bidderRequestsCount is undefined
};
if (params.adPosition) {
bidRequest.adPosition = params.adPosition;
}
if (hasVideoMediaType(validBidRequest)) {
bidRequest.videoParams = getVideoParams(validBidRequest)
}
return bidRequest;
}
/**
* return video param (global or overrided per bidder)
*/
function getVideoParams(validBidRequest) {
const videoParams = validBidRequest.mediaTypes.video || {};
if (videoParams.playerSize) {
videoParams.w = videoParams.playerSize[0][0];
videoParams.h = videoParams.playerSize[0][1];
}
const bidderVideoParams = (validBidRequest.params && validBidRequest.params.video) || {}
// override video params from seedtag bidder params
Object.keys(bidderVideoParams).forEach(key => {
videoParams[key] = validBidRequest.params.video[key]
})
return videoParams
}
function buildBidResponse(seedtagBid) {
const mediaType = mapMediaType(seedtagBid.mediaType);
const bid = {
requestId: seedtagBid.bidId,
cpm: seedtagBid.price,
width: seedtagBid.width,
height: seedtagBid.height,
creativeId: seedtagBid.creativeId,
currency: seedtagBid.currency,
netRevenue: true,
mediaType: mediaType,
ttl: seedtagBid.ttl,
nurl: seedtagBid.nurl,
meta: {
advertiserDomains: seedtagBid && seedtagBid.adomain && seedtagBid.adomain.length > 0 ? seedtagBid.adomain : []
}
};
if (mediaType === VIDEO) {
bid.vastXml = seedtagBid.content;
} else {
bid.ad = seedtagBid.content;
}
return bid;
}
export function getTimeoutUrl (data) {
let queryParams = '';
if (
utils.isArray(data) && data[0] &&
utils.isArray(data[0].params) && data[0].params[0]
) {
const params = data[0].params[0];
queryParams =
'?publisherToken=' + params.publisherId +
'&adUnitId=' + params.adUnitId;
}
return SEEDTAG_SSP_ONTIMEOUT_ENDPOINT + queryParams;
}
export const spec = {
code: BIDDER_CODE,
aliases: [SEEDTAG_ALIAS],
supportedMediaTypes: [BANNER, VIDEO],
/**
* Determines whether or not the given bid request is valid.
*
* @param {BidRequest} bid The bid params to validate.
* @return boolean True if this is a valid bid, and false otherwise.
*/
isBidRequestValid(bid) {
return hasVideoMediaType(bid)
? hasMandatoryParams(bid.params) && hasMandatoryVideoParams(bid)
: hasMandatoryParams(bid.params);
},
/**
* Make a server request from the list of BidRequests.
*
* @param {validBidRequests[]} - an array of bids
* @return ServerRequest Info describing the request to the server.
*/
buildRequests(validBidRequests, bidderRequest) {
const payload = {
url: bidderRequest.refererInfo.referer,
publisherToken: validBidRequests[0].params.publisherId,
cmp: !!bidderRequest.gdprConsent,
timeout: bidderRequest.timeout,
version: '$prebid.version$',
connectionType: getConnectionType(),
bidRequests: utils._map(validBidRequests, buildBidRequest)
};
if (payload.cmp) {
const gdprApplies = bidderRequest.gdprConsent.gdprApplies;
if (gdprApplies !== undefined) payload['ga'] = gdprApplies;
payload['cd'] = bidderRequest.gdprConsent.consentString;
}
const payloadString = JSON.stringify(payload)
return {
method: 'POST',
url: SEEDTAG_SSP_ENDPOINT,
data: payloadString
}
},
/**
* Unpack the response from the server into a list of bids.
*
* @param {ServerResponse} serverResponse A successful response from the server.
* @return {Bid[]} An array of bids which were nested inside the server.
*/
interpretResponse: function(serverResponse) {
const serverBody = serverResponse.body;
if (serverBody && serverBody.bids && utils.isArray(serverBody.bids)) {
return utils._map(serverBody.bids, function(bid) {
return buildBidResponse(bid);
});
} else {
return [];
}
},
/**
* Register the user sync pixels which should be dropped after the auction.
*
* @param {SyncOptions} syncOptions Which user syncs are allowed?
* @param {ServerResponse[]} serverResponses List of server's responses.
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs(syncOptions, serverResponses) {
const serverResponse = serverResponses[0];
if (syncOptions.iframeEnabled && serverResponse) {
const cookieSyncUrl = serverResponse.body.cookieSync;
return cookieSyncUrl ? [{ type: 'iframe', url: cookieSyncUrl }] : [];
} else {
return [];
}
},
/**
* Register bidder specific code, which will execute if bidder timed out after an auction
* @param {data} Containing timeout specific data
*/
onTimeout(data) {
const url = getTimeoutUrl(data);
utils.triggerPixel(url);
},
/**
* Function to call when the adapter wins the auction
* @param {bid} Bid information received from the server
*/
onBidWon: function (bid) {
if (bid && bid.nurl) {
utils.triggerPixel(bid.nurl);
}
}
}
registerBidder(spec);