Skip to content

Commit

Permalink
OpenX Bid Adapter: read first party data segments (prebid#7202)
Browse files Browse the repository at this point in the history
* OpenX Bid Adapter: read fpd data from ortb2.user.data for permutive

* OpenX Bid Adapter: read liveintent segments from request

* OpenX Bid Adapter: read segtax

* OpenX Bid Adapter: fix IE11 Object.entries() unsupported issue

addresses prebid#7202 (comment)
  • Loading branch information
laurb9 authored and agrandes-tappx committed Sep 29, 2021
1 parent 803a32c commit 6332ad7
Show file tree
Hide file tree
Showing 2 changed files with 243 additions and 93 deletions.
27 changes: 27 additions & 0 deletions modules/openxBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,29 @@ 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);
}
}

if (bids[0].params.platform) {
defaultParams.ph = bids[0].params.platform;
}
Expand Down Expand Up @@ -308,6 +331,10 @@ function appendUserIdsToQueryParams(queryParams, userIds) {
break;
case 'lipb':
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)}`;
}
break;
case 'parrableId':
queryParams[key] = userIdObjectOrValue.eid;
Expand Down
309 changes: 216 additions & 93 deletions test/spec/modules/openxBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,84 @@ describe('OpenxAdapter', function () {
}
};

// Sample bid requests

const BANNER_BID_REQUESTS_WITH_MEDIA_TYPES = [{
bidder: 'openx',
params: {
unit: '11',
delDomain: 'test-del-domain'
},
adUnitCode: '/adunit-code/test-path',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
}
},
bidId: 'test-bid-id-1',
bidderRequestId: 'test-bid-request-1',
auctionId: 'test-auction-1',
ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } },
}, {
bidder: 'openx',
params: {
unit: '22',
delDomain: 'test-del-domain'
},
adUnitCode: 'adunit-code',
mediaTypes: {
banner: {
sizes: [[728, 90]]
}
},
bidId: 'test-bid-id-2',
bidderRequestId: 'test-bid-request-2',
auctionId: 'test-auction-2',
ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-1' } } },
}];

const VIDEO_BID_REQUESTS_WITH_MEDIA_TYPES = [{
bidder: 'openx',
mediaTypes: {
video: {
playerSize: [640, 480]
}
},
params: {
unit: '12345678',
delDomain: 'test-del-domain'
},
adUnitCode: 'adunit-code',

bidId: '30b31c1838de1e',
bidderRequestId: '22edbae2733bf6',
auctionId: '1d1a030790a475',
transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e',
ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } },
}];

const MULTI_FORMAT_BID_REQUESTS = [{
bidder: 'openx',
params: {
unit: '12345678',
delDomain: 'test-del-domain'
},
adUnitCode: 'adunit-code',
mediaTypes: {
banner: {
sizes: [[300, 250]]
},
video: {
playerSize: [300, 250]
}
},
bidId: '30b31c1838de1e',
bidderRequestId: '22edbae2733bf6',
auctionId: '1d1a030790a475',
transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e',
ortb2Imp: { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } },
}];

describe('inherited functions', function () {
it('exists and is a function', function () {
expect(adapter.callBids).to.exist.and.to.be.a('function');
Expand Down Expand Up @@ -181,28 +259,8 @@ describe('OpenxAdapter', function () {

describe('when request is for a multiformat ad', function () {
describe('and request config uses mediaTypes video and banner', () => {
const multiformatBid = {
bidder: 'openx',
params: {
unit: '12345678',
delDomain: 'test-del-domain'
},
adUnitCode: 'adunit-code',
mediaTypes: {
banner: {
sizes: [[300, 250]]
},
video: {
playerSize: [300, 250]
}
},
bidId: '30b31c1838de1e',
bidderRequestId: '22edbae2733bf6',
auctionId: '1d1a030790a475',
transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e'
};
it('should return true multisize when required params found', function () {
expect(spec.isBidRequestValid(multiformatBid)).to.equal(true);
expect(spec.isBidRequestValid(MULTI_FORMAT_BID_REQUESTS[0])).to.equal(true);
});
});
});
Expand Down Expand Up @@ -327,39 +385,7 @@ describe('OpenxAdapter', function () {
});

describe('buildRequests for banner ads', function () {
const bidRequestsWithMediaTypes = [{
'bidder': 'openx',
'params': {
'unit': '11',
'delDomain': 'test-del-domain'
},
'adUnitCode': '/adunit-code/test-path',
mediaTypes: {
banner: {
sizes: [[300, 250], [300, 600]]
}
},
'bidId': 'test-bid-id-1',
'bidderRequestId': 'test-bid-request-1',
'auctionId': 'test-auction-1',
'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } }
}, {
'bidder': 'openx',
'params': {
'unit': '22',
'delDomain': 'test-del-domain'
},
'adUnitCode': 'adunit-code',
mediaTypes: {
banner: {
sizes: [[728, 90]]
}
},
'bidId': 'test-bid-id-2',
'bidderRequestId': 'test-bid-request-2',
'auctionId': 'test-auction-2',
'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-1' } } }
}];
const bidRequestsWithMediaTypes = BANNER_BID_REQUESTS_WITH_MEDIA_TYPES;

const bidRequestsWithPlatform = [{
'bidder': 'openx',
Expand Down Expand Up @@ -1246,25 +1272,7 @@ describe('OpenxAdapter', function () {
});

describe('buildRequests for video', function () {
const bidRequestsWithMediaTypes = [{
'bidder': 'openx',
'mediaTypes': {
video: {
playerSize: [640, 480]
}
},
'params': {
'unit': '12345678',
'delDomain': 'test-del-domain'
},
'adUnitCode': 'adunit-code',

'bidId': '30b31c1838de1e',
'bidderRequestId': '22edbae2733bf6',
'auctionId': '1d1a030790a475',
'transactionId': '4008d88a-8137-410b-aa35-fbfdabcb478e',
'ortb2Imp': { ext: { data: { pbadslot: '/12345/my-gpt-tag-0' } } }
}];
const bidRequestsWithMediaTypes = VIDEO_BID_REQUESTS_WITH_MEDIA_TYPES;
const mockBidderRequest = {refererInfo: {}};

it('should send bid request to openx url via GET, with mediaTypes having video parameter', function () {
Expand Down Expand Up @@ -1517,26 +1525,7 @@ describe('OpenxAdapter', function () {
});

describe('buildRequest for multi-format ad', function () {
const multiformatBid = {
bidder: 'openx',
params: {
unit: '12345678',
delDomain: 'test-del-domain'
},
adUnitCode: 'adunit-code',
mediaTypes: {
banner: {
sizes: [[300, 250]]
},
video: {
playerSize: [300, 250]
}
},
bidId: '30b31c1838de1e',
bidderRequestId: '22edbae2733bf6',
auctionId: '1d1a030790a475',
transactionId: '4008d88a-8137-410b-aa35-fbfdabcb478e'
};
const multiformatBid = MULTI_FORMAT_BID_REQUESTS[0];
let mockBidderRequest = {refererInfo: {}};

it('should default to a banner request', function () {
Expand All @@ -1547,6 +1536,140 @@ describe('OpenxAdapter', function () {
});
});

describe('buildRequests for all kinds of ads', function () {
utils._each({
banner: BANNER_BID_REQUESTS_WITH_MEDIA_TYPES[0],
video: VIDEO_BID_REQUESTS_WITH_MEDIA_TYPES[0],
multi: MULTI_FORMAT_BID_REQUESTS[0]
}, (bidRequest, name) => {
describe('with segments', function () {
const TESTS = [
{
name: 'should send proprietary segment data from first party config',
config: {
ortb2: {
user: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp2', segment: [{id: 'baz'}]},
]
}
}
},
expect: 'dmp1/4:foo|bar,dmp2:baz',
},
{
name: 'should combine same provider segment data from first party config',
config: {
ortb2: {
user: {
data: [
{name: 'dmp1', ext: {segtax: 4}, segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp1', ext: {}, segment: [{id: 'baz'}]},
]
}
}
},
expect: 'dmp1/4:foo|bar,dmp1:baz',
},
{
name: 'should not send any segment data if first party config is incomplete',
config: {
ortb2: {
user: {
data: [
{name: 'provider-with-no-segments'},
{segment: [{id: 'segments-with-no-provider'}]},
{},
]
}
}
}
},
{
name: 'should send first party data segments and liveintent segments from request',
config: {
ortb2: {
user: {
data: [
{name: 'dmp1', segment: [{id: 'foo'}, {id: 'bar'}]},
{name: 'dmp2', segment: [{id: 'baz'}]},
]
}
}
},
request: {
userId: {
lipb: {
lipbid: 'aaa',
segments: ['l1', 'l2']
},
},
},
expect: 'dmp1:foo|bar,dmp2:baz,liveintent:l1|l2',
},
{
name: 'should send just liveintent segment from request if no first party config',
config: {},
request: {
userId: {
lipb: {
lipbid: 'aaa',
segments: ['l1', 'l2']
},
},
},
expect: 'liveintent:l1|l2',
},
{
name: 'should send nothing if lipb section does not contain segments',
config: {},
request: {
userId: {
lipb: {
lipbid: 'aaa',
},
},
},
},
];
utils._each(TESTS, (t) => {
context('in ortb2.user.data', function () {
let bidRequests;
let configStub;

beforeEach(function () {
let fpdConfig = t.config
configStub = sinon
.stub(config, 'getConfig')
.withArgs('ortb2.user.data')
.callsFake((key) => {
return utils.deepAccess(fpdConfig, key);
});
bidRequests = [{...bidRequest, ...t.request}];
});

afterEach(function () {
config.getConfig.restore();
});

const mockBidderRequest = {refererInfo: {}};
it(`${t.name} for type ${name}`, 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));
} else {
expect(request[0].data.sm).to.not.exist;
}
});
});
});
});
});
})

describe('interpretResponse for banner ads', function () {
beforeEach(function () {
sinon.spy(userSync, 'registerSync');
Expand Down

0 comments on commit 6332ad7

Please sign in to comment.