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

Rubicon: support SupplyChain (schain) #4315

Merged
merged 44 commits into from
Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8d635ff
Add microadBidAdapter
Feb 15, 2019
3055b90
Remove unnecessary encodeURIComponent from microadBidAdapter
Feb 26, 2019
67fb91b
Submit Advangelists Prebid Adapter
Feb 26, 2019
3ebb916
Submit Advangelists Prebid Adapter 1.1
Feb 27, 2019
4f5c451
Correct procudtion endpoint for prebid
Feb 28, 2019
2dc6d1d
Merge branch 'microad-bid-adapter' of git://github.com/strong-zero/Pr…
Feb 28, 2019
fa3e081
Merge remote-tracking branch 'origin/master' into master-rubicon-clean
Mar 18, 2019
600a46e
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Mar 18, 2019
7f578b3
Merge branch 'master' of https://github.com/prebid/Prebid.js
Mar 23, 2019
176a312
Merge branch 'master' of https://github.com/prebid/Prebid.js
Mar 26, 2019
9abf89c
Merge branch 'master' of https://github.com/prebid/Prebid.js
May 13, 2019
6ce04ab
Merge remote-tracking branch 'upstream/master'
Jun 10, 2019
415e2f6
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jun 27, 2019
61fb82c
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 11, 2019
3cc4c67
analytics update with wrapper name
Jul 11, 2019
cd81e02
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 15, 2019
e2b4e04
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 15, 2019
53b5970
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 15, 2019
5c00ed5
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 16, 2019
28848ad
reverted error merge
Jul 16, 2019
ab635ee
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 19, 2019
93308f5
Merge branch 'master' of https://github.com/prebid/Prebid.js into mas…
Jul 24, 2019
1cfd52d
Merge branch 'master' of https://github.com/prebid/Prebid.js
Jul 25, 2019
d619807
Merge branch 'master' of https://github.com/prebid/Prebid.js
Jul 27, 2019
9893f0f
Merge branch 'master' of https://github.com/prebid/Prebid.js
Aug 7, 2019
986a251
Merge remote-tracking branch 'upstream/master'
Aug 23, 2019
2cae7c0
Merge branch 'master' of https://github.com/prebid/Prebid.js into ori…
Aug 27, 2019
d7fd252
Merge branch 'master' of https://github.com/prebid/Prebid.js
Sep 4, 2019
c7c01ba
Merge branch 'master' of https://github.com/prebid/Prebid.js
Sep 10, 2019
24a28aa
Merge branch 'master' of https://github.com/prebid/Prebid.js
Sep 10, 2019
7a1a4ec
Merge branch 'master' of https://github.com/rubicon-project/Prebid.js
Sep 25, 2019
4173d16
Merge branch 'master' of https://github.com/prebid/Prebid.js
Sep 25, 2019
ca13952
update changed default value of netRevenue to true
Sep 27, 2019
0b6cd48
Merge branch 'master' of https://github.com/prebid/Prebid.js
Oct 3, 2019
5f0110d
Merge branch 'master' of https://github.com/prebid/Prebid.js
Oct 3, 2019
f930a34
Merge remote-tracking branch 'upstream/master'
Oct 8, 2019
9bff282
Merge branch 'master' of https://github.com/prebid/Prebid.js
Oct 14, 2019
c7ea2eb
Starting schain
tjeastmond Oct 15, 2019
aaab6bb
More tests for banner schain support
tjeastmond Oct 18, 2019
c42e1fc
Video tests
tjeastmond Oct 21, 2019
71ef8c5
Encoding tweaks, required fields and comments
tjeastmond Oct 22, 2019
e18d191
Removed .only() from tests
tjeastmond Oct 22, 2019
e0e1300
Change requests per Bret
tjeastmond Oct 24, 2019
0a458a8
Merge branch 'master' into hb-6418-schain
tjeastmond Nov 8, 2019
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
68 changes: 66 additions & 2 deletions modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ export const spec = {
utils.deepSetValue(data, 'regs.coppa', 1);
}

if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) {
utils.deepSetValue(data, 'source.ext.schain', bidRequest.schain);
}

