Skip to content

Commit

Permalink
DSPx Bid Adapter: add ortb2 content, topics support (#11941)
Browse files Browse the repository at this point in the history
* DSPx Bid Adapter: add ortb2 content, topics support

DSPx Bid Adapter: add ortb2 content, topics support

* DSPx Bid Adapter: add ortb2 content, topics support

---------

Co-authored-by: avj <avj8383@gmail.com>
  • Loading branch information
onlsol and zav1517 authored Jul 8, 2024
1 parent eada00f commit ba82380
Show file tree
Hide file tree
Showing 2 changed files with 364 additions and 61 deletions.
230 changes: 176 additions & 54 deletions modules/dspxBidAdapter.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {deepAccess, getBidIdParameter, isFn, logError, logMessage, logWarn} from '../src/utils.js';
import {deepAccess, getBidIdParameter, isFn, logError, logMessage, logWarn, isEmptyStr, isArray} from '../src/utils.js';

import {registerBidder} from '../src/adapters/bidderFactory.js';
import {BANNER, VIDEO} from '../src/mediaTypes.js';
import {Renderer} from '../src/Renderer.js';
Expand All @@ -7,7 +8,6 @@ import {includes} from '../src/polyfill.js';
/**
* @typedef {import('../src/adapters/bidderFactory.js').BidRequest} BidRequest
*/

const BIDDER_CODE = 'dspx';
const ENDPOINT_URL = 'https://buyer.dspx.tv/request/';
const ENDPOINT_URL_DEV = 'https://dcbuyer.dspx.tv/request/';
Expand Down Expand Up @@ -63,24 +63,18 @@ export const spec = {
rnd: rnd,
ref: referrer,
bid_id: bidId,
pbver: '$prebid.version$'
pbver: '$prebid.version$',
};

payload.pfilter = {};
if (params.pfilter !== undefined) {
payload.pfilter = params.pfilter;
}

if (bidderRequest && bidderRequest.gdprConsent) {
if (payload.pfilter !== undefined) {
if (!payload.pfilter.gdpr_consent) {
payload.pfilter.gdpr_consent = bidderRequest.gdprConsent.consentString;
payload.pfilter.gdpr = bidderRequest.gdprConsent.gdprApplies;
}
} else {
payload.pfilter = {
'gdpr_consent': bidderRequest.gdprConsent.consentString,
'gdpr': bidderRequest.gdprConsent.gdprApplies
};
if (!payload.pfilter.gdpr_consent) {
payload.pfilter.gdpr_consent = bidderRequest.gdprConsent.consentString;
payload.pfilter.gdpr = bidderRequest.gdprConsent.gdprApplies;
}
}

Expand All @@ -94,48 +88,10 @@ export const spec = {
payload.prebidDevMode = 1;
}

// fill userId params
if (bidRequest.userId) {
if (bidRequest.userId.netId) {
payload.did_netid = bidRequest.userId.netId;
}
if (bidRequest.userId.id5id) {
payload.did_id5 = bidRequest.userId.id5id.uid || '0';
if (bidRequest.userId.id5id.ext.linkType !== undefined) {
payload.did_id5_linktype = bidRequest.userId.id5id.ext.linkType;
}
}
let uId2 = deepAccess(bidRequest, 'userId.uid2.id');
if (uId2) {
payload.did_uid2 = uId2;
}
let sharedId = deepAccess(bidRequest, 'userId.sharedid.id');
if (sharedId) {
payload.did_sharedid = sharedId;
}
let pubcId = deepAccess(bidRequest, 'userId.pubcid');
if (pubcId) {
payload.did_pubcid = pubcId;
}
let crumbsPubcid = deepAccess(bidRequest, 'crumbs.pubcid');
if (crumbsPubcid) {
payload.did_cpubcid = crumbsPubcid;
}
}

if (bidRequest.schain) {
payload.schain = bidRequest.schain;
}

if (payload.pfilter === undefined || !payload.pfilter.floorprice) {
if (!payload.pfilter.floorprice) {
let bidFloor = getBidFloor(bidRequest);
if (bidFloor > 0) {
if (payload.pfilter !== undefined) {
payload.pfilter.floorprice = bidFloor;
} else {
payload.pfilter = { 'floorprice': bidFloor };
}
// payload.bidFloor = bidFloor;
payload.pfilter.floorprice = bidFloor;
}
}

Expand All @@ -146,6 +102,7 @@ export const spec = {
payload.pbcode = pbcode;
}

// media types
payload.media_types = convertMediaInfoForRequest(mediaTypesInfo);
if (mediaTypesInfo[VIDEO] !== undefined) {
payload.vctx = getVideoContext(bidRequest);
Expand All @@ -159,6 +116,33 @@ export const spec = {
.forEach(key => payload.vpl[key] = videoParams[key]);
}

// iab content
let content = deepAccess(bidderRequest, 'ortb2.site.content');
if (content) {
let stringContent = siteContentToString(content);
if (stringContent) {
payload.pfilter.iab_content = stringContent;
}
}

// Google Topics
const segments = extractUserSegments(bidderRequest);
if (segments) {
assignDefinedValues(payload, {
segtx: segments.segtax,
segcl: segments.segclass,
segs: segments.segments
});
}

// schain
if (bidRequest.schain) {
payload.schain = bidRequest.schain;
}

// fill userId params
fillUsersIds(bidRequest, payload);

return {
method: 'GET',
url: endpoint,
Expand Down Expand Up @@ -257,6 +241,74 @@ export const spec = {
}
}

/**
* Adds userIds to payload
*
* @param bidRequest
* @param payload
*/
function fillUsersIds(bidRequest, payload) {
if (bidRequest.hasOwnProperty('userId')) {
let didMapping = {
did_netid: 'userId.netId',
did_id5: 'userId.id5id.uid',
did_id5_linktype: 'userId.id5id.ext.linkType',
did_uid2: 'userId.uid2',
did_sharedid: 'userId.sharedid',
did_pubcid: 'userId.pubcid',
did_uqid: 'userId.utiq',
did_cruid: 'userId.criteoid',
did_euid: 'userId.euid',
// did_tdid: 'unifiedId',
did_tdid: 'userId.tdid',
did_ppuid: function() {
let path = 'userId.pubProvidedId';
let value = deepAccess(bidRequest, path);
if (isArray(value)) {
for (const rec of value) {
if (rec.uids && rec.uids.length > 0) {
for (let i = 0; i < rec.uids.length; i++) {
if ('id' in rec.uids[i] && deepAccess(rec.uids[i], 'ext.stype') === 'ppuid') {
return (rec.uids[i].atype ?? '') + ':' + rec.source + ':' + rec.uids[i].id;
}
}
}
}
}
return undefined;
},
did_cpubcid: 'crumbs.pubcid'
};
for (let paramName in didMapping) {
let path = didMapping[paramName];

// handle function
if (typeof path == 'function') {
let value = path(paramName);
if (value) {
payload[paramName] = value;
}
continue;
}
// direct access
let value = deepAccess(bidRequest, path);
if (typeof value == 'string' || typeof value == 'number') {
payload[paramName] = value;
} else if (typeof value == 'object') {
// trying to find string ID value
if (typeof deepAccess(bidRequest, path + '.id') == 'string') {
payload[paramName] = deepAccess(bidRequest, path + '.id');
} else {
if (Object.keys(value).length > 0) {
logError(`DSPx: WARNING: fillUserIds had to use first key in user object to get value for bid.userId key: ${path}.`);
payload[paramName] = value[Object.keys(value)[0]];
}
}
}
}
}
}

function appendToUrl(url, what) {
if (!what) {
return url;
Expand All @@ -276,7 +328,7 @@ function objectToQueryString(obj, prefix) {
: encodeURIComponent(k) + '=' + encodeURIComponent(v));
}
}
return str.join('&');
return str.filter(n => n).join('&');
}

/**
Expand Down Expand Up @@ -508,4 +560,74 @@ function createOutstreamEmbedCode(bid) {
return fragment;
}

/**
* Convert site.content to string
* @param content
*/
function siteContentToString(content) {
if (!content) {
return '';
}
let stringKeys = ['id', 'title', 'series', 'season', 'artist', 'genre', 'isrc', 'url', 'keywords'];
let intKeys = ['episode', 'context', 'livestream'];
let arrKeys = ['cat'];
let retArr = [];
arrKeys.forEach(k => {
let val = deepAccess(content, k);
if (val && Array.isArray(val)) {
retArr.push(k + ':' + val.join('|'));
}
});
intKeys.forEach(k => {
let val = deepAccess(content, k);
if (val && typeof val === 'number') {
retArr.push(k + ':' + val);
}
});
stringKeys.forEach(k => {
let val = deepAccess(content, k);
if (val && typeof val === 'string') {
retArr.push(k + ':' + encodeURIComponent(val));
}
});
return retArr.join(',');
}

/**
* Assigns multiple values to the specified keys on an object if the values are not undefined.
* @param {Object} target - The object to which the values will be assigned.
* @param {Object} values - An object containing key-value pairs to be assigned.
*/
function assignDefinedValues(target, values) {
for (const key in values) {
if (values[key] !== undefined) {
target[key] = values[key];
}
}
}

/**
* Extracts user segments/topics from the bid request object
* @param {Object} bid - The bid request object
* @returns {{segclass: *, segtax: *, segments: *}|undefined} - User segments/topics or undefined if not found
*/
function extractUserSegments(bid) {
const userData = deepAccess(bid, 'ortb2.user.data') || [];
for (const dataObj of userData) {
if (dataObj.segment && isArray(dataObj.segment) && dataObj.segment.length > 0) {
const segments = dataObj.segment
.filter(seg => seg.id && !isEmptyStr(seg.id) && isFinite(seg.id))
.map(seg => Number(seg.id));
if (segments.length > 0) {
return {
segtax: deepAccess(dataObj, 'ext.segtax'),
segclass: deepAccess(dataObj, 'ext.segclass'),
segments: segments.join(',')
};
}
}
}
return undefined;
}

registerBidder(spec);
Loading

0 comments on commit ba82380

Please sign in to comment.