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

Floors new signals #5295

Merged
merged 4 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from 3 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
34 changes: 22 additions & 12 deletions modules/priceFloors.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,20 +274,22 @@ export function getFloorDataFromAdUnits(adUnits) {
/**
* @summary This function takes the adUnits for the auction and update them accordingly as well as returns the rules hashmap for the auction
*/
export function updateAdUnitsForAuction(adUnits, floorData, skipped, auctionId) {
export function updateAdUnitsForAuction(adUnits, floorData, auctionId) {
adUnits.forEach((adUnit) => {
adUnit.bids.forEach(bid => {
if (skipped) {
if (floorData.skipped) {
delete bid.getFloor;
} else {
bid.getFloor = getFloor;
}
// information for bid and analytics adapters
bid.auctionId = auctionId;
bid.floorData = {
skipped,
modelVersion: utils.deepAccess(floorData, 'data.modelVersion') || '',
location: floorData.data.location,
skipped: floorData.skipped,
modelVersion: utils.deepAccess(floorData, 'data.modelVersion'),
location: utils.deepAccess(floorData, 'data.location'),
skipRate: floorData.skipRate,
fetchStatus: _floorsConfig.fetchStatus
}
});
});
Expand All @@ -306,14 +308,17 @@ export function createFloorsDataForAuction(adUnits, auctionId) {
} else {
resolvedFloorsData.data = getFloorsDataForAuction(resolvedFloorsData.data);
}
// if we still do not have a valid floor data then floors is not on for this auction
// if we still do not have a valid floor data then floors is not on for this auction, so skip
if (Object.keys(utils.deepAccess(resolvedFloorsData, 'data.values') || {}).length === 0) {
return;
resolvedFloorsData.skipped = true;
} else {
// determine the skip rate now
const auctionSkipRate = utils.getParameterByName('pbjs_skipRate') || resolvedFloorsData.skipRate;
const isSkipped = Math.random() * 100 < parseFloat(auctionSkipRate);
resolvedFloorsData.skipped = isSkipped;
}
// determine the skip rate now
const isSkipped = Math.random() * 100 < parseFloat(utils.deepAccess(resolvedFloorsData, 'data.skipRate') || 0);
resolvedFloorsData.skipped = isSkipped;
updateAdUnitsForAuction(adUnits, resolvedFloorsData, isSkipped, auctionId);
// add floorData to bids
updateAdUnitsForAuction(adUnits, resolvedFloorsData, auctionId);
return resolvedFloorsData;
}

Expand Down Expand Up @@ -389,6 +394,7 @@ export function isFloorsDataValid(floorsData) {
*/
export function parseFloorData(floorsData, location) {
if (floorsData && typeof floorsData === 'object' && isFloorsDataValid(floorsData)) {
utils.logInfo(`${MODULE_NAME}: A ${location} set the auction floor data set to `, floorsData);
return {
...floorsData,
location
Expand Down Expand Up @@ -416,6 +422,7 @@ export function requestBidsHook(fn, reqBidsConfigObj) {
if (_floorsConfig.auctionDelay > 0 && fetching) {
hookConfig.timer = setTimeout(() => {
utils.logWarn(`${MODULE_NAME}: Fetch attempt did not return in time for auction`);
_floorsConfig.fetchStatus = 'timeout';
continueAuction(hookConfig);
}, _floorsConfig.auctionDelay);
_delayedAuctions.push(hookConfig);
Expand Down Expand Up @@ -443,6 +450,7 @@ function resumeDelayedAuctions() {
*/
export function handleFetchResponse(fetchResponse) {
fetching = false;
_floorsConfig.fetchStatus = 'success';
let floorResponse;
try {
floorResponse = JSON.parse(fetchResponse);
Expand All @@ -458,7 +466,8 @@ export function handleFetchResponse(fetchResponse) {

function handleFetchError(status) {
fetching = false;
utils.logError(`${MODULE_NAME}: Fetch errored with: ${status}`);
_floorsConfig.fetchStatus = 'error';
utils.logError(`${MODULE_NAME}: Fetch errored with: `, status);

// if any auctions are waiting for fetch to finish, we need to continue them!
resumeDelayedAuctions();
Expand Down Expand Up @@ -505,6 +514,7 @@ export function handleSetFloorsConfig(config) {
'enabled', enabled => enabled !== false, // defaults to true
'auctionDelay', auctionDelay => auctionDelay || 0,
'endpoint', endpoint => endpoint || {},
'skipRate', () => !isNaN(utils.deepAccess(config, 'data.skipRate')) ? config.data.skipRate : config.skipRate || 0,
'enforcement', enforcement => utils.pick(enforcement || {}, [
'enforceJS', enforceJS => enforceJS !== false, // defaults to true
'enforcePBS', enforcePBS => enforcePBS === true, // defaults to false
Expand Down
6 changes: 4 additions & 2 deletions modules/rubiconAnalyticsAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,12 @@ function sendMessage(auctionId, bidWonId) {
if (auctionCache.floorData) {
auction.floors = utils.pick(auctionCache.floorData, [
'location',
'modelName', () => auctionCache.floorData.modelVersion || '',
'modelName', () => auctionCache.floorData.modelVersion,
'skipped',
'enforcement', () => utils.deepAccess(auctionCache.floorData, 'enforcements.enforceJS'),
'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals')
'dealsEnforced', () => utils.deepAccess(auctionCache.floorData, 'enforcements.floorDeals'),
'skipRate', skipRate => !isNaN(skipRate) ? skipRate : 0,
'fetchFailed'
]);
}

Expand Down
89 changes: 88 additions & 1 deletion test/spec/modules/priceFloors_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,13 @@ describe('the price floors module', function () {
data: undefined
});
runStandardAuction();
validateBidRequests(false, undefined);
validateBidRequests(false, {
skipped: true,
modelVersion: undefined,
location: undefined,
skipRate: 0,
fetchStatus: undefined
});
});
it('should use adUnit level data if not setConfig or fetch has occured', function () {
handleSetFloorsConfig({
Expand Down Expand Up @@ -344,6 +350,8 @@ describe('the price floors module', function () {
skipped: false,
modelVersion: 'adUnit Model Version',
location: 'adUnit',
skipRate: 0,
fetchStatus: undefined
});
});
it('bidRequests should have getFloor function and flooring meta data when setConfig occurs', function () {
Expand All @@ -353,6 +361,53 @@ describe('the price floors module', function () {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: undefined
});
});
it('should take the right skipRate depending on input', function () {
// first priority is data object
sandbox.stub(Math, 'random').callsFake(() => 0.99);
let inputFloors = {
...basicFloorConfig,
skipRate: 10,
data: {
...basicFloorData,
skipRate: 50
}
};
handleSetFloorsConfig(inputFloors);
runStandardAuction();
validateBidRequests(true, {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 50,
fetchStatus: undefined
});

// if that does not exist uses topLevel skipRate setting
delete inputFloors.data.skipRate;
handleSetFloorsConfig(inputFloors);
runStandardAuction();
validateBidRequests(true, {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 10,
fetchStatus: undefined
});

// if that is not there defaults to zero
delete inputFloors.skipRate;
handleSetFloorsConfig(inputFloors);
runStandardAuction();
validateBidRequests(true, {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: undefined
});
});
it('should not overwrite previous data object if the new one is bad', function () {
Expand All @@ -378,6 +433,8 @@ describe('the price floors module', function () {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: undefined
});
});
it('should dynamically add new schema fileds and functions if added via setConfig', function () {
Expand Down Expand Up @@ -453,6 +510,8 @@ describe('the price floors module', function () {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: 'timeout'
});
fakeFloorProvider.respond();
});
Expand Down Expand Up @@ -482,10 +541,33 @@ describe('the price floors module', function () {
expect(exposedAdUnits).to.not.be.undefined;

// the exposedAdUnits should be from the fetch not setConfig level data
// and fetchStatus is success since fetch worked
validateBidRequests(true, {
skipped: false,
modelVersion: 'fetch model name',
location: 'fetch',
skipRate: 0,
fetchStatus: 'success'
});
});
it('Should not break if floor provider returns 404', function () {
// run setConfig indicating fetch
handleSetFloorsConfig({...basicFloorConfig, auctionDelay: 250, endpoint: {url: 'http://www.fakeFloorProvider.json'}});

// run the auction and make server respond with 404
fakeFloorProvider.respond();
runStandardAuction();

// error should have been called for fetch error
expect(logErrorSpy.calledOnce).to.equal(true);
// should have caught the response error and still used setConfig data
// and fetch failed is true
validateBidRequests(true, {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: 'error'
});
});
it('Should not break if floor provider returns non json', function () {
Expand All @@ -498,11 +580,16 @@ describe('the price floors module', function () {
fakeFloorProvider.respond();
runStandardAuction();

// error should have been called for response floor data not being valid
expect(logErrorSpy.calledOnce).to.equal(true);
// should have caught the response error and still used setConfig data
// and fetchStatus is 'success' but location is setConfig since it had bad data
validateBidRequests(true, {
skipped: false,
modelVersion: 'basic model',
location: 'setConfig',
skipRate: 0,
fetchStatus: 'success'
});
});
it('should handle not using fetch correctly', function () {
Expand Down
6 changes: 4 additions & 2 deletions test/spec/modules/rubiconAnalyticsAdapter_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,8 @@ describe('rubicon analytics adapter', function () {
auctionInit.bidderRequests[0].bids[0].floorData = {
skipped: false,
modelVersion: 'someModelName',
location: 'setConfig'
location: 'setConfig',
skipRate: 15
};
let flooredResponse = {
...BID,
Expand Down Expand Up @@ -733,7 +734,8 @@ describe('rubicon analytics adapter', function () {
modelName: 'someModelName',
skipped: false,
enforcement: true,
dealsEnforced: false
dealsEnforced: false,
skipRate: 15
});
// first adUnit's adSlot
expect(message.auctions[0].adUnits[0].adSlot).to.equal('12345/sports');
Expand Down