return {
method: 'POST',
url: VIDEO_ENDPOINT,
Expand All @@ -277,7 +281,7 @@ export const spec = {
url: FASTLANE_ENDPOINT,
data: spec.getOrderedParams(bidParams).reduce((paramString, key) => {
const propValue = bidParams[key];
return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${key}=${encodeURIComponent(propValue)}&` : paramString;
return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString;
}, '') + `slots=1&rand=${Math.random()}`,
bidRequest
};
Expand Down Expand Up @@ -308,7 +312,7 @@ export const spec = {
url: FASTLANE_ENDPOINT,
data: spec.getOrderedParams(combinedSlotParams).reduce((paramString, key) => {
const propValue = combinedSlotParams[key];
return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${key}=${encodeURIComponent(propValue)}&` : paramString;
return ((utils.isStr(propValue) && propValue !== '') || utils.isNumber(propValue)) ? `${paramString}${encodeParam(key, propValue)}&` : paramString;
}, '') + `slots=${bidsInGroup.length}&rand=${Math.random()}`,
bidRequest: bidsInGroup
});
Expand All @@ -332,6 +336,8 @@ export const spec = {
'p_pos',
'gdpr',
'gdpr_consent',
'rp_schain',
'rf',
'tpid_tdid',
'tpid_liveintent.com',
'tg_v.LIseg',
Expand Down Expand Up @@ -482,9 +488,38 @@ export const spec = {
data['coppa'] = 1;
}

// if SupplyChain is supplied and contains all required fields
if (bidRequest.schain && hasValidSupplyChainParams(bidRequest.schain)) {
data.rp_schain = spec.serializeSupplyChain(bidRequest.schain);
}

return data;
},

/**
* Serializes schain params according to OpenRTB requirements
* @param {Object} supplyChain
* @returns {String}
*/
serializeSupplyChain: function (supplyChain) {
const supplyChainIsValid = hasValidSupplyChainParams(supplyChain);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check occurs on line 460 as well as inside this function.

Is there a specific reason? Think if it is not valid we just do not set the rp_schain

So seems like this:
if (!supplyChainIsValid) return '';
will never actually be hit right?

Copy link
Contributor Author

@tjeastmond tjeastmond Oct 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea was that serializeSupplyChain() could be used directly from other methods if need be, and to eventually extract that method to a utils lib for other modules to use. I can remove it though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm good with either way, makes sense if many others will want to use it so we can move it later!

if (!supplyChainIsValid) return '';
const { ver, complete, nodes } = supplyChain;
return `${ver},${complete}!${spec.serializeSupplyChainNodes(nodes)}`;
},

/**
* Properly sorts schain object params
* @param {Array} nodes
* @returns {String}
*/
serializeSupplyChainNodes: function (nodes) {
const nodePropOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
return nodes.map(node => {
return nodePropOrder.map(prop => encodeURIComponent(node[prop] || '')).join(',');
}).join('!');
},

/**
* @param {*} responseObj
* @param {BidRequest|Object.<string, BidRequest[]>} bidRequest - if request was SRA the bidRequest argument will be a keyed BidRequest array object,
Expand Down Expand Up @@ -982,6 +1017,35 @@ export function hasValidVideoParams(bid) {
return isValid;
}

/**
* Make sure the required params are present
* @param {Object} schain
* @param {Bool}
*/
export function hasValidSupplyChainParams(schain) {
let isValid = false;
const requiredFields = ['asi', 'sid', 'hp'];
if (!schain.nodes) return isValid;
isValid = schain.nodes.reduce((status, node) => {
if (!status) return status;
return requiredFields.every(field => node[field]);
}, true);
if (!isValid) utils.logError('Rubicon: required schain params missing');
return isValid;
}

/**
* Creates a URL key value param, encoding the
* param unless the key is schain
* @param {String} key
* @param {String} param
* @returns {String}
*/
export function encodeParam(key, param) {
if (key === 'rp_schain') return `rp_schain=${param}`;
return `${key}=${encodeURIComponent(param)}`;
}

/**
* split array into multiple arrays of defined size
* @param {Array} array
Expand Down
137 changes: 137 additions & 0 deletions test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,49 @@ describe('the rubicon adapter', function () {
* @param {Array.<overrideProps>} [indexOverMap]
* @return {{status: string, cpm: number, zone_id: *, size_id: *, impression_id: *, ad_id: *, creative_id: string, type: string, targeting: *[]}}
*/

