-
Notifications
You must be signed in to change notification settings - Fork 0
Add support for Privacy Regulations | Fix video param validation #79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: prebid-master
Are you sure you want to change the base?
Changes from all commits
6abd25e
89de802
78df3ed
9a34ff6
7ad51e9
06858ed
df112dc
e38b2f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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'; | ||
|
|
||
|
|
@@ -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({ | ||
|
|
@@ -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])) { | ||
|
|
@@ -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, | ||
|
|
@@ -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; | ||
shubhamc-ins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| function _getGppConsent(bidderRequest) { | ||
| let gpp = deepAccess(bidderRequest, 'gppConsent.gppString') | ||
shubhamc-ins marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, if you see the latest code of rubicon. They do check for There was a problem hiding this comment. Choose a reason for hiding this commentThe 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);
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) { | ||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
@@ -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; | ||
| } | ||
|
|
||
|
|
@@ -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 | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -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', | ||
|
|
||

There was a problem hiding this comment.
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?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, correct!
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
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
contentobject from here. We can do that in exchange. @rohanInsticator