Skip to content

Commit

Permalink
Merge pull request #4 from prebid/master
Browse files Browse the repository at this point in the history
merge in upstream changes
  • Loading branch information
MikeSperone authored Sep 22, 2020
2 parents 1a94f12 + 9297612 commit 86eab24
Show file tree
Hide file tree
Showing 25 changed files with 857 additions and 167 deletions.
1 change: 1 addition & 0 deletions PR_REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ For modules and core platform updates, the initial reviewer should request an ad
- If they support SChain, add `schain_supported: true`
- If their bidder doesn't work well with safeframed creatives, add `safeframes_ok: false`. This will alert publishers to not use safeframed creatives when creating the ad server entries for their bidder.
- If they're setting a deal ID in some scenarios, add `bidder_supports_deals: true`
- If they have an IAB Global Vendor List ID, add `gvl_id: ID`. There's no default.
- If all above is good, add a `LGTM` comment and request 1 additional core member to review.
- Once there is 2 `LGTM` on the PR, merge to master
- Ask the submitter to add a PR for documentation if applicable.
Expand Down
16 changes: 13 additions & 3 deletions modules/connectadBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const SUPPORTED_MEDIA_TYPES = [BANNER];

export const spec = {
code: BIDDER_CODE,
gvlid: 138,
aliases: [ BIDDER_CODE_ALIAS ],
supportedMediaTypes: SUPPORTED_MEDIA_TYPES,

Expand Down Expand Up @@ -81,8 +82,10 @@ export const spec = {
pisze: bid.mediaTypes.banner.sizes[0] || bid.sizes[0],
sizes: bid.mediaTypes.banner.sizes,
adTypes: getSize(bid.mediaTypes.banner.sizes || bid.sizes),
floor: getBidFloor(bid)
}, bid.params);
bidfloor: getBidFloor(bid),
siteId: bid.params.siteId,
networkId: bid.params.networkId
});

if (placement.networkId && placement.siteId) {
data.placements.push(placement);
Expand Down Expand Up @@ -134,6 +137,13 @@ export const spec = {
return bidResponses;
},

transformBidParams: function (params, isOpenRtb) {
return utils.convertTypes({
'siteId': 'number',
'networkId': 'number'
}, params);
},

getUserSyncs: function(syncOptions, serverResponses, gdprConsent, uspConsent) {
let syncEndpoint = 'https://cdn.connectad.io/connectmyusers.php?';

Expand Down Expand Up @@ -227,7 +237,7 @@ function getBidFloor(bidRequest) {
});
}

let floor = floorInfo.floor || 0;
let floor = floorInfo.floor || bidRequest.params.bidfloor || bidRequest.params.floorprice || 0;