function getBidderRequest() {
return {
bidderCode: 'rubicon',
auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a',
bidderRequestId: '178e34bad3658f',
bids: [
{
bidder: 'rubicon',
params: {
accountId: '14062',
siteId: '70608',
zoneId: '335918',
userId: '12346',
keywords: ['a', 'b', 'c'],
inventory: {
rating: '5-star', // This actually should not be sent to frank!! causes 400
prodtype: ['tech', 'mobile']
},
visitor: {
ucat: 'new',
lastsearch: 'iphone',
likes: ['sports', 'video games']
},
position: 'atf',
referrer: 'localhost',
latLong: [40.7607823, '111.8910325']
},
adUnitCode: '/19968336/header-bid-tag-0',
code: 'div-1',
sizes: [[300, 250], [320, 50]],
bidId: '2ffb201a808da7',
bidderRequestId: '178e34bad3658f',
auctionId: 'c45dd708-a418-42ec-b8a7-b70a6c6fab0a',
transactionId: 'd45dd707-a418-42ec-b8a7-b70a6c6fab0b'
}
],
start: 1472239426002,
auctionStart: 1472239426000,
timeout: 5000
};
};

function createResponseAdByIndex(i, sizeId, indexOverMap) {
const overridePropMap = (indexOverMap && indexOverMap[i] && typeof indexOverMap[i] === 'object') ? indexOverMap[i] : {};
const overrideProps = Object.keys(overridePropMap).reduce((aggregate, key) => {
Expand Down Expand Up @@ -2267,4 +2310,98 @@ describe('the rubicon adapter', function () {
});
});
});

describe.only('Supply Chain Support', function() {
const nodePropsOrder = ['asi', 'sid', 'hp', 'rid', 'name', 'domain'];
let bidRequests;
let schainConfig;

const getSupplyChainConfig = () => {
return {
ver: '1.0',
complete: 1,
nodes: [
{
asi: 'rubicon.com',
sid: '1234',
hp: 1,
rid: 'bid-request-1',
name: 'pub one',
domain: 'pub1.com'
},
{
asi: 'theexchange.com',
sid: '5678',
hp: 1,
rid: 'bid-request-2',
name: 'pub two',
domain: 'pub2.com'
},
{
asi: 'wesellads.com',
sid: '9876',
hp: 1,
rid: 'bid-request-3',
// name: 'alladsallthetime',
domain: 'alladsallthetime.com'
}
]
};
};

beforeEach(() => {
bidRequests = getBidderRequest();
schainConfig = getSupplyChainConfig();
bidRequests.bids[0].schain = schainConfig;
});

it('should properly serialize schain object with correct delimiters', () => {
const results = spec.buildRequests(bidRequests.bids, bidRequests);
const numNodes = schainConfig.nodes.length;
const schain = parseQuery(results[0].data).rp_schain;

// each node serialization should start with an !
expect(schain.match(/!/g).length).to.equal(numNodes);

// 5 commas per node plus 1 for version
expect(schain.match(/,/g).length).to.equal(numNodes * 5 + 1);
});

it('should send the proper version for the schain', () => {
const results = spec.buildRequests(bidRequests.bids, bidRequests);
const schain = parseQuery(results[0].data).rp_schain.split('!');
const version = schain.shift().split(',')[0];
expect(version).to.equal(bidRequests.bids[0].schain.ver);
});

it('should send the correct value for complete in schain', () => {
const results = spec.buildRequests(bidRequests.bids, bidRequests);
const schain = parseQuery(results[0].data).rp_schain.split('!');
const complete = schain.shift().split(',')[1];
expect(complete).to.equal(String(bidRequests.bids[0].schain.complete));
});

it('should send available params in the right order', () => {
const results = spec.buildRequests(bidRequests.bids, bidRequests);
const schain = parseQuery(results[0].data).rp_schain.split('!');
schain.shift();

schain.forEach((serializeNode, nodeIndex) => {
const nodeProps = serializeNode.split(',');
nodeProps.forEach((nodeProp, propIndex) => {
const node = schainConfig.nodes[nodeIndex];
const key = nodePropsOrder[propIndex];
expect(nodeProp).to.equal(node[key] ? String(node[key]) : '');
});
});
});

it('should copy the schain JSON to to bid.source.ext.schain', () => {
createVideoBidderRequest();
const schain = getSupplyChainConfig();
bidderRequest.bids[0].schain = schain;
const request = spec.buildRequests(bidderRequest.bids, bidderRequest);
expect(request[0].data.source.ext.schain).to.deep.equal(schain);
});
});
});