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

Standardizing First Party Data #4269

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 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
e86d6d0
Changes to set/get config and server adapter
Oct 4, 2019
bcabaf1
Use protected data if present within Rubicon adapter
Oct 7, 2019
324d090
Remove structure not supported in IE
Oct 8, 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
89f08d6
Merge branch 'master' of https://github.com/rubicon-project/Prebid.js…
Oct 17, 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
12 changes: 12 additions & 0 deletions modules/prebidServerBidAdapter/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,18 @@ const OPEN_RTB_PROTOCOL = {
utils.deepSetValue(request, 'regs.coppa', 1);
}

if (!utils.isEmpty(s2sBidRequest.protected)) {
utils.deepSetValue(request, 'ext.prebid.data.bidders', s2sBidRequest.protected.allowedBidders);

if (s2sBidRequest.protected.context) {
utils.deepSetValue(request, 'site.ext.data', s2sBidRequest.protected.context);
}

if (s2sBidRequest.protected.user) {
utils.deepSetValue(request, 'user.ext.data', s2sBidRequest.protected.user);
}
}

return request;
},

Expand Down
51 changes: 36 additions & 15 deletions modules/rubiconBidAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,20 @@ export const spec = {
utils.deepSetValue(data, 'regs.coppa', 1);
}

const siteData = Object.assign({}, bidRequest.params.inventory, bidderRequest.protected.context);
const userData = Object.assign({}, bidRequest.params.visitor, bidderRequest.protected.user);
if (!utils.isEmpty(siteData) || !utils.isEmpty(userData)) {
utils.deepSetValue(data, 'ext.prebid.data.bidders', [ bidderRequest.bidderCode ]);

if (!utils.isEmpty(siteData)) {
utils.deepSetValue(data, 'site.ext.data', siteData);
}

if (!utils.isEmpty(userData)) {
utils.deepSetValue(data, 'user.ext.data', userData);
}
}

return {
method: 'POST',
url: VIDEO_ENDPOINT,
Expand Down Expand Up @@ -400,7 +414,6 @@ export const spec = {
'tk_flint': `${configIntType || DEFAULT_INTEGRATION}_v$prebid.version$`,
'x_source.tid': bidRequest.transactionId,
'p_screen_res': _getScreenResolution(),
'kw': Array.isArray(params.keywords) ? params.keywords.join(',') : '',
'tk_user_key': params.userId,
'p_geo.latitude': isNaN(parseFloat(latitude)) ? undefined : parseFloat(latitude).toFixed(4),
'p_geo.longitude': isNaN(parseFloat(longitude)) ? undefined : parseFloat(longitude).toFixed(4),
Expand All @@ -426,22 +439,30 @@ export const spec = {
}

// visitor properties
if (params.visitor !== null && typeof params.visitor === 'object') {
Object.keys(params.visitor).forEach((key) => {
if (params.visitor[key] != null) {
data[`tg_v.${key}`] = params.visitor[key].toString(); // initialize array;
}
});
}
const visitorData = Object.assign({}, params.visitor, bidderRequest.protected.user);
Object.keys(visitorData).forEach((key) => {
if (visitorData[key] != null && key !== 'keywords') {
data[`tg_v.${key}`] = typeof visitorData[key] === 'object' && !Array.isArray(visitorData[key])
? JSON.stringify(visitorData[key])
: visitorData[key].toString(); // initialize array;
}
});

// inventory properties
if (params.inventory !== null && typeof params.inventory === 'object') {
Object.keys(params.inventory).forEach((key) => {
if (params.inventory[key] != null) {
data[`tg_i.${key}`] = params.inventory[key].toString();
}
});
}
const inventoryData = Object.assign({}, params.inventory, bidderRequest.protected.context);
Object.keys(inventoryData).forEach((key) => {
if (inventoryData[key] != null && key !== 'keywords') {
data[`tg_i.${key}`] = typeof inventoryData[key] === 'object' && !Array.isArray(inventoryData[key])
? JSON.stringify(inventoryData[key])
: inventoryData[key].toString();
}
});

// keywords
const keywords = (params.keywords || []).concat(
utils.deepAccess(bidderRequest, 'protected.user.keywords') || [],
utils.deepAccess(bidderRequest, 'protected.context.keywords') || []);
data.kw = keywords.length ? keywords.join(',') : '';

// digitrust properties
const digitrustParams = _getDigiTrustQueryParams(bidRequest, 'FASTLANE');
Expand Down
5 changes: 4 additions & 1 deletion src/adapterManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getLabels, resolveStatus } from './sizeMapping';
import { processNativeAdUnitParams, nativeAdapters } from './native';
import { newBidder } from './adapters/bidderFactory';
import { ajaxBuilder } from './ajax';
import { config, RANDOM } from './config';
import { config, PRE_BID_SERVER_KEY, RANDOM } from './config';
import includes from 'core-js/library/fn/array/includes';
import find from 'core-js/library/fn/array/find';
import { adunitCounter } from './adUnits';
Expand Down Expand Up @@ -309,6 +309,8 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request
events.emit(CONSTANTS.EVENTS.BID_REQUESTED, bidRequest);
});

