Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
GLStephen committed Dec 4, 2020
2 parents 029842f + 8107da6 commit 59ef88d
Show file tree
Hide file tree
Showing 27 changed files with 752 additions and 95 deletions.
2 changes: 1 addition & 1 deletion .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
"safari >=8",
"edge >= 14",
"ff >= 57",
"ie >= 10",
"ie >= 11",
"ios >= 8"
]
}
Expand Down
163 changes: 146 additions & 17 deletions modules/adagioBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ import find from 'core-js-pure/features/array/find.js';
import * as utils from '../src/utils.js';
import { config } from '../src/config.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { loadExternalScript } from '../src/adloader.js'
import { loadExternalScript } from '../src/adloader.js';
import JSEncrypt from 'jsencrypt/bin/jsencrypt.js';
import sha256 from 'crypto-js/sha256.js';
import { getStorageManager } from '../src/storageManager.js';
import { getRefererInfo } from '../src/refererDetection.js';
import { createEidsArray } from './userId/eids.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';
import { Renderer } from '../src/Renderer.js';
import { OUTSTREAM } from '../src/video.js';

export const BIDDER_CODE = 'adagio';
export const LOG_PREFIX = 'Adagio:';
export const VERSION = '2.5.0';
export const VERSION = '2.6.0';
export const FEATURES_VERSION = '1';
export const ENDPOINT = 'https://mp.4dex.io/prebid';
export const SUPPORTED_MEDIA_TYPES = ['banner'];
export const SUPPORTED_MEDIA_TYPES = [BANNER, VIDEO];
export const ADAGIO_TAG_URL = 'https://script.4dex.io/localstore.js';
export const ADAGIO_LOCALSTORAGE_KEY = 'adagioScript';
export const GVLID = 617;
export const storage = getStorageManager(GVLID, 'adagio');
export const RENDERER_URL = 'https://script.4dex.io/outstream-player.js';

