Skip to content

Commit

Permalink
Rubicon Bid Adapter: Pass SUA data if available (#10721)
Browse files Browse the repository at this point in the history
* rubicon pass client hints

* linttt
  • Loading branch information
robertrmartinez authored Nov 13, 2023
1 parent af4936a commit c8870c1
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 1 deletion.
31 changes: 30 additions & 1 deletion modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
logMessage,
logWarn,
mergeDeep,
parseSizesInput, _each
parseSizesInput,
pick,
_each
} from '../src/utils.js';
import {getAllOrtbKeywords} from '../libraries/keywords/keywords.js';
import {convertTypes} from '../libraries/transformParamsUtils/convertTypes.js';
Expand Down Expand Up @@ -973,6 +975,33 @@ function applyFPD(bidRequest, mediaType, data) {
if (data['tg_i.pbadslot']) {
delete data['tg_i.dfp_ad_unit_code'];
}

// High Entropy stuff -> sua object is the ORTB standard (default to pass unless specifically disabled)
const clientHints = deepAccess(fpd, 'device.sua');
if (clientHints && rubiConf.chEnabled !== false) {
// pick out client hints we want to send (any that are undefined or empty will NOT be sent)
pick(clientHints, [
'architecture', arch => data.m_ch_arch = arch,
'bitness', bitness => data.m_ch_bitness = bitness,
'browsers', browsers => {
if (!Array.isArray(browsers)) return;
// reduce down into ua and full version list attributes
const [ua, fullVer] = browsers.reduce((accum, browserData) => {
accum[0].push(`"${browserData?.brand}"|v="${browserData?.version?.[0]}"`);
accum[1].push(`"${browserData?.brand}"|v="${browserData?.version?.join?.('.')}"`);
return accum;
}, [[], []]);
data.m_ch_ua = ua?.join?.(',');
data.m_ch_full_ver = fullVer?.join?.(',');
},
'mobile', isMobile => data.m_ch_mobile = `?${isMobile}`,
'model', model => data.m_ch_model = model,
'platform', platform => {
data.m_ch_platform = platform?.brand;
data.m_ch_platform_ver = platform?.version?.join?.('.');
}
])
}
} else {
if (Object.keys(impExt).length) {
mergeDeep(data.imp[0].ext, impExt);
Expand Down
120 changes: 120 additions & 0 deletions test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1818,6 +1818,126 @@ describe('the rubicon adapter', function () {
expect(data['tg_i.dfp_ad_unit_code']).to.equal('/a/b/c');
});
});

describe('client hints', function () {
let standardSuaObject;
beforeEach(function () {
standardSuaObject = {
source: 2,
platform: {
brand: 'macOS',
version: [
'12',
'6',
'0'
]
},
browsers: [
{
brand: 'Not.A/Brand',
version: [
'8',
'0',
'0',
'0'
]
},
{
brand: 'Chromium',
version: [
'114',
'0',
'5735',
'198'
]
},
{
brand: 'Google Chrome',
version: [
'114',
'0',
'5735',
'198'
]
}
],
mobile: 0,
model: '',
bitness: '64',
architecture: 'x86'
}
});
it('should send m_ch_* params if ortb2.device.sua object is there', function () {
let bidRequestSua = utils.deepClone(bidderRequest);
bidRequestSua.bids[0].ortb2 = { device: { sua: standardSuaObject } };

// How should fastlane query be constructed with default SUA
let expectedValues = {
m_ch_arch: 'x86',
m_ch_bitness: '64',
m_ch_ua: `"Not.A/Brand"|v="8","Chromium"|v="114","Google Chrome"|v="114"`,
m_ch_full_ver: `"Not.A/Brand"|v="8.0.0.0","Chromium"|v="114.0.5735.198","Google Chrome"|v="114.0.5735.198"`,
m_ch_mobile: '?0',
m_ch_platform: 'macOS',
m_ch_platform_ver: '12.6.0'
}

// Build Fastlane call
let [request] = spec.buildRequests(bidRequestSua.bids, bidRequestSua);
let data = parseQuery(request.data);

// Loop through expected values and if they do not match push an error
const errors = Object.entries(expectedValues).reduce((accum, [key, val]) => {
if (data[key] !== val) accum.push(`${key} - expect: ${val} - got: ${data[key]}`)
return accum;
}, []);

// should be no errors
expect(errors).to.deep.equal([]);
});
it('should not send invalid values for m_ch_*', function () {
let bidRequestSua = utils.deepClone(bidderRequest);

// Alter input SUA object
// send model
standardSuaObject.model = 'Suface Duo';
// send mobile = 1
standardSuaObject.mobile = 1;

// make browsers not an array
standardSuaObject.browsers = 'My Browser';

// make platform not have version
delete standardSuaObject.platform.version;

// delete architecture
delete standardSuaObject.architecture;

// add SUA to bid
bidRequestSua.bids[0].ortb2 = { device: { sua: standardSuaObject } };

// Build Fastlane request
let [request] = spec.buildRequests(bidRequestSua.bids, bidRequestSua);
let data = parseQuery(request.data);

// should show new names
expect(data.m_ch_model).to.equal('Suface Duo');
expect(data.m_ch_mobile).to.equal('?1');

// should still send platform
expect(data.m_ch_platform).to.equal('macOS');

// platform version not sent
expect(data).to.not.haveOwnProperty('m_ch_platform_ver');

// both ua and full_ver not sent because browsers not array
expect(data).to.not.haveOwnProperty('m_ch_ua');
expect(data).to.not.haveOwnProperty('m_ch_full_ver');

// arch not sent
expect(data).to.not.haveOwnProperty('m_ch_arch');
});
});
});

if (FEATURES.VIDEO) {
Expand Down

0 comments on commit c8870c1

Please sign in to comment.