s2sBidRequest.protected = config.getProtectedData(PRE_BID_SERVER_KEY);

// make bid requests
s2sAdapter.callBids(
s2sBidRequest,
Expand Down Expand Up @@ -337,6 +339,7 @@ adapterManager.callBids = (adUnits, bidRequests, addBidResponse, doneCb, request
request: requestCallbacks.request.bind(null, bidRequest.bidderCode),
done: requestCallbacks.done
} : undefined);
bidRequest.protected = config.getProtectedData(bidRequest.bidderCode);
adapter.callBids(bidRequest, addBidResponse.bind(bidRequest), doneCb.bind(bidRequest), ajax, onTimelyResponse);
});
}
Expand Down
37 changes: 36 additions & 1 deletion src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const GRANULARITY_OPTIONS = {

const ALL_TOPICS = '*';

export const PRE_BID_SERVER_KEY = 'preBidServer';
const KNOWN_PROTECTED_KEYS = [ 'context', 'user' ];

/**
* @typedef {object} PrebidConfig
*
Expand All @@ -49,6 +52,7 @@ export function newConfig() {
let listeners = [];
let defaults;
let config;
let protectedConfig;

function resetConfig() {
defaults = {};
Expand Down Expand Up @@ -182,6 +186,7 @@ export function newConfig() {
}

config = newConfig;
protectedConfig = {};

function hasGranularity(val) {
return find(Object.keys(GRANULARITY_OPTIONS), option => val === GRANULARITY_OPTIONS[option]);
Expand Down Expand Up @@ -223,16 +228,45 @@ export function newConfig() {
return subscribe(...args);
}

function getProtectedData(bidderCode) {
if (typeof bidderCode !== 'string' || !protectedConfig.hasOwnProperty(bidderCode)) {
return {};
}

return protectedConfig[bidderCode];
}

/*
* Sets configuration given an object containing key-value pairs and calls
* listeners that were added by the `subscribe` function
*/
function setConfig(options) {
function setConfig(options, allowedBiddersObj) {
if (typeof options !== 'object') {
utils.logError('setConfig options must be an object');
return;
}

if (typeof allowedBiddersObj === 'object' && Array.isArray(allowedBiddersObj.allowedBidders) && allowedBiddersObj.allowedBidders.length) {
const allowedBidders = allowedBiddersObj.allowedBidders;

allowedBidders.forEach(allowedBidder => {
protectedConfig[allowedBidder] = Object.assign({}, protectedConfig[allowedBidder], options);
});

const knownProtectedKeys = Object.keys(options).filter(key => includes(KNOWN_PROTECTED_KEYS, key));
if (knownProtectedKeys.length) {
const knownProtectedConfigs = knownProtectedKeys.reduce((result, key) => Object.assign(result, { [key]: options[key] }), {});
protectedConfig[PRE_BID_SERVER_KEY] = Object.assign({}, protectedConfig[PRE_BID_SERVER_KEY], knownProtectedConfigs);
const allowedBiddersSet = new Set();
allowedBidders.concat(protectedConfig[PRE_BID_SERVER_KEY].allowedBidders || []).forEach(allowedBidder => allowedBiddersSet.add(allowedBidder));
const uniqAllowedBidders = [];
allowedBiddersSet.forEach(uniqueBidder => uniqAllowedBidders.push(uniqueBidder));
protectedConfig[PRE_BID_SERVER_KEY].allowedBidders = uniqAllowedBidders;
}

return;
}

let topics = Object.keys(options);
let topicalConfig = {};

Expand Down Expand Up @@ -331,6 +365,7 @@ export function newConfig() {

return {
getConfig,
getProtectedData,
setConfig,
setDefaults,
resetConfig
Expand Down
40 changes: 39 additions & 1 deletion test/spec/config_spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { expect } from 'chai';
import { assert } from 'chai';
import { newConfig } from 'src/config';
import { newConfig, PRE_BID_SERVER_KEY } from 'src/config';

const utils = require('src/utils');

let getConfig;
let getProtectedData;
let setConfig;
let setDefaults;

Expand All @@ -14,6 +15,7 @@ describe('config API', function () {
beforeEach(function () {
const config = newConfig();
getConfig = config.getConfig;
getProtectedData = config.getProtectedData;
setConfig = config.setConfig;
setDefaults = config.setDefaults;
logErrorSpy = sinon.spy(utils, 'logError');
Expand Down Expand Up @@ -189,4 +191,40 @@ describe('config API', function () {
setConfig({ bidderSequence: 'random' });
expect(logWarnSpy.called).to.equal(false);
});

it('should store data only allowed for certain bidders separate from normal config object', () => {
setConfig({ foo: 'bar' }, { allowedBidders: [ 'rubicon', 'appnexus' ] });
expect(getConfig('foo')).to.be.undefined;
expect(getConfig()).to.not.have.property('foo');
});

it('should only return protected data that specified bidder has access to', () => {
const context = {
keywords: ['power tools'],
search: 'drill',
content: { userrating: 4 },
data: {
pageType: 'article',
category: 'tools'
}
};

const user = {
keywords: ['a', 'b'],
gender: 'M',
yob: '1984',
geo: { country: 'ca' },
data: {
registered: true,
interests: ['cars']
}
};

setConfig({ context, user, foo: 'bar' }, { allowedBidders: [ 'rubicon', 'appnexus' ] });
setConfig({ oneBidderOnly: 'secret' }, { allowedBidders: [ 'rubicon' ] });
expect(getProtectedData()).to.be.empty;
expect(getProtectedData('rubicon')).to.deep.equal({ context, user, foo: 'bar', oneBidderOnly: 'secret' });
expect(getProtectedData('appnexus')).to.deep.equal({ context, user, foo: 'bar', });
expect(getProtectedData(PRE_BID_SERVER_KEY)).to.deep.equal({ context, user, allowedBidders: [ 'rubicon', 'appnexus' ] });
});
});
38 changes: 36 additions & 2 deletions test/spec/modules/prebidServerBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ const REQUEST = {
}
]
}
]
],
protected: {}
};

const VIDEO_REQUEST = {
Expand Down Expand Up @@ -1261,7 +1262,40 @@ describe('S2S Adapter', function () {
adapter.callBids(REQUEST, bidRequests, addBidResponse, done, ajax);
const parsedRequestBody = JSON.parse(requests[0].requestBody);
expect(parsedRequestBody.source.ext.schain).to.deep.equal(schainObject);
})
});

it('passes first party data in request', () => {
const s2sBidRequest = utils.deepClone(REQUEST);
const bidRequests = utils.deepClone(BID_REQUESTS);

const context = {
keywords: ['power tools'],
search: 'drill',
content: { userrating: 4 },
data: {
pageType: 'article',
category: 'tools'
}
};
const user = {
keywords: ['a', 'b'],
gender: 'M',
yob: '1984',
geo: { country: 'ca' },
data: {
registered: true,
interests: ['cars']
}
};
const allowedBidders = [ 'rubicon', 'appnexus' ];

s2sBidRequest.protected = { context, user, allowedBidders };
adapter.callBids(s2sBidRequest, bidRequests, addBidResponse, done, ajax);
const parsedRequestBody = JSON.parse(requests[0].requestBody);
expect(parsedRequestBody.ext.prebid.data.bidders).to.deep.equal(allowedBidders);
expect(parsedRequestBody.site.ext.data).to.deep.equal(context);
expect(parsedRequestBody.user.ext.data).to.deep.equal(user);
});
});

