Skip to content

Commit

Permalink
Merge branch 'prebid-8' into fix-appnexus-segments
Browse files Browse the repository at this point in the history
  • Loading branch information
dgirardi committed Jun 12, 2023
2 parents 710afd1 + ded443f commit 736b20f
Show file tree
Hide file tree
Showing 40 changed files with 1,830 additions and 760 deletions.
3 changes: 2 additions & 1 deletion modules/.submodules.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"verizonMediaIdSystem",
"zeotapIdPlusIdSystem",
"adqueryIdSystem",
"gravitoIdSystem"
"gravitoIdSystem",
"freepassIdSystem"
],
"adpod": [
"freeWheelAdserverVideo",
Expand Down
76 changes: 26 additions & 50 deletions modules/1plusXRtdProvider.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {submodule} from '../src/hook.js';
import {config} from '../src/config.js';
import {ajax} from '../src/ajax.js';
import {deepAccess, deepSetValue, isArray, isNumber, logError, logMessage, mergeDeep} from '../src/utils.js';
import { submodule } from '../src/hook.js';
import { ajax } from '../src/ajax.js';
import {
logMessage, logError,
deepAccess, deepSetValue, mergeDeep,
isNumber, isArray,
} from '../src/utils.js';

// Constants
const REAL_TIME_MODULE = 'realTimeData';
Expand Down Expand Up @@ -146,7 +149,7 @@ const getTargetingDataFromPapi = (papiUrl) => {
* @param {string[]} topics Represents the topics of the page
* @returns {Object} Object describing the updates to make on bidder configs
*/
export const buildOrtb2Updates = ({ segments = [], topics = [] }, bidder) => {
export const buildOrtb2Updates = ({ segments = [], topics = [] }) => {
const userData = {
name: ORTB2_NAME,
segment: segments.map((segmentId) => ({ id: segmentId }))
Expand All @@ -163,74 +166,46 @@ export const buildOrtb2Updates = ({ segments = [], topics = [] }, bidder) => {
* Merges the targeting data with the existing config for bidder and updates
* @param {string} bidder Bidder for which to set config
* @param {Object} ortb2Updates Updates to be applied to bidder config
* @param {Object} bidderConfigs All current bidder configs
* @returns {Object} Updated bidder config
* @param {Object} biddersOrtb2 All current bidder configs
*/
export const updateBidderConfig = (bidder, ortb2Updates, bidderConfigs) => {
const { site, siteContentData, userData } = ortb2Updates;
const bidderConfigCopy = mergeDeep({}, bidderConfigs[bidder]);
export const updateBidderConfig = (bidder, ortb2Updates, biddersOrtb2) => {
const { siteContentData, userData } = ortb2Updates;
mergeDeep(biddersOrtb2, { [bidder]: {} });
const bidderConfig = deepAccess(biddersOrtb2, bidder);

if (site) {
// Legacy : cf. comment on buildOrtb2Updates first lines
const currentSite = deepAccess(bidderConfigCopy, 'ortb2.site');
const updatedSite = mergeDeep(currentSite, site);
deepSetValue(bidderConfigCopy, 'ortb2.site', updatedSite);
}

if (siteContentData) {
const siteDataPath = 'ortb2.site.content.data';
const currentSiteContentData = deepAccess(bidderConfigCopy, siteDataPath) || [];
{
const siteDataPath = 'site.content.data';
const currentSiteContentData = deepAccess(bidderConfig, siteDataPath) || [];
const updatedSiteContentData = [
...currentSiteContentData.filter(({ name }) => name != siteContentData.name),
siteContentData
];
deepSetValue(bidderConfigCopy, siteDataPath, updatedSiteContentData);
deepSetValue(bidderConfig, siteDataPath, updatedSiteContentData);
}

if (userData) {
const userDataPath = 'ortb2.user.data';
const currentUserData = deepAccess(bidderConfigCopy, userDataPath) || [];
{
const userDataPath = 'user.data';
const currentUserData = deepAccess(bidderConfig, userDataPath) || [];
const updatedUserData = [
...currentUserData.filter(({ name }) => name != userData.name),
userData
];
deepSetValue(bidderConfigCopy, userDataPath, updatedUserData);
deepSetValue(bidderConfig, userDataPath, updatedUserData);
}

return bidderConfigCopy;
};

const setAppnexusAudiences = (audiences) => {
config.setConfig({
appnexusAuctionKeywords: {
'1plusX': audiences,
},
});
}

/**
* Updates bidder configs with the targeting data retreived from Profile API
* @param {Object} papiResponse Response from Profile API
* @param {Object} config Module configuration
* @param {string[]} config.bidders Bidders specified in module's configuration
*/
export const setTargetingDataToConfig = (papiResponse, { bidders }) => {
const bidderConfigs = config.getBidderConfig();
export const setTargetingDataToConfig = (papiResponse, { bidders, biddersOrtb2 }) => {
const { s: segments, t: topics } = papiResponse;

const ortb2Updates = buildOrtb2Updates({ segments, topics });
for (const bidder of bidders) {
const ortb2Updates = buildOrtb2Updates({ segments, topics }, bidder);
const updatedBidderConfig = updateBidderConfig(bidder, ortb2Updates, bidderConfigs);
if (updatedBidderConfig) {
config.setBidderConfig({
bidders: [bidder],
config: updatedBidderConfig
});
}
if (bidder === 'appnexus') {
// Do the legacy stuff for appnexus with segments
setAppnexusAudiences(segments);
}
updateBidderConfig(bidder, ortb2Updates, biddersOrtb2);
}
}

Expand All @@ -256,13 +231,14 @@ const getBidRequestData = (reqBidsConfigObj, callback, moduleConfig, userConsent
try {
// Get the required config
const { customerId, bidders } = extractConfig(moduleConfig, reqBidsConfigObj);
const { ortb2Fragments: { bidder: biddersOrtb2 } } = reqBidsConfigObj;
// Get PAPI URL
const papiUrl = getPapiUrl(customerId, extractConsent(userConsent) || {}, extractFpid())
// Call PAPI
getTargetingDataFromPapi(papiUrl)
.then((papiResponse) => {
logMessage(LOG_PREFIX, 'Get targeting data request successful');
setTargetingDataToConfig(papiResponse, { bidders });
setTargetingDataToConfig(papiResponse, { bidders, biddersOrtb2 });
callback();
})
} catch (error) {
Expand Down
90 changes: 56 additions & 34 deletions modules/adhashBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {registerBidder} from '../src/adapters/bidderFactory.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { getStorageManager } from '../src/storageManager.js';
import { includes } from '../src/polyfill.js';
import { BANNER, VIDEO } from '../src/mediaTypes.js';

const VERSION = '3.2';
const VERSION = '3.6';
const BAD_WORD_STEP = 0.1;
const BAD_WORD_MIN = 0.2;
const ADHASH_BIDDER_CODE = 'adhash';
Expand All @@ -19,6 +19,8 @@ const ADHASH_BIDDER_CODE = 'adhash';
* @returns boolean flag is the page safe
*/
function brandSafety(badWords, maxScore) {
const delimiter = '~';

/**
* Performs the ROT13 encoding on the string argument and returns the resulting string.
* The Adhash bidder uses ROT13 so that the response is not blocked by:
Expand All @@ -40,17 +42,17 @@ function brandSafety(badWords, maxScore) {
/**
* Calculates the scoring for each bad word with dimishing returns
* @param {integer} points points that this word costs
* @param {integer} occurances number of occurances
* @param {integer} occurrences number of occurrences
* @returns {float} final score
*/
const scoreCalculator = (points, occurances) => {
const scoreCalculator = (points, occurrences) => {
let positive = true;
if (points < 0) {
points *= -1;
positive = false;
}
let result = 0;
for (let i = 0; i < occurances; i++) {
for (let i = 0; i < occurrences; i++) {
result += Math.max(points - i * BAD_WORD_STEP, BAD_WORD_MIN);
}
return positive ? result : -result;
Expand All @@ -60,22 +62,50 @@ function brandSafety(badWords, maxScore) {
* Checks what rule will match in the given array with words
* @param {string} rule rule type (full, partial, starts, ends, regexp)
* @param {string} decodedWord decoded word
* @param {array} wordsToMatch array to find a match
* @param {string} wordsToMatch list of all words on the page separated by delimiters
* @returns {object|boolean} matched rule and occurances. If nothing is matched returns false
*/
const wordsMatchedWithRule = function (rule, decodedWord, wordsToMatch) {
if (rule === 'full' && wordsToMatch && wordsToMatch.includes(decodedWord)) {
return { rule, occurances: wordsToMatch.filter(element => element === decodedWord).length };
} else if (rule === 'partial' && wordsToMatch && wordsToMatch.some(element => element.indexOf(decodedWord) > -1)) {
return { rule, occurances: wordsToMatch.filter(element => element.indexOf(decodedWord) > -1).length };
} else if (rule === 'starts' && wordsToMatch && wordsToMatch.some(word => word.startsWith(decodedWord))) {
return { rule, occurances: wordsToMatch.filter(element => element.startsWith(decodedWord)).length };
} else if (rule === 'ends' && wordsToMatch && wordsToMatch.some(word => word.endsWith(decodedWord))) {
return { rule, occurances: wordsToMatch.filter(element => element.endsWith(decodedWord)).length };
} else if (rule === 'regexp' && wordsToMatch && wordsToMatch.some(element => element.match(new RegExp(decodedWord, 'i')))) {
return { rule, occurances: wordsToMatch.filter(element => element.match(new RegExp(decodedWord, 'i'))).length };
if (!wordsToMatch) {
return false;
}

let occurrences;
let adjustedWordToMatch;
decodedWord = decodedWord.split(' ').join(`${delimiter}${delimiter}`);
switch (rule) {
case 'full':
adjustedWordToMatch = `${delimiter}${decodedWord}${delimiter}`;
break;
case 'partial':
adjustedWordToMatch = decodedWord;
break;
case 'starts':
adjustedWordToMatch = `${delimiter}${decodedWord}`;
break;
case 'ends':
adjustedWordToMatch = `${decodedWord}${delimiter}`;
break;
case 'combo':
const allOccurrences = [];
const paddedWordsToMatch = `${delimiter}${wordsToMatch}${delimiter}`;
const decodedWordsSplit = decodedWord.split(`${delimiter}${delimiter}`);
for (const decodedWordPart of decodedWordsSplit) {
adjustedWordToMatch = `${delimiter}${decodedWordPart}${delimiter}`;
allOccurrences.push(paddedWordsToMatch.split(adjustedWordToMatch).length - 1);
}
occurrences = Math.min(...allOccurrences);
return occurrences > 0 ? { rule, occurrences } : false;
case 'regexp':
occurrences = [...wordsToMatch.matchAll(new RegExp(decodedWord, 'gi'))].length;
return occurrences > 0 ? { rule, occurrences } : false;
default:
return false;
}
return false;

const paddedWordsToMatch = `${delimiter}${wordsToMatch}${delimiter}`;
occurrences = paddedWordsToMatch.split(adjustedWordToMatch).length - 1;
return occurrences > 0 ? { rule, occurrences } : false;
};

// Default parameters if the bidder is unable to send some of them
Expand All @@ -91,11 +121,11 @@ function brandSafety(badWords, maxScore) {
.toLowerCase()
.trim();
const content = window.top.document.body.innerText.toLowerCase();
const contentWords = content.trim().split(/\s+/).length;
// \p{L} matches a single unicode code point in the category 'letter'. Matches any kind of letter from any language.
const regexp = new RegExp('[\\p{L}]+', 'gu');
const words = content.match(regexp);
const wordsInUrl = wordsAndNumbersInUrl.match(regexp);
const wordsMatched = content.match(regexp);
const words = wordsMatched.join(`${delimiter}${delimiter}`);
const wordsInUrl = wordsAndNumbersInUrl.match(regexp).join(`${delimiter}${delimiter}`);

for (const [word, rule, points] of badWords) {
const decodedWord = rot13(word.toLowerCase());
Expand All @@ -110,19 +140,11 @@ function brandSafety(badWords, maxScore) {

// Check if site content's words match any of our brand safety rules
const matchedRule = wordsMatchedWithRule(rule, decodedWord, words);
if (matchedRule.rule === 'full') {
score += scoreCalculator(points, matchedRule.occurances);
} else if (matchedRule.rule === 'partial') {
score += scoreCalculator(points, matchedRule.occurances);
} else if (matchedRule.rule === 'starts') {
score += scoreCalculator(points, matchedRule.occurances);
} else if (matchedRule.rule === 'ends') {
score += scoreCalculator(points, matchedRule.occurances);
} else if (matchedRule.rule === 'regexp') {
score += scoreCalculator(points, matchedRule.occurances);
if (matchedRule !== false) {
score += scoreCalculator(points, matchedRule.occurrences);
}
}
return score < (maxScore * contentWords) / 1000;
return score < (maxScore * wordsMatched.length) / 1000;
} catch (e) {
return true;
}
Expand Down Expand Up @@ -183,8 +205,8 @@ export const spec = {
}

// Needed for the ad density calculation
var adHeight = validBidRequests[i].sizes[index][1];
var adWidth = validBidRequests[i].sizes[index][0];
const adHeight = validBidRequests[i].sizes[index][1];
const adWidth = validBidRequests[i].sizes[index][0];
if (!window.adsCount) {
window.adsCount = 0;
}
Expand Down Expand Up @@ -247,7 +269,7 @@ export const spec = {
const bidderResponse = JSON.stringify({ responseText: JSON.stringify(responseBody) });
const requestData = JSON.stringify(request.data);

var response = {
let response = {
requestId: request.bidRequest.bidId,
cpm: responseBody.creatives[0].costEUR,
width: request.bidRequest.sizes[0][0],
Expand Down
4 changes: 2 additions & 2 deletions modules/adkernelBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export const spec = {
{code: 'denakop'},
{code: 'rtbanalytica'},
{code: 'unibots'},
{code: 'catapultx'},
{code: 'ergadx'},
{code: 'turktelekom'},
{code: 'felixads'},
Expand All @@ -100,7 +99,8 @@ export const spec = {
{code: 'rtbdemand_com'},
{code: 'bidbuddy'},
{code: 'adliveconnect'},
{code: 'didnadisplay'}
{code: 'didnadisplay'},
{code: 'qortex'}
],
supportedMediaTypes: [BANNER, VIDEO, NATIVE],

Expand Down
3 changes: 2 additions & 1 deletion modules/asoBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export const spec = {
code: BIDDER_CODE,
supportedMediaTypes: [BANNER, VIDEO],
aliases: [
{code: 'bcmint'}
{code: 'bcmint'},
{code: 'bidgency'}
],

isBidRequestValid: bid => {
Expand Down
6 changes: 6 additions & 0 deletions modules/beopBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export const spec = {
*/
buildRequests: function(validBidRequests, bidderRequest) {
const slots = validBidRequests.map(beOpRequestSlotsMaker);
const firstPartyData = bidderRequest.ortb2;
const psegs = (firstPartyData && firstPartyData.user && firstPartyData.user.ext && firstPartyData.user.ext.data) ? firstPartyData.user.ext.data.permutive : undefined;
const pageUrl = getPageUrl(bidderRequest.refererInfo, window);
const gdpr = bidderRequest.gdprConsent;
const firstSlot = slots[0];
Expand All @@ -69,6 +71,10 @@ export const spec = {
tc_string: (gdpr && gdpr.gdprApplies) ? gdpr.consentString : null,
};

if (psegs) {
Object.assign(payloadObject, {psegs: psegs});
}

const payloadString = JSON.stringify(payloadObject);
return {
method: 'POST',
Expand Down
Loading

0 comments on commit 736b20f

Please sign in to comment.