Skip to content
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

OpenX Bid Adapter: Handle site.content.data & bug fixes #7576

Merged
merged 3 commits into from
Oct 18, 2021
Merged
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
56 changes: 34 additions & 22 deletions modules/openxBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,27 +256,14 @@ function buildCommonQueryParamsFromBids(bids, bidderRequest) {
nocache: new Date().getTime()
};

const firstPartyData = config.getConfig('ortb2.user.data')
if (Array.isArray(firstPartyData) && firstPartyData.length > 0) {
// extract and merge valid segments by provider/taxonomy
const fpd = firstPartyData
.filter(
data => (Array.isArray(data.segment) &&
data.segment.length > 0 &&
data.name !== undefined &&
data.name.length > 0)
)
.reduce((acc, data) => {
const name = typeof data.ext === 'object' && data.ext.segtax ? `${data.name}/${data.ext.segtax}` : data.name;
acc[name] = (acc[name] || []).concat(data.segment.map(seg => seg.id));
return acc;
}, {})
const sm = Object.keys(fpd)
.map((name, _) => name + ':' + fpd[name].join('|'))
.join(',')
if (sm.length > 0) {
defaultParams.sm = encodeURIComponent(sm);
}
const userDataSegments = buildFpdQueryParams('ortb2.user.data');
if (userDataSegments.length > 0) {
defaultParams.sm = userDataSegments;
}

const siteContentDataSegments = buildFpdQueryParams('ortb2.site.content.data');
if (siteContentDataSegments.length > 0) {
defaultParams.scsm = siteContentDataSegments;
}

if (bids[0].params.platform) {
Expand Down Expand Up @@ -317,12 +304,37 @@ function buildCommonQueryParamsFromBids(bids, bidderRequest) {
return defaultParams;
}

function buildFpdQueryParams(fpdPath) {
const firstPartyData = config.getConfig(fpdPath);
if (!Array.isArray(firstPartyData) || !firstPartyData.length) {
return '';
}
const fpd = firstPartyData
.filter(
data => (Array.isArray(data.segment) &&
data.segment.length > 0 &&
data.name !== undefined &&
data.name.length > 0)
)
.reduce((acc, data) => {
const name = typeof data.ext === 'object' && data.ext.segtax ? `${data.name}/${data.ext.segtax}` : data.name;
acc[name] = (acc[name] || []).concat(data.segment.map(seg => seg.id));
return acc;
}, {})
return Object.keys(fpd)
.map((name, _) => name + ':' + [...new Set(fpd[name])].join('|'))
.join(',')
}

function appendUserIdsToQueryParams(queryParams, userIds) {
utils._each(userIds, (userIdObjectOrValue, userIdProviderKey) => {
const key = USER_ID_CODE_TO_QUERY_ARG[userIdProviderKey];

if (USER_ID_CODE_TO_QUERY_ARG.hasOwnProperty(userIdProviderKey)) {
switch (userIdProviderKey) {
case 'merkleId':
queryParams[key] = userIdObjectOrValue.id;
break;
case 'flocId':
queryParams[key] = userIdObjectOrValue.id;
break;
Expand All @@ -333,7 +345,7 @@ function appendUserIdsToQueryParams(queryParams, userIds) {
queryParams[key] = userIdObjectOrValue.lipbid;
if (Array.isArray(userIdObjectOrValue.segments) && userIdObjectOrValue.segments.length > 0) {
const liveIntentSegments = 'liveintent:' + userIdObjectOrValue.segments.join('|')
queryParams.sm = `${queryParams.sm ? queryParams.sm + encodeURIComponent(',') : ''}${encodeURIComponent(liveIntentSegments)}`;
queryParams.sm = `${queryParams.sm ? queryParams.sm + ',' : ''}${liveIntentSegments}`;
}
break;
case 'parrableId':
Expand Down
124 changes: 111 additions & 13 deletions test/spec/modules/openxBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1085,7 +1085,7 @@ describe('OpenxAdapter', function () {
intentIqId: '1111-intentiqid',
lipb: {lipbid: '1111-lipb'},
lotamePanoramaId: '1111-lotameid',
merkleId: '1111-merkleid',
merkleId: {id: '1111-merkleid'},
netId: 'fH5A3n2O8_CZZyPoJVD-eabc6ECb7jhxCicsds7qSg',
parrableId: { eid: 'eidVersion.encryptionKeyReference.encryptedValue' },
pubcid: '1111-pubcid',
Expand Down Expand Up @@ -1139,6 +1139,9 @@ describe('OpenxAdapter', function () {
let userIdValue;
// handle cases where userId key refers to an object
switch (userIdProviderKey) {
case 'merkleId':
userIdValue = EXAMPLE_DATA_BY_ATTR.merkleId.id;
break;
case 'flocId':
userIdValue = EXAMPLE_DATA_BY_ATTR.flocId.id;
break;
Expand Down Expand Up @@ -1545,7 +1548,7 @@ describe('OpenxAdapter', function () {
describe('with segments', function () {
const TESTS = [
{
name: 'should send proprietary segment data from first party config',
name: 'should send proprietary segment data from ortb2.user.data',
config: {
ortb2: {
user: {
Expand All @@ -1556,10 +1559,77 @@ describe('OpenxAdapter', function () {
}
}
},
expect: 'dmp1/4:foo|bar,dmp2:baz',
expect: {sm: 'dmp1/4:foo|bar,dmp2:baz'},
},
{
name: 'should send proprietary segment data from ortb2.site.content.data',
config: {
ortb2: {
site: {
content: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp2', segment: [{id: 'baz'}]},
]
}
}
}
},
expect: {scsm: 'dmp1/4:foo|bar,dmp2:baz'},
},
{
name: 'should send proprietary segment data from both ortb2.site.content.data and ortb2.user.data',
config: {
ortb2: {
user: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp2', segment: [{id: 'baz'}]},
]
},
site: {
content: {
data: [
{name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'bar2'}]},
{name: 'dmp4', segment: [{id: 'baz2'}]},
]
}
}
}
},
expect: {
sm: 'dmp1/4:foo|bar,dmp2:baz',
scsm: 'dmp3/5:foo2|bar2,dmp4:baz2'
},
},
{
name: 'should not send duplicate proprietary segment data from first party config ',
config: {
ortb2: {
user: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}, {id: 'foo'}]},
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
]
},
site: {
content: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'foo2'}, {id: 'bar2'}]},
{name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'bar2'}]},
]
}
}
}
},
expect: {
sm: 'dmp1/4:foo|bar',
scsm: 'dmp1/4:foo|bar,dmp3/5:foo2|bar2'
},
},
{
name: 'should combine same provider segment data from first party config',
name: 'should combine same provider segment data from ortb2.user.data',
config: {
ortb2: {
user: {
Expand All @@ -1570,7 +1640,23 @@ describe('OpenxAdapter', function () {
}
}
},
expect: 'dmp1/4:foo|bar,dmp1:baz',
expect: {sm: 'dmp1/4:foo|bar,dmp1:baz'},
},
{
name: 'should combine same provider segment data from ortb2.site.content.data',
config: {
ortb2: {
site: {
content: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp1', ext: {}, segment: [{id: 'baz'}]},
]
}
}
}
},
expect: {scsm: 'dmp1/4:foo|bar,dmp1:baz'},
},
{
name: 'should not send any segment data if first party config is incomplete',
Expand All @@ -1595,6 +1681,14 @@ describe('OpenxAdapter', function () {
{name: 'dmp1', segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp2', segment: [{id: 'baz'}]},
]
},
site: {
content: {
data: [
{name: 'dmp3', ext: {segtax: 5}, segment: [{id: 'foo2'}, {id: 'bar2'}]},
{name: 'dmp4', segment: [{id: 'baz2'}]},
]
}
}
}
},
Expand All @@ -1606,7 +1700,10 @@ describe('OpenxAdapter', function () {
},
},
},
expect: 'dmp1:foo|bar,dmp2:baz,liveintent:l1|l2',
expect: {
sm: 'dmp1:foo|bar,dmp2:baz,liveintent:l1|l2',
scsm: 'dmp3/5:foo2|bar2,dmp4:baz2'
},
},
{
name: 'should send just liveintent segment from request if no first party config',
Expand All @@ -1619,7 +1716,7 @@ describe('OpenxAdapter', function () {
},
},
},
expect: 'liveintent:l1|l2',
expect: {sm: 'liveintent:l1|l2'},
},
{
name: 'should send nothing if lipb section does not contain segments',
Expand All @@ -1636,13 +1733,11 @@ describe('OpenxAdapter', function () {
utils._each(TESTS, (t) => {
context('in ortb2.user.data', function () {
let bidRequests;
let configStub;

beforeEach(function () {
let fpdConfig = t.config
configStub = sinon
sinon
.stub(config, 'getConfig')
.withArgs('ortb2.user.data')
.withArgs(sinon.match(/^ortb2\.user\.data$|^ortb2\.site\.content\.data$/))
.callsFake((key) => {
return utils.deepAccess(fpdConfig, key);
});
Expand All @@ -1658,10 +1753,13 @@ describe('OpenxAdapter', function () {
const request = spec.buildRequests(bidRequests, mockBidderRequest)
expect(request.length).to.equal(1);
if (t.expect) {
expect(request[0].data.sm).to.exist;
expect(request[0].data.sm).to.equal(encodeURIComponent(t.expect));
for (const key in t.expect) {
expect(request[0].data[key]).to.exist;
expect(request[0].data[key]).to.equal(t.expect[key]);
}
} else {
expect(request[0].data.sm).to.not.exist;
expect(request[0].data.scsm).to.not.exist;
}
});
});
Expand Down