describe('response handler', function () {
Expand Down
62 changes: 61 additions & 1 deletion test/spec/modules/rubiconBidAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ describe('the rubicon adapter', function () {
],
start: 1472239426002,
auctionStart: 1472239426000,
timeout: 5000
timeout: 5000,
protected: {}
};

sizeMap = [
Expand Down Expand Up @@ -861,6 +862,43 @@ describe('the rubicon adapter', function () {
expect(data[key]).to.equal(value);
});
});

it('should use protected data in bidder request over the bid params, if present', () => {
const context = {
keywords: ['e', 'f'],
rating: '4-star'
};
const user = {
keywords: ['d'],
gender: 'M',
yob: '1984',
geo: { country: 'ca' }
};

const expectedQuery = {
'kw': 'a,b,c,d,e,f',
'tg_v.ucat': 'new',
'tg_v.lastsearch': 'iphone',
'tg_v.likes': 'sports,video games',
'tg_v.gender': 'M',
'tg_v.yob': '1984',
'tg_v.geo': '{"country":"ca"}',
'tg_i.rating': '4-star',
'tg_i.prodtype': 'tech,mobile',
};

bidderRequest.protected = { context, user };

// get the built request
let [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
let data = parseQuery(request.data);

// make sure that tg_v, tg_i, and kw values are correct
Object.keys(expectedQuery).forEach(key => {
let value = expectedQuery[key];
expect(data[key]).to.deep.equal(value);
});
});
});

describe('singleRequest config', function () {
Expand Down Expand Up @@ -1393,6 +1431,28 @@ describe('the rubicon adapter', function () {
const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
expect(request.data.regs.coppa).to.equal(1);
});

it('should include first party data', () => {
createVideoBidderRequest();

const context = {
keywords: ['e', 'f'],
rating: '4-star'
};
const user = {
keywords: ['d'],
gender: 'M',
yob: '1984',
geo: { country: 'ca' }
};

bidderRequest.protected = { context, user };

const [request] = spec.buildRequests(bidderRequest.bids, bidderRequest);
expect(request.data.ext.prebid.data.bidders).to.deep.equal([ 'rubicon' ]);
expect(request.data.site.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.inventory, context));
expect(request.data.user.ext.data).to.deep.equal(Object.assign({}, bidderRequest.bids[0].params.visitor, user));
});
});

describe('combineSlotUrlParams', function () {
Expand Down