mk9-prebid
Version:
Header Bidding Management Library
291 lines (268 loc) • 9.63 kB
JavaScript
;
import { getAdUnitSizes, logWarn, deepSetValue } from '../src/utils.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import includes from 'core-js-pure/features/array/includes.js';
import {config} from '../src/config.js';
const BID_SCHEME = 'https://';
const BID_DOMAIN = 'technoratimedia.com';
const USER_SYNC_HOST = 'https://ad-cdn.technoratimedia.com';
const VIDEO_PARAMS = [ 'minduration', 'maxduration', 'startdelay', 'placement', 'linearity', 'mimes', 'protocols', 'api' ];
const BLOCKED_AD_SIZES = [
'1x1',
'1x2'
];
const SUPPORTED_USER_ID_SOURCES = [
'liveramp.com', // Liveramp IdentityLink
'nextroll.com', // NextRoll XID
'verizonmedia.com', // Verizon Media ConnectID
'pubcid.org' // PubCommon ID
];
export const spec = {
code: 'synacormedia',
supportedMediaTypes: [ BANNER, VIDEO ],
sizeMap: {},
isVideoBid: function(bid) {
return bid.mediaTypes !== undefined &&
bid.mediaTypes.hasOwnProperty('video');
},
isBidRequestValid: function(bid) {
const hasRequiredParams = bid && bid.params && (bid.params.hasOwnProperty('placementId') || bid.params.hasOwnProperty('tagId')) && bid.params.hasOwnProperty('seatId');
const hasAdSizes = bid && getAdUnitSizes(bid).filter(size => BLOCKED_AD_SIZES.indexOf(size.join('x')) === -1).length > 0
return !!(hasRequiredParams && hasAdSizes);
},
buildRequests: function(validBidReqs, bidderRequest) {
if (!validBidReqs || !validBidReqs.length || !bidderRequest) {
return;
}
const refererInfo = bidderRequest.refererInfo;
const openRtbBidRequest = {
id: bidderRequest.auctionId,
site: {
domain: config.getConfig('publisherDomain') || location.hostname,
page: refererInfo.referer,
ref: document.referrer
},
device: {
ua: navigator.userAgent
},
imp: []
};
const schain = validBidReqs[0].schain;
if (schain) {
openRtbBidRequest.source = { ext: { schain } };
}
let seatId = null;
validBidReqs.forEach((bid, i) => {
if (seatId && seatId !== bid.params.seatId) {
logWarn(`Synacormedia: there is an inconsistent seatId: ${bid.params.seatId} but only sending bid requests for ${seatId}, you should double check your configuration`);
return;
} else {
seatId = bid.params.seatId;
}
const tagIdOrplacementId = bid.params.tagId || bid.params.placementId;
const bidFloor = bid.params.bidfloor ? parseFloat(bid.params.bidfloor) : null;
if (isNaN(bidFloor)) {
logWarn(`Synacormedia: there is an invalid bid floor: ${bid.params.bidfloor}`);
}
let pos = parseInt(bid.params.pos, 10);
if (isNaN(pos)) {
logWarn(`Synacormedia: there is an invalid POS: ${bid.params.pos}`);
pos = 0;
}
const videoOrBannerKey = this.isVideoBid(bid) ? 'video' : 'banner';
const adSizes = getAdUnitSizes(bid)
.filter(size => BLOCKED_AD_SIZES.indexOf(size.join('x')) === -1);
let imps = [];
if (videoOrBannerKey === 'banner') {
imps = this.buildBannerImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey);
} else if (videoOrBannerKey === 'video') {
imps = this.buildVideoImpressions(adSizes, bid, tagIdOrplacementId, pos, bidFloor, videoOrBannerKey);
}
if (imps.length > 0) {
imps.forEach(i => openRtbBidRequest.imp.push(i));
}
});
// CCPA
if (bidderRequest && bidderRequest.uspConsent) {
deepSetValue(openRtbBidRequest, 'regs.ext.us_privacy', bidderRequest.uspConsent);
}
// User ID
if (validBidReqs[0] && validBidReqs[0].userIdAsEids && Array.isArray(validBidReqs[0].userIdAsEids)) {
const eids = this.processEids(validBidReqs[0].userIdAsEids);
if (eids.length) {
deepSetValue(openRtbBidRequest, 'user.ext.eids', eids);
}
}
if (openRtbBidRequest.imp.length && seatId) {
return {
method: 'POST',
url: `${BID_SCHEME}${seatId}.${BID_DOMAIN}/openrtb/bids/${seatId}?src=$$REPO_AND_VERSION$$`,
data: openRtbBidRequest,
options: {
contentType: 'application/json',
withCredentials: true
}
};
}
},
processEids: function(userIdAsEids) {
const eids = [];
userIdAsEids.forEach(function(eid) {
if (SUPPORTED_USER_ID_SOURCES.indexOf(eid.source) > -1) {
eids.push(eid);
}
});
return eids;
},
buildBannerImpressions: function (adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) {
let format = [];
let imps = [];
adSizes.forEach((size, i) => {
if (!size || size.length !== 2) {
return;
}
format.push({
w: size[0],
h: size[1],
});
});
if (format.length > 0) {
const imp = {
id: `${videoOrBannerKey.substring(0, 1)}${bid.bidId}`,
banner: {
format,
pos
},
tagid: tagIdOrPlacementId,
};
if (bidFloor !== null && !isNaN(bidFloor)) {
imp.bidfloor = bidFloor;
}
imps.push(imp);
}
return imps;
},
buildVideoImpressions: function(adSizes, bid, tagIdOrPlacementId, pos, bidFloor, videoOrBannerKey) {
let imps = [];
adSizes.forEach((size, i) => {
if (!size || size.length != 2) {
return;
}
const size0 = size[0];
const size1 = size[1];
const imp = {
id: `${videoOrBannerKey.substring(0, 1)}${bid.bidId}-${size0}x${size1}`,
tagid: tagIdOrPlacementId
};
if (bidFloor !== null && !isNaN(bidFloor)) {
imp.bidfloor = bidFloor;
}
const videoOrBannerValue = {
w: size0,
h: size1,
pos
};
if (bid.mediaTypes.video) {
if (!bid.params.video) {
bid.params.video = {};
}
this.setValidVideoParams(bid.mediaTypes.video, bid.params.video);
}
if (bid.params.video) {
this.setValidVideoParams(bid.params.video, videoOrBannerValue);
}
imp[videoOrBannerKey] = videoOrBannerValue;
imps.push(imp);
});
return imps;
},
setValidVideoParams: function (sourceObj, destObj) {
Object.keys(sourceObj)
.filter(param => includes(VIDEO_PARAMS, param) && sourceObj[param] !== null && (!isNaN(parseInt(sourceObj[param], 10)) || !(sourceObj[param].length < 1)))
.forEach(param => destObj[param] = Array.isArray(sourceObj[param]) ? sourceObj[param] : parseInt(sourceObj[param], 10));
},
interpretResponse: function(serverResponse, bidRequest) {
const updateMacros = (bid, r) => {
return r ? r.replace(/\${AUCTION_PRICE}/g, bid.price) : r;
};
if (!serverResponse.body || typeof serverResponse.body != 'object') {
logWarn('Synacormedia: server returned empty/non-json response: ' + JSON.stringify(serverResponse.body));
return;
}
const {id, seatbid: seatbids} = serverResponse.body;
let bids = [];
if (id && seatbids) {
seatbids.forEach(seatbid => {
seatbid.bid.forEach(bid => {
const creative = updateMacros(bid, bid.adm);
const nurl = updateMacros(bid, bid.nurl);
const [, impType, impid] = bid.impid.match(/^([vb])([\w\d]+)/);
let height = bid.h;
let width = bid.w;
const isVideo = impType === 'v';
const isBanner = impType === 'b';
if ((!height || !width) && bidRequest.data && bidRequest.data.imp && bidRequest.data.imp.length > 0) {
bidRequest.data.imp.forEach(req => {
if (bid.impid === req.id) {
if (isVideo) {
height = req.video.h;
width = req.video.w;
} else if (isBanner) {
let bannerHeight = 1;
let bannerWidth = 1;
if (req.banner.format && req.banner.format.length > 0) {
bannerHeight = req.banner.format[0].h;
bannerWidth = req.banner.format[0].w;
}
height = bannerHeight;
width = bannerWidth;
} else {
height = 1;
width = 1;
}
}
});
}
const bidObj = {
requestId: impid,
cpm: parseFloat(bid.price),
width: parseInt(width, 10),
height: parseInt(height, 10),
creativeId: `${seatbid.seat}_${bid.crid}`,
currency: 'USD',
netRevenue: true,
mediaType: isVideo ? VIDEO : BANNER,
ad: creative,
ttl: 60
};
if (bid.adomain != undefined || bid.adomain != null) {
bidObj.meta = { advertiserDomains: bid.adomain };
}
if (isVideo) {
const [, uuid] = nurl.match(/ID=([^&]*)&?/);
if (!config.getConfig('cache.url')) {
bidObj.videoCacheKey = encodeURIComponent(uuid);
}
bidObj.vastUrl = nurl;
}
bids.push(bidObj);
});
});
}
return bids;
},
getUserSyncs: function (syncOptions, serverResponses) {
const syncs = [];
if (syncOptions.iframeEnabled) {
syncs.push({
type: 'iframe',
url: `${USER_SYNC_HOST}/html/usersync.html?src=$$REPO_AND_VERSION$$`
});
} else {
logWarn('Synacormedia: Please enable iframe based user sync.');
}
return syncs;
}
};
registerBidder(spec);