Skip to content

Commit

Permalink
Insticator Bid Adaptor: add support for different privacy platforms, …
Browse files Browse the repository at this point in the history
…ortb site first party data, fix video validation bug (prebid#11279)

* add support for:
- dsa
- us_privacy/ccpa
- gpp
- gppSid

* video validation fix

* support first part site details

* support user data from bidder, and update server endpoint for alternative bidder configs

* - enhance user object with ext and consent
- remove video param validation

* add test case for more than one privacy platforms

* update user.ext.consent

* update coppa and support video context
  • Loading branch information
shubhamc-ins authored Apr 15, 2024
1 parent 5ad4877 commit 2fd1b74
Show file tree
Hide file tree
Showing 2 changed files with 412 additions and 49 deletions.
164 changes: 137 additions & 27 deletions modules/insticatorBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {config} from '../src/config.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {registerBidder} from '../src/adapters/bidderFactory.js';
import {deepAccess, generateUUID, logError, isArray, isInteger, isArrayOfNums} from '../src/utils.js';
import {deepAccess, generateUUID, logError, isArray, isInteger, isArrayOfNums, deepSetValue} from '../src/utils.js';
import {getStorageManager} from '../src/storageManager.js';
import {find} from '../src/polyfill.js';

Expand All @@ -12,35 +12,37 @@ const USER_ID_COOKIE_EXP = 2592000000; // 30 days
const BID_TTL = 300; // 5 minutes
const GVLID = 910;

const isSubarray = (arr, target) => {
if (!isArrayOfNums(arr) || arr.length === 0) {
return false;
}
const targetSet = new Set(target);
return arr.every(el => targetSet.has(el));
};

export const OPTIONAL_VIDEO_PARAMS = {
'minduration': (value) => isInteger(value),
'maxduration': (value) => isInteger(value),
'protocols': (value) => isSubarray(value, [2, 3, 5, 6, 7, 8]), // protocols values supported by Inticator, according to the OpenRTB spec
'protocols': (value) => isArrayOfNums(value), // protocols values supported by Inticator, according to the OpenRTB spec
'startdelay': (value) => isInteger(value),
'linearity': (value) => isInteger(value) && [1].includes(value),
'skip': (value) => isInteger(value) && [1, 0].includes(value),
'skipmin': (value) => isInteger(value),
'skipafter': (value) => isInteger(value),
'sequence': (value) => isInteger(value),
'battr': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]),
'battr': (value) => isArrayOfNums(value),
'maxextended': (value) => isInteger(value),
'minbitrate': (value) => isInteger(value),
'maxbitrate': (value) => isInteger(value),
'playbackmethod': (value) => isSubarray(value, [1, 2, 3, 4]),
'playbackmethod': (value) => isArrayOfNums(value),
'playbackend': (value) => isInteger(value) && [1, 2, 3].includes(value),
'delivery': (value) => isSubarray(value, [1, 2, 3]),
'delivery': (value) => isArrayOfNums(value),
'pos': (value) => isInteger(value) && [0, 1, 2, 3, 4, 5, 6, 7].includes(value),
'api': (value) => isSubarray(value, [1, 2, 3, 4, 5, 6, 7]),
'api': (value) => isArrayOfNums(value),
};

const ORTB_SITE_FIRST_PARTY_DATA = {
'cat': v => Array.isArray(v) && v.every(c => typeof c === 'string'),
'sectioncat': v => Array.isArray(v) && v.every(c => typeof c === 'string'),
'pagecat': v => Array.isArray(v) && v.every(c => typeof c === 'string'),
'search': v => typeof v === 'string',
'mobile': v => isInteger(),
'content': v => typeof v === 'object',
'keywords': v => typeof v === 'string',
}

export const storage = getStorageManager({bidderCode: BIDDER_CODE});

