Skip to content

Commit

Permalink
Improve Digital adapter: adding Extend mode (prebid#8399)
Browse files Browse the repository at this point in the history
Co-authored-by: iosfaisal <100519197+iosfaisal@users.noreply.github.com>
Co-authored-by: Jozef Bartek <j.bartek@improvedigital.com>
Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com>

Co-authored-by: Faisal Islam <93644923+faisalvs@users.noreply.github.com>
Co-authored-by: iosfaisal <100519197+iosfaisal@users.noreply.github.com>
  • Loading branch information
3 people authored and renebaudisch committed Jun 28, 2022
1 parent 539bbf8 commit 64e6012
Show file tree
Hide file tree
Showing 2 changed files with 389 additions and 115 deletions.
143 changes: 100 additions & 43 deletions modules/improvedigitalBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@ import {Renderer} from '../src/Renderer.js';
import {createEidsArray} from './userId/eids.js';

const BIDDER_CODE = 'improvedigital';
const REQUEST_URL = 'https://ad.360yield.com/pb';
const CREATIVE_TTL = 300;

const AD_SERVER_URL = 'https://ad.360yield.com/pb';
const EXTEND_URL = 'https://pbs.360yield.com/openrtb2/auction';
const IFRAME_SYNC_URL = 'https://hb.360yield.com/prebid-universal-creative/load-cookie.html';

const VIDEO_PARAMS = {
DEFAULT_MIMES: ['video/mp4'],
SUPPORTED_PROPERTIES: ['mimes', 'minduration', 'maxduration', 'protocols', 'w', 'h', 'startdelay', 'placement', 'linearity', 'skip', 'skipmin',
Expand Down Expand Up @@ -57,6 +60,7 @@ export const spec = {
gvlid: 253,
aliases: ['id'],
supportedMediaTypes: [BANNER, NATIVE, VIDEO],
syncStore: { extendMode: false, placementId: null },

/**
* Determines whether or not the given bid request is valid.
Expand All @@ -77,7 +81,6 @@ export const spec = {
*/
buildRequests(bidRequests, bidderRequest) {
const request = {
id: getUniqueIdentifierStr(),
cur: [config.getConfig('currency.adServerCurrency') || 'USD'],
ext: {
improvedigital: {
Expand All @@ -100,15 +103,15 @@ export const spec = {
// Coppa
const coppa = config.getConfig('coppa');
if (typeof coppa === 'boolean') {
deepSetValue(request, 'regs.coppa', ID_UTIL.toBit(coppa));
deepSetValue(request, 'regs.coppa', Number(coppa));
}

if (bidderRequest) {
// GDPR
const gdprConsent = deepAccess(bidderRequest, 'gdprConsent')
if (gdprConsent) {
if (typeof gdprConsent.gdprApplies === 'boolean') {
deepSetValue(request, 'regs.ext.gdpr', ID_UTIL.toBit(gdprConsent.gdprApplies));
deepSetValue(request, 'regs.ext.gdpr', Number(gdprConsent.gdprApplies));
}
deepSetValue(request, 'user.ext.consent', gdprConsent.consentString);

Expand Down Expand Up @@ -144,6 +147,9 @@ export const spec = {
deepSetValue(request, 'source.ext.schain', bidRequest0.schain);
deepSetValue(request, 'source.tid', bidRequest0.transactionId);

// Save a placement id to send it to the ad server when fetching the user syncs
this.syncStore.placementId = this.syncStore.placementId || bidRequest0.params.placementId;

if (bidRequest0.userId) {
const eids = createEidsArray(bidRequest0.userId);
deepSetValue(request, 'user.ext.eids', eids.length ? eids : undefined);
Expand Down Expand Up @@ -174,7 +180,7 @@ export const spec = {
return;
}
const bidRequest = getBidRequest(bidObject.impid, [bidderRequest]);
const idExt = deepAccess(bidObject, `ext.${BIDDER_CODE}`);
const idExt = deepAccess(bidObject, `ext.${BIDDER_CODE}`, {});

const bid = {
requestId: bidObject.impid,
Expand Down Expand Up @@ -210,57 +216,100 @@ export const spec = {
* @param {ServerResponse[]} serverResponses List of server's responses.
* @return {UserSync[]} The user syncs which should be dropped.
*/
getUserSyncs(syncOptions, serverResponses) {
if (syncOptions.pixelEnabled) {
const syncs = [];
getUserSyncs(syncOptions, serverResponses, gdprConsent, uspConsent) {
if (config.getConfig('coppa') === true || !ID_UTIL.hasPurpose1Consent(gdprConsent)) {
return [];
}

const syncs = [];
if ((this.syncStore.extendMode || !syncOptions.pixelEnabled) && syncOptions.iframeEnabled) {
const { gdprApplies, consentString } = gdprConsent || {};
syncs.push({
type: 'iframe',
url: IFRAME_SYNC_URL +
`?placement_id=${this.syncStore.placementId}` +
(this.syncStore.extendMode ? '&pbs=1' : '') +
(typeof gdprApplies === 'boolean' ? `&gdpr=${Number(gdprApplies)}` : '') +
(consentString ? `&gdpr_consent=${consentString}` : '') +
(uspConsent ? `&us_privacy=${encodeURIComponent(uspConsent)}` : '')
});
} else if (syncOptions.pixelEnabled) {
serverResponses.forEach(response => {
const syncArr = deepAccess(response, `body.ext.${BIDDER_CODE}.sync`, []);
syncArr.forEach(syncElement => {
if (syncs.indexOf(syncElement) === -1) {
syncs.push(syncElement);
syncArr.forEach(url => {
if (!syncs.some(sync => sync.url === url)) {
syncs.push({ type: 'image', url });
}
});
});
return syncs.map(sync => ({ type: 'image', url: sync }));
}
return [];

return syncs;
}
};

registerBidder(spec);

const ID_REQUEST = {
buildServerRequests(requestObject, bidRequests, bidderRequest) {
buildServerRequests(basicRequest, bidRequests, bidderRequest) {
const globalExtendMode = config.getConfig('improvedigital.extend') === true;
const requests = [];
if (config.getConfig('improvedigital.singleRequest') === true) {
requestObject.imp = bidRequests.map((bidRequest) => this.buildImp(bidRequest));
requests[0] = this.formatRequest(requestObject, bidderRequest);
} else {
bidRequests.map((bidRequest) => {
const request = deepClone(requestObject);
request.id = bidRequest.bidId || getUniqueIdentifierStr();
request.imp = [this.buildImp(bidRequest)];
deepSetValue(request, 'source.tid', bidRequest.transactionId);
requests.push(this.formatRequest(request, bidderRequest));
});
const singleRequestMode = config.getConfig('improvedigital.singleRequest') === true;

const extendImps = [];
const adServerImps = [];

function formatRequest(imps, transactionId, extendMode) {
const request = deepClone(basicRequest);
request.imp = imps;
request.id = getUniqueIdentifierStr();
if (transactionId) {
deepSetValue(request, 'source.tid', transactionId);
}
return {
method: 'POST',
url: extendMode ? EXTEND_URL : AD_SERVER_URL,
data: JSON.stringify(request),
bidderRequest
}
};

bidRequests.map((bidRequest) => {
const extendModeEnabled = this.isExtendModeEnabled(globalExtendMode, bidRequest.params);
const imp = this.buildImp(bidRequest, extendModeEnabled);
if (singleRequestMode) {
extendModeEnabled ? extendImps.push(imp) : adServerImps.push(imp);
} else {
requests.push(formatRequest([imp], bidRequest.transactionId, extendModeEnabled));
}
});

if (!singleRequestMode) {
return requests;
}
// In the single request mode, split imps between those going to the ad server and those going to extend server
if (extendImps.length) {
requests.push(formatRequest(extendImps, null, true));
}
if (adServerImps.length) {
requests.push(formatRequest(adServerImps, null, false));
}

return requests;
},

formatRequest(request, bidderRequest) {
return {
method: 'POST',
url: REQUEST_URL,
data: JSON.stringify(request),
bidderRequest
isExtendModeEnabled(globalExtendMode, bidParams) {
const extendMode = typeof bidParams.extend === 'boolean' ? bidParams.extend : globalExtendMode;
if (extendMode && !spec.syncStore.extendMode) {
spec.syncStore.extendMode = true;
}
return extendMode;
},

buildImp(bidRequest) {
buildImp(bidRequest, extendMode) {
const imp = {
id: getBidIdParameter('bidId', bidRequest) || getUniqueIdentifierStr(),
secure: ID_UTIL.toBit(window.location.protocol === 'https:'),
secure: Number(window.location.protocol === 'https:'),
};

// Floor
Expand All @@ -271,15 +320,19 @@ const ID_REQUEST = {
deepSetValue(imp, 'bidfloorcur', bidFloorCur ? bidFloorCur.toUpperCase() : undefined);
}

const bidderParamsPath = extendMode ? 'ext.prebid.bidder.improvedigital' : 'ext.bidder';
const placementId = getBidIdParameter('placementId', bidRequest.params);
if (placementId) {
deepSetValue(imp, 'ext.bidder.placementId', placementId);
deepSetValue(imp, `${bidderParamsPath}.placementId`, placementId);
if (extendMode) {
deepSetValue(imp, 'ext.prebid.storedrequest.id', '' + placementId);
}
} else {
deepSetValue(imp, 'ext.bidder.publisherId', getBidIdParameter('publisherId', bidRequest.params));
deepSetValue(imp, 'ext.bidder.placementKey', getBidIdParameter('placementKey', bidRequest.params));
deepSetValue(imp, `${bidderParamsPath}.publisherId`, getBidIdParameter('publisherId', bidRequest.params));
deepSetValue(imp, `${bidderParamsPath}.placementKey`, getBidIdParameter('placementKey', bidRequest.params));
}

deepSetValue(imp, 'ext.bidder.keyValues', getBidIdParameter('keyValues', bidRequest.params) || undefined);
deepSetValue(imp, `${bidderParamsPath}.keyValues`, getBidIdParameter('keyValues', bidRequest.params) || undefined);

// Adding GPID
const gpid = deepAccess(bidRequest, 'ortb2Imp.ext.gpid') ||
Expand Down Expand Up @@ -374,7 +427,7 @@ const ID_REQUEST = {
const assetParams = nativeParams[i];
const asset = {
id: assetOrtbParams.id,
required: ID_UTIL.toBit(assetParams.required),
required: Number(assetParams.required),
};
switch (assetOrtbParams.assetType) {
case NATIVE_DATA.ASSET_TYPES.TITLE:
Expand Down Expand Up @@ -458,9 +511,10 @@ const ID_RESPONSE = {
this.buildNativeAd(bid, bidRequest, bidResponse)
}
} else {
if (bidResponse.adm.search(/^<vast/i) === 0) {
// Detect media type for multi-format response
if (bidResponse.adm.search(/^(<\?xml|<vast)/i) !== -1) {
this.buildVideoAd(bid, bidRequest, bidResponse);
} else if (bidResponse.adm.indexOf('{') === 0) {
} else if (bidResponse.adm[0] === '{') {
this.buildNativeAd(bid, bidRequest, bidResponse);
} else {
this.buildBannerAd(bid, bidRequest, bidResponse);
Expand Down Expand Up @@ -604,7 +658,10 @@ const ID_RAZR = {
};

const ID_UTIL = {
toBit(val) {
return val ? 1 : 0;
},
hasPurpose1Consent(gdprConsent) {
if (gdprConsent && gdprConsent.gdprApplies && gdprConsent.apiVersion === 2) {
return (deepAccess(gdprConsent, 'vendorData.purpose.consents.1') === true);
}
return true;
}
};
Loading

0 comments on commit 64e6012

Please sign in to comment.