export const ADAGIO_PUBKEY = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9el0+OEn6fvEh1RdVHQu4cnT0
Expand All @@ -27,6 +31,35 @@ t0b0lsHN+W4n9kitS/DZ/xnxWK/9vxhv0ZtL1LL/rwR5Mup7rmJbNtDoNBw4TIGj
pV6EP3MTLosuUEpLaQIDAQAB
-----END PUBLIC KEY-----`;

// This provide a whitelist and a basic validation
// of OpenRTB 2.5 options used by the Adagio SSP.
// https://www.iab.com/wp-content/uploads/2016/03/OpenRTB-API-Specification-Version-2-5-FINAL.pdf
export const ORTB_VIDEO_PARAMS = {
'mimes': (value) => Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string'),
'minduration': (value) => utils.isInteger(value),
'maxduration': (value) => utils.isInteger(value),
'protocols': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].indexOf(v) !== -1),
'w': (value) => utils.isInteger(value),
'h': (value) => utils.isInteger(value),
'startdelay': (value) => utils.isInteger(value),
'placement': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5].indexOf(v) !== -1),
'linearity': (value) => [1, 2].indexOf(value) !== -1,
'skip': (value) => [0, 1].indexOf(value) !== -1,
'skipmin': (value) => utils.isInteger(value),
'skipafter': (value) => utils.isInteger(value),
'sequence': (value) => utils.isInteger(value),
'battr': (value) => Array.isArray(value) && value.every(v => Array.from({length: 17}, (_, i) => i + 1).indexOf(v) !== -1),
'maxextended': (value) => utils.isInteger(value),
'minbitrate': (value) => utils.isInteger(value),
'maxbitrate': (value) => utils.isInteger(value),
'boxingallowed': (value) => [0, 1].indexOf(value) !== -1,
'playbackmethod': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1),
'playbackend': (value) => [1, 2, 3].indexOf(value) !== -1,
'delivery': (value) => [1, 2, 3].indexOf(value) !== -1,
'pos': (value) => [0, 1, 2, 3, 4, 5, 6, 7].indexOf(value) !== -1,
'api': (value) => Array.isArray(value) && value.every(v => [1, 2, 3, 4, 5, 6].indexOf(v) !== -1)
};

let currentWindow;

export function adagioScriptFromLocalStorageCb(ls) {
Expand Down Expand Up @@ -64,7 +97,7 @@ export function adagioScriptFromLocalStorageCb(ls) {

export function getAdagioScript() {
storage.getDataFromLocalStorage(ADAGIO_LOCALSTORAGE_KEY, (ls) => {
internal.adagioScriptFromLocalStorageCb(ls)
internal.adagioScriptFromLocalStorageCb(ls);
});

storage.localStorageIsEnabled(isValid => {
Expand Down Expand Up @@ -335,7 +368,7 @@ function getOrAddAdagioAdUnit(adUnitCode) {
w.ADAGIO = w.ADAGIO || {};

if (w.ADAGIO.adUnits[adUnitCode]) {
return w.ADAGIO.adUnits[adUnitCode]
return w.ADAGIO.adUnits[adUnitCode];
}

return w.ADAGIO.adUnits[adUnitCode] = {};
Expand Down Expand Up @@ -435,7 +468,7 @@ function getElementFromTopWindow(element, currentWindow) {
};

function autoDetectAdUnitElementId(adUnitCode) {
const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode)
const autoDetectedAdUnit = utils.getGptSlotInfoForAdUnitCode(adUnitCode);
let adUnitElementId = null;

if (autoDetectedAdUnit && autoDetectedAdUnit.divId) {
Expand All @@ -450,16 +483,16 @@ function autoDetectEnvironment() {
let environment;
switch (device) {
case 2:
environment = 'desktop'
environment = 'desktop';
break;
case 4:
environment = 'mobile'
environment = 'mobile';
break;
case 5:
environment = 'tablet'
environment = 'tablet';
break;
};
return environment
return environment;
};

function getFeatures(bidRequest, bidderRequest) {
Expand Down Expand Up @@ -507,6 +540,21 @@ function getFeatures(bidRequest, bidderRequest) {
return features;
};

function isRendererPreferredFromPublisher(bidRequest) {
// renderer defined at adUnit level
const adUnitRenderer = utils.deepAccess(bidRequest, 'renderer');
const hasValidAdUnitRenderer = !!(adUnitRenderer && adUnitRenderer.url && adUnitRenderer.render);

// renderer defined at adUnit.mediaTypes level
const mediaTypeRenderer = utils.deepAccess(bidRequest, 'mediaTypes.video.renderer');
const hasValidMediaTypeRenderer = !!(mediaTypeRenderer && mediaTypeRenderer.url && mediaTypeRenderer.render);

return !!(
(hasValidAdUnitRenderer && !(adUnitRenderer.backupOnly === true)) ||
(hasValidMediaTypeRenderer && !(mediaTypeRenderer.backupOnly === true))
);
}

export const internal = {
enqueue,
getOrAddAdagioAdUnit,
Expand All @@ -521,7 +569,8 @@ export const internal = {
getRefererInfo,
adagioScriptFromLocalStorageCb,
getCurrentWindow,
canAccessTopWindow
canAccessTopWindow,
isRendererPreferredFromPublisher
};

function _getGdprConsent(bidderRequest) {
Expand All @@ -539,7 +588,7 @@ function _getGdprConsent(bidderRequest) {
const consent = {};

if (apiVersion !== undefined) {
consent.apiVersion = apiVersion
consent.apiVersion = apiVersion;
}

if (consentString !== undefined) {
Expand Down Expand Up @@ -575,10 +624,62 @@ function _getSchain(bidRequest) {

function _getEids(bidRequest) {
if (utils.deepAccess(bidRequest, 'userId')) {
return createEidsArray(bidRequest.userId)
return createEidsArray(bidRequest.userId);
}
}

function _buildVideoBidRequest(bidRequest) {
const videoAdUnitParams = utils.deepAccess(bidRequest, 'mediaTypes.video', {});
const videoBidderParams = utils.deepAccess(bidRequest, 'params.video', {});
const computedParams = {};

// Special case for playerSize.
// Eeach props will be overrided if they are defined in config.
if (Array.isArray(videoAdUnitParams.playerSize)) {
const tempSize = (Array.isArray(videoAdUnitParams.playerSize[0])) ? videoAdUnitParams.playerSize[0] : videoAdUnitParams.playerSize;
computedParams.w = tempSize[0];
computedParams.h = tempSize[1];
}

const videoParams = {
...computedParams,
...videoAdUnitParams,
...videoBidderParams
};

if (videoParams.context && videoParams.context === OUTSTREAM) {
bidRequest.mediaTypes.video.playerName = (internal.isRendererPreferredFromPublisher(bidRequest)) ? 'other' : 'adagio';

if (bidRequest.mediaTypes.video.playerName === 'other') {
utils.logWarn(`${LOG_PREFIX} renderer.backupOnly has not been set. Adagio recommends to use its own player to get expected behavior.`);
}
}

// Only whitelisted OpenRTB options need to be validated.
// Other options will still remain in the `mediaTypes.video` object
// sent in the ad-request, but will be ignored by the SSP.
Object.keys(ORTB_VIDEO_PARAMS).forEach(paramName => {
if (videoParams.hasOwnProperty(paramName)) {
if (ORTB_VIDEO_PARAMS[paramName](videoParams[paramName])) {
bidRequest.mediaTypes.video[paramName] = videoParams[paramName];
} else {
delete bidRequest.mediaTypes.video[paramName];
utils.logWarn(`${LOG_PREFIX} The OpenRTB video param ${paramName} has been skipped due to misformating. Please refer to OpenRTB 2.5 spec.`);
}
}
});
}

function _renderer(bid) {
bid.renderer.push(() => {
if (typeof window.ADAGIO.outstreamPlayer === 'function') {
window.ADAGIO.outstreamPlayer(bid);
} else {
utils.logError(`${LOG_PREFIX} Adagio outstream player is not defined`);
}
});
}

export const spec = {
code: BIDDER_CODE,
gvlid: GVLID,
Expand All @@ -600,7 +701,7 @@ export const spec = {
...params,
adUnitElementId,
environment
}
};

const debugData = () => ({
action: 'pb-dbg',
Expand Down Expand Up @@ -631,7 +732,7 @@ export const spec = {
// Store adUnits config.
// If an adUnitCode has already been stored, it will be replaced.
w.ADAGIO = w.ADAGIO || {};
w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode)
w.ADAGIO.pbjsAdUnits = w.ADAGIO.pbjsAdUnits.filter((adUnit) => adUnit.code !== adUnitCode);
w.ADAGIO.pbjsAdUnits.push({
code: adUnitCode,
mediaTypes: mediaTypes || {},
Expand Down Expand Up @@ -667,14 +768,19 @@ export const spec = {
const eids = _getEids(validBidRequests[0]) || [];
const adUnits = utils._map(validBidRequests, (bidRequest) => {
bidRequest.features = internal.getFeatures(bidRequest, bidderRequest);

if (utils.deepAccess(bidRequest, 'mediaTypes.video')) {
_buildVideoBidRequest(bidRequest);
}

return bidRequest;
});

// Group ad units by organizationId
const groupedAdUnits = adUnits.reduce((groupedAdUnits, adUnit) => {
adUnit.params.organizationId = adUnit.params.organizationId.toString();

groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || []
groupedAdUnits[adUnit.params.organizationId] = groupedAdUnits[adUnit.params.organizationId] || [];
groupedAdUnits[adUnit.params.organizationId].push(adUnit);

return groupedAdUnits;
Expand Down Expand Up @@ -709,7 +815,7 @@ export const spec = {
options: {
contentType: 'text/plain'
}
}
};
});

return requests;
Expand All @@ -730,7 +836,30 @@ export const spec = {
if (response.bids) {
response.bids.forEach(bidObj => {
const bidReq = (find(bidRequest.data.adUnits, bid => bid.bidId === bidObj.requestId));

if (bidReq) {
if (bidObj.mediaType === VIDEO) {
const mediaTypeContext = utils.deepAccess(bidReq, 'mediaTypes.video.context');
// Adagio SSP returns a `vastXml` only. No `vastUrl` nor `videoCacheKey`.
if (!bidObj.vastUrl && bidObj.vastXml) {
bidObj.vastUrl = 'data:text/xml;charset=utf-8;base64,' + btoa(bidObj.vastXml.replace(/\\"/g, '"'));
}

if (mediaTypeContext === OUTSTREAM) {
bidObj.renderer = Renderer.install({
id: bidObj.requestId,
adUnitCode: bidObj.adUnitCode,
url: bidObj.urlRenderer || RENDERER_URL,
config: {
...utils.deepAccess(bidReq, 'mediaTypes.video'),
...utils.deepAccess(bidObj, 'outstream', {})
}
});

bidObj.renderer.setRender(_renderer);
}
}

bidObj.site = bidReq.params.site;
bidObj.placement = bidReq.params.placement;
bidObj.pagetype = bidReq.params.pagetype;
Expand Down
58 changes: 52 additions & 6 deletions modules/adagioBidAdapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ Connects to Adagio demand source to fetch bids.
bids: [{
bidder: 'adagio', // Required
params: {
organizationId: '0', // Required - Organization ID provided by Adagio.
site: 'news-of-the-day', // Required - Site Name provided by Adagio.
placement: 'ban_atf', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'.
adUnitElementId: 'dfp_banniere_atf', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above.
organizationId: '1002', // Required - Organization ID provided by Adagio.
site: 'adagio-io', // Required - Site Name provided by Adagio.
placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'.
adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above.

// The following params are limited to 30 characters,
// and can only contain the following characters:
Expand All @@ -37,7 +37,54 @@ Connects to Adagio demand source to fetch bids.
environment: 'mobile', // Recommended. Environment where the page is displayed.
category: 'sport', // Recommended. Category of the content displayed in the page.
subcategory: 'handball', // Optional. Subcategory of the content displayed in the page.
postBid: false // Optional. Use it in case of Post-bid integration only.
postBid: false, // Optional. Use it in case of Post-bid integration only.
// Optional debug mode, used to get a bid response with expected cpm.
debug: {
enabled: true,
cpm: 3.00 // default to 1.00
}
}
}]
},
{
code: 'article_outstream',
mediaTypes: {
video: {
context: 'outstream',
playerSize: [640, 480],
mimes: ['video/mp4'],
skip: 1
// Other OpenRTB 2.5 video options…
}
},
bids: [{
bidder: 'adagio', // Required
params: {
organizationId: '1002', // Required - Organization ID provided by Adagio.
site: 'adagio-io', // Required - Site Name provided by Adagio.
placement: 'in_article', // Required. Refers to the placement of an adunit in a page. Must not contain any information about the type of device. Other example: `mpu_btf'.
adUnitElementId: 'article_outstream', // Required - AdUnit element id. Refers to the adunit id in a page. Usually equals to the adunit code above.

// The following params are limited to 30 characters,
// and can only contain the following characters:
// - alphanumeric (A-Z+a-z+0-9, case-insensitive)
// - dashes `-`
// - underscores `_`
// Also, each param can have at most 50 unique active values (case-insensitive).
pagetype: 'article', // Highly recommended. The pagetype describes what kind of content will be present in the page.
environment: 'mobile', // Recommended. Environment where the page is displayed.
category: 'sport', // Recommended. Category of the content displayed in the page.
subcategory: 'handball', // Optional. Subcategory of the content displayed in the page.
postBid: false, // Optional. Use it in case of Post-bid integration only.
video: {
skip: 0
// OpenRTB 2.5 video options defined here override ones defined in mediaTypes.
},
// Optional debug mode, used to get a bid response with expected cpm.
debug: {
enabled: true,
cpm: 3.00 // default to 1.00
}
}
}]
}
Expand Down Expand Up @@ -88,5 +135,4 @@ Connects to Adagio demand source to fetch bids.
]
}
}

```
2 changes: 1 addition & 1 deletion modules/adkernelBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const NATIVE_INDEX = NATIVE_MODEL.reduce((acc, val, idx) => {
export const spec = {

code: 'adkernel',
aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite'],
aliases: ['headbidding', 'adsolut', 'oftmediahb', 'audiencemedia', 'waardex_ak', 'roqoon', 'andbeyond', 'adbite', 'houseofpubs'],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],

/**
Expand Down
Loading

0 comments on commit 59ef88d

Please sign in to comment.