config.setDefaults({
Expand Down Expand Up @@ -103,6 +105,7 @@ function buildVideo(bidRequest) {
const placement = deepAccess(bidRequest, 'mediaTypes.video.placement') || 3;
const plcmt = deepAccess(bidRequest, 'mediaTypes.video.plcmt') || undefined;
const playerSize = deepAccess(bidRequest, 'mediaTypes.video.playerSize');
const context = deepAccess(bidRequest, 'mediaTypes.video.context');

if (!w && playerSize) {
if (Array.isArray(playerSize[0])) {
Expand All @@ -121,17 +124,26 @@ function buildVideo(bidRequest) {

const bidRequestVideo = deepAccess(bidRequest, 'mediaTypes.video');
const videoBidderParams = deepAccess(bidRequest, 'params.video', {});

let optionalParams = {};
for (const param in OPTIONAL_VIDEO_PARAMS) {
if (bidRequestVideo[param]) {
if (bidRequestVideo[param] && OPTIONAL_VIDEO_PARAMS[param](bidRequestVideo[param])) {
optionalParams[param] = bidRequestVideo[param];
}
// remove invalid optional params from bidder specific overrides
if (videoBidderParams[param] && !OPTIONAL_VIDEO_PARAMS[param](videoBidderParams[param])) {
delete videoBidderParams[param];
}
}

if (plcmt) {
optionalParams['plcmt'] = plcmt;
}

if (context !== undefined) {
optionalParams['context'] = context;
}

let videoObj = {
placement,
mimes,
Expand Down Expand Up @@ -190,31 +202,102 @@ function buildDevice(bidRequest) {
return device;
}

function _getCoppa(bidderRequest) {
const coppa = deepAccess(bidderRequest, 'ortb2.regs.coppa');

// If coppa is defined in the request, use it
if (coppa !== undefined) {
return coppa;
}
return config.getConfig('coppa') === true ? 1 : 0;
}

function _getGppConsent(bidderRequest) {
let gpp = deepAccess(bidderRequest, 'gppConsent.gppString')
let gppSid = deepAccess(bidderRequest, 'gppConsent.applicableSections')

if (!gpp || !gppSid) {
gpp = deepAccess(bidderRequest, 'ortb2.regs.gpp', '')
gppSid = deepAccess(bidderRequest, 'ortb2.regs.gpp_sid', [])
}
return { gpp, gppSid }
}

function _getUspConsent(bidderRequest) {
return (deepAccess(bidderRequest, 'uspConsent')) ? { uspConsent: bidderRequest.uspConsent } : false;
}

function buildRegs(bidderRequest) {
let regs = {
ext: {},
};
if (bidderRequest.gdprConsent) {
return {
ext: {
gdpr: bidderRequest.gdprConsent.gdprApplies ? 1 : 0,
gdprConsentString: bidderRequest.gdprConsent.consentString,
},
};
regs.ext.gdpr = bidderRequest.gdprConsent.gdprApplies ? 1 : 0;
regs.ext.gdprConsentString = bidderRequest.gdprConsent.consentString;
}

regs.coppa = _getCoppa(bidderRequest);

const { gpp, gppSid } = _getGppConsent(bidderRequest);

if (gpp) {
regs.ext.gpp = gpp;
}

if (gppSid) {
regs.ext.gppSid = gppSid;
}

const usp = _getUspConsent(bidderRequest);

if (usp) {
regs.ext.us_privacy = usp.uspConsent;
regs.ext.ccpa = usp.uspConsent
}

const dsa = deepAccess(bidderRequest, 'ortb2.regs.ext.dsa');
if (dsa) {
regs.ext.dsa = dsa;
}

return {};
return regs;
}

function buildUser(bid) {
const userId = getUserId() || generateUUID();
const yob = deepAccess(bid, 'params.user.yob')
const gender = deepAccess(bid, 'params.user.gender')
const keywords = deepAccess(bid, 'params.user.keywords')
const data = deepAccess(bid, 'params.user.data')
const ext = deepAccess(bid, 'params.user.ext')

setUserId(userId);

return {
const userData = {
id: userId,
yob,
gender,
};
}

if (yob) {
userData.yob = yob;
}

if (gender) {
userData.gender = gender;
}

if (keywords) {
userData.keywords = keywords;
}

if (data) {
userData.data = data;
}

if (ext) {
userData.ext = ext;
}

return userData
}

function extractSchain(bids, requestId) {
Expand Down Expand Up @@ -283,6 +366,20 @@ function buildRequest(validBidRequests, bidderRequest) {
req.user.ext = { eids };
}

const ortb2SiteData = deepAccess(bidderRequest, 'ortb2.site');
if (ortb2SiteData) {
for (const key in ORTB_SITE_FIRST_PARTY_DATA) {
const value = ortb2SiteData[key];
if (value && ORTB_SITE_FIRST_PARTY_DATA[key](value)) {
req.site[key] = value;
}
}
}

if (bidderRequest.gdprConsent) {
deepSetValue(req, 'user.ext.consent', bidderRequest.gdprConsent.consentString);
}

return req;
}

Expand Down Expand Up @@ -326,6 +423,13 @@ function buildBid(bid, bidderRequest) {
bidResponse.vastUrl = 'data:text/xml;charset=utf-8;base64,' + window.btoa(bidResponse.vastXml.replace(/\\"/g, '"'));
}

if (bid.ext && bid.ext.dsa) {
bidResponse.ext = {
...bidResponse.ext,
dsa: bid.ext.dsa,
}
}

return bidResponse;
}

Expand Down Expand Up @@ -453,7 +557,6 @@ function validateVideo(bid) {
if (video[param]) {
if (!OPTIONAL_VIDEO_PARAMS[param](video[param])) {
logError(`insticator: video ${param} is invalid or not supported by insticator`);
return false
}
}
}
Expand Down Expand Up @@ -485,6 +588,13 @@ export const spec = {
let endpointUrl = config.getConfig('insticator.endpointUrl') || ENDPOINT;
endpointUrl = endpointUrl.replace(/^http:/, 'https:');

// Use the first bid request's bid_request_url if it exists ( for updating server url)
if (validBidRequests.length > 0) {
if (deepAccess(validBidRequests[0], 'params.bid_endpoint_request_url')) {
endpointUrl = deepAccess(validBidRequests[0], 'params.bid_endpoint_request_url').replace(/^http:/, 'https:');
}
}

if (validBidRequests.length > 0) {
requests.push({
method: 'POST',
Expand Down
Loading

0 comments on commit 2fd1b74

Please sign in to comment.