Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

even though this is under site in prebid js, we can use this for both site & content in bid requests on the exchange right?

Copy link
Author

@shubhamc-ins shubhamc-ins Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, correct!

Copy link
Author

@shubhamc-ins shubhamc-ins Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also we are not doing any validation on content object from here. We can do that in exchange. @rohanInsticator

}

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) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check how rubicon adapter works, they pass consent string regardless of gdpr consent object is true or not. We should do that too since the consentString can be there for some Non-EU countries too. Please update the same for user object too

Copy link
Author

@shubhamc-ins shubhamc-ins Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, if you see the latest code of rubicon.
Screenshot 2024-03-26 at 11 28 18 AM

They do check for gdprConsent and have removed the user.ext.consent as regs.ext.gdprConsentString servers the same purpose.
@rohanInsticator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User consent is there in rubicon, not sure what you were checking

utils.deepSetValue(data, 'user.ext.consent', bidderRequest.gdprConsent.consentString);

Copy link
Author

@shubhamc-ins shubhamc-ins Mar 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rohanInsticator you are checking our repository, it's master is not synced with Prebid's official master branch.

Check this https://github.com/prebid/Prebid.js/blob/master/modules/rubiconBidAdapter.js

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was looking at PrebidJS repo only in my intellij. I cannot pull the latest on master over there somehow. Even if rubicon has removed it, they must be appending it on the Server Side as IAB said it's good to have that field inside User Object too.

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