return floor;
}
Expand Down
13 changes: 9 additions & 4 deletions modules/dfpAdServerVideo.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { deepAccess, isEmpty, logError, parseSizesInput, formatQS, parseUrl, bui
import { config } from '../src/config.js';
import { getHook, submodule } from '../src/hook.js';
import { auctionManager } from '../src/auctionManager.js';
import events from '../src/events.js';
import CONSTANTS from '../src/constants.json';

/**
* @typedef {Object} DfpVideoParams
Expand Down Expand Up @@ -245,17 +247,20 @@ function getCustParams(bid, options) {
allTargetingData = (allTargeting) ? allTargeting[adUnit.code] : {};
}

const optCustParams = deepAccess(options, 'params.cust_params');
let customParams = Object.assign({},
const prebidTargetingSet = Object.assign({},
// Why are we adding standard keys here ? Refer https://github.com/prebid/Prebid.js/issues/3664
{ hb_uuid: bid && bid.videoCacheKey },
// hb_uuid will be deprecated and replaced by hb_cache_id
{ hb_cache_id: bid && bid.videoCacheKey },
allTargetingData,
adserverTargeting,
optCustParams,
);
return encodeURIComponent(formatQS(customParams));
events.emit(CONSTANTS.EVENTS.SET_TARGETING, {[adUnit.code]: prebidTargetingSet});

// merge the prebid + publisher targeting sets
const publisherTargetingSet = deepAccess(options, 'params.cust_params');
const targetingSet = Object.assign({}, prebidTargetingSet, publisherTargetingSet);
return encodeURIComponent(formatQS(targetingSet));
}

registerVideoSupport('dfp', {
Expand Down
107 changes: 62 additions & 45 deletions modules/gumgumBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,17 @@ function isBidRequestValid (bid) {
params,
adUnitCode
} = bid;
const id = params.inScreen || params.inScreenPubID || params.inSlot || params.ICV || params.video || params.inVideo;
const legacyParamID = params.inScreen || params.inScreenPubID || params.inSlot || params.ICV || params.video || params.inVideo;
const id = legacyParamID || params.slot || params.native || params.zone || params.pubID;

if (invalidRequestIds[id]) {
utils.logWarn(`[GumGum] Please check the implementation for ${id} for the placement ${adUnitCode}`);
return false;
}

switch (true) {
case !!(params.zone): break;
case !!(params.pubId): break;
case !!(params.inScreen): break;
case !!(params.inScreenPubID): break;
case !!(params.inSlot): break;
Expand Down Expand Up @@ -217,8 +220,8 @@ function _getFloor (mediaTypes, bidfloor, bid) {
});

if (typeof floorInfo === 'object' &&
floorInfo.currency === 'USD' &&
!isNaN(parseFloat(floorInfo.floor))) {
floorInfo.currency === 'USD' &&
!isNaN(parseFloat(floorInfo.floor))) {
floor = Math.max(floor, parseFloat(floorInfo.floor));
}
}
Expand Down Expand Up @@ -255,6 +258,7 @@ function buildRequests (validBidRequests, bidderRequest) {
sizes = mediaTypes.banner.sizes;
} else if (mediaTypes.video) {
sizes = mediaTypes.video.playerSize;
data = _getVidParams(mediaTypes.video);
}

if (pageViewId) {
Expand All @@ -265,48 +269,27 @@ function buildRequests (validBidRequests, bidderRequest) {
data.fp = bidFloor;
}

if (params.inScreenPubID) {
data.pubId = params.inScreenPubID;
data.pi = 2;
}
if (params.inScreen) {
data.t = params.inScreen;
data.pi = 2;
}
if (params.inSlot) {
data.si = parseInt(params.inSlot, 10);
// check for sizes and type
if (params.sizes && Array.isArray(params.sizes)) {
const bf = params.sizes.reduce(function(r, i) {
// only push if it's an array of length 2
if (Array.isArray(i) && i.length === 2) {
r.push(`${i[0]}x${i[1]}`);
}
return r;
}, []);
data.bf = bf.toString();
if (params.zone) {
data.t = params.zone;
data.pi = 2; // inscreen
// override pi if the following is found
if (params.slot) {
data.si = parseInt(params.slot, 10);
data.pi = 3;
data.bf = sizes.reduce((acc, curSlotDim) => `${acc}${acc && ','}${curSlotDim[0]}x${curSlotDim[1]}`, '');
} else if (params.native) {
data.ni = parseInt(params.native, 10);
data.pi = 5;
} else if (mediaTypes.video) {
data.pi = mediaTypes.video.linearity === 1 ? 7 : 6; // video : invideo
}
data.pi = 3;
}
if (params.ICV) {
data.ni = parseInt(params.ICV, 10);
data.pi = 5;
}
if (params.videoPubID) {
data = Object.assign(data, _getVidParams(mediaTypes.video));
data.pubId = params.videoPubID;
data.pi = 7;
}
if (params.video) {
data = Object.assign(data, _getVidParams(mediaTypes.video));
data.t = params.video;
data.pi = 7;
}
if (params.inVideo) {
data = Object.assign(data, _getVidParams(mediaTypes.video));
data.t = params.inVideo;
data.pi = 6;
} else if (params.pubId) {
data.pubId = params.pubId
data.pi = mediaTypes.video ? 7 : 2; // video : inscreen
} else { // legacy params
data = { ...data, ...handleLegacyParams(params, sizes) }
}

if (gdprConsent) {
data.gdprApplies = gdprConsent.gdprApplies ? 1 : 0;
}
Expand Down Expand Up @@ -335,6 +318,40 @@ function buildRequests (validBidRequests, bidderRequest) {
return bids;
}

function handleLegacyParams (params, sizes) {
const data = {};
if (params.inScreenPubID) {
data.pubId = params.inScreenPubID;
data.pi = 2;
}
if (params.inScreen) {
data.t = params.inScreen;
data.pi = 2;
}
if (params.inSlot) {
data.si = parseInt(params.inSlot, 10);
data.pi = 3;
data.bf = sizes.reduce((acc, curSlotDim) => `${acc}${acc && ','}${curSlotDim[0]}x${curSlotDim[1]}`, '');
}
if (params.ICV) {
data.ni = parseInt(params.ICV, 10);
data.pi = 5;
}
if (params.videoPubID) {
data.pubId = params.videoPubID;
data.pi = 7;
}
if (params.video) {
data.t = params.video;
data.pi = 7;
}
if (params.inVideo) {
data.t = params.inVideo;
data.pi = 6;
}
return data;
}

/**
* Unpack the response from the server into a list of bids.
*
Expand All @@ -347,7 +364,7 @@ function interpretResponse (serverResponse, bidRequest) {

if (!serverResponseBody || serverResponseBody.err) {
const data = bidRequest.data || {}
const id = data.t || data.si || data.ni || data.pubId;
const id = data.si || data.ni || data.t || data.pubId;
const delayTime = serverResponseBody ? serverResponseBody.err.drt : DELAY_REQUEST_TIME;
invalidRequestIds[id] = { productId: data.pi, timestamp: new Date().getTime() };

Expand Down Expand Up @@ -403,7 +420,7 @@ function interpretResponse (serverResponse, bidRequest) {
bidResponses.push({
// dealId: DEAL_ID,
// referrer: REFERER,
...(product === 7 && { vastXml: markup }),
...(product === 7 && { vastXml: markup, mediaType: VIDEO }),
ad: wrapper ? getWrapperCode(wrapper, Object.assign({}, serverResponseBody, { bidRequest })) : markup,
...(product === 6 && {ad: markup}),
cpm: isTestUnit ? 0.1 : cpm,
Expand Down
114 changes: 114 additions & 0 deletions modules/instreamTracking.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { config } from '../src/config.js';
import { auctionManager } from '../src/auctionManager.js';
import { INSTREAM } from '../src/video.js';
import * as events from '../src/events.js';
import * as utils from '../src/utils.js';
import { BID_STATUS, EVENTS, TARGETING_KEYS } from '../src/constants.json';

const {CACHE_ID, UUID} = TARGETING_KEYS;
const {BID_WON, AUCTION_END} = EVENTS;
const {RENDERED} = BID_STATUS;

const INSTREAM_TRACKING_DEFAULT_CONFIG = {
enabled: false,
maxWindow: 1000 * 60, // the time in ms after which polling for instream delivery stops
pollingFreq: 500 // the frequency of polling
};

// Set instreamTracking default values
config.setDefaults({
'instreamTracking': utils.deepClone(INSTREAM_TRACKING_DEFAULT_CONFIG)
});

const whitelistedResources = /video|fetch|xmlhttprequest|other/;

/**
* Here the idea is
* find all network entries via performance.getEntriesByType()
* filter it by video cache key in the url
* and exclude the ad server urls so that we dont match twice
* eg:
* dfp ads call: https://securepubads.g.doubleclick.net/gampad/ads?...hb_cache_id%3D55e85cd3-6ea4-4469-b890-84241816b131%26...
* prebid cache url: https://prebid.adnxs.com/pbc/v1/cache?uuid=55e85cd3-6ea4-4469-b890-84241816b131
*
* if the entry exists, emit the BID_WON
*
* Note: this is a workaround till a better approach is engineered.
*
* @param {Array<AdUnit>} adUnits
* @param {Array<Bid>} bidsReceived
* @param {Array<BidRequest>} bidderRequests
*
* @return {boolean} returns TRUE if tracking started
*/
export function trackInstreamDeliveredImpressions({adUnits, bidsReceived, bidderRequests}) {
const instreamTrackingConfig = config.getConfig('instreamTracking') || {};
// check if instreamTracking is enabled and performance api is available
if (!instreamTrackingConfig.enabled || !window.performance || !window.performance.getEntriesByType) {
return false;
}

// filter for video bids
const instreamBids = bidsReceived.filter(bid => {
const bidderRequest = utils.getBidRequest(bid.requestId, bidderRequests);
return bidderRequest && utils.deepAccess(bidderRequest, 'mediaTypes.video.context') === INSTREAM && bid.videoCacheKey;
});
if (!instreamBids.length) {
return false;
}

// find unique instream ad units
const instreamAdUnitMap = {};
adUnits.forEach(adUnit => {
if (!instreamAdUnitMap[adUnit.code] && utils.deepAccess(adUnit, 'mediaTypes.video.context') === INSTREAM) {
instreamAdUnitMap[adUnit.code] = true;
}
});
const instreamAdUnitsCount = Object.keys(instreamAdUnitMap).length;

const start = Date.now();
const {maxWindow, pollingFreq, urlPattern} = instreamTrackingConfig;

let instreamWinningBidsCount = 0;
let lastRead = 0; // offset for performance.getEntriesByType

function poll() {
// get network entries using the last read offset
const entries = window.performance.getEntriesByType('resource').splice(lastRead);
for (const resource of entries) {
const url = resource.name;
// check if the resource is of whitelisted resource to avoid checking img or css or script urls
if (!whitelistedResources.test(resource.initiatorType)) {
continue;
}

instreamBids.forEach((bid) => {
// match the video cache key excluding ad server call
const matches = !(url.indexOf(CACHE_ID) !== -1 || url.indexOf(UUID) !== -1) && url.indexOf(bid.videoCacheKey) !== -1;
if (urlPattern && urlPattern instanceof RegExp && !urlPattern.test(url)) {
return;
}
if (matches && bid.status !== RENDERED) {
// video found
instreamWinningBidsCount++;
auctionManager.addWinningBid(bid);
events.emit(BID_WON, bid);
}
});
}
// update offset
lastRead += entries.length;

const timeElapsed = Date.now() - start;
if (timeElapsed < maxWindow && instreamWinningBidsCount < instreamAdUnitsCount) {
setTimeout(poll, pollingFreq);
}
}

// start polling for network entries
setTimeout(poll, pollingFreq);

return true;
}

events.on(AUCTION_END, trackInstreamDeliveredImpressions)
4 changes: 2 additions & 2 deletions modules/logicadBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER} from '../src/mediaTypes.js';
import {BANNER, NATIVE} from '../src/mediaTypes.js';

const BIDDER_CODE = 'logicad';
const ENDPOINT_URL = 'https://pb.ladsp.com/adrequest/prebid';

export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER],
supportedMediaTypes: [BANNER, NATIVE],
isBidRequestValid: function (bid) {
return !!(bid.params && bid.params.tid);
},
Expand Down
Loading

0 comments on commit 86eab24

Please sign in to comment.