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

AIP #6 impl #267

Merged
merged 9 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 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
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions routes/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,23 @@ module.exports = {
.keys()
.pattern(
/^(IMPRESSION|CLICK)$/,
Joi.object({ min: numericString.default('0'), max: numericString.default('0') })
Joi.object({ min: numericString.required(), max: numericString.required() })
Copy link
Member

Choose a reason for hiding this comment

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

why does this no longer have a default?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This because if it has default '0', then the behavior becomes unreliable if an advertiser specifies one property and omits the next. e.g. specifies min omits max

),
eventSubmission: Joi.object({ allow: Joi.array().items(Joi.object()) }),
nonce: Joi.string(),
created: Joi.number(),
activeFrom: Joi.number()
activeFrom: Joi.number(),
priceMultiplicationRules: Joi.array().items(
Joi.object({
multiplier: Joi.number().precision(10), // max 10 decimal places
amount: numericString,
evType: Joi.array().items(Joi.string().lowercase()),
country: Joi.array().items(Joi.string().lowercase()),
publisher: Joi.array().items(Joi.string()),
osType: Joi.array().items(Joi.string().lowercase())
})
),
priceDynamicAdjustment: Joi.bool()
}).required()
},
validatorMessage: {
Expand Down
2 changes: 1 addition & 1 deletion services/sentry/eventAggregator.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function makeRecorder(channelId) {
// this will be saved in the channel object, which is passed into the eventReducer

// Record the events
aggr = events.reduce(eventReducer.reduce.bind(null, channel), aggr)
aggr = events.reduce(eventReducer.reduce.bind(null, channel, session), aggr)
if (cfg.AGGR_THROTTLE) {
throttledPersistAndReset()
return { success: true }
Expand Down
4 changes: 2 additions & 2 deletions services/sentry/lib/eventReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ function newAggr(channelId) {
return { channelId, created: new Date(), events: {}, totals: {}, earners: [] }
}

function reduce(channel, initialAggr, ev) {
function reduce(channel, session, initialAggr, event) {
let aggr = { ...initialAggr }

const ev = { ...event, ...session } // add session details (i.e country, device type etc) to event
Copy link
Member

Choose a reason for hiding this comment

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

why don't you just pass the session to getPayout

Copy link
Member

Choose a reason for hiding this comment

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

please answer this @samparsky

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i have changed it to pass session

const payout = getPayout(channel, ev)
if (payout) {
aggr.events[ev.type] = mergeEv(initialAggr.events[ev.type], payout)
Expand Down
78 changes: 72 additions & 6 deletions services/sentry/lib/getPayout.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,84 @@
/* eslint-disable no-nested-ternary */
// const BN = require('bignumber.js') // allows
const BN = require('bn.js')
const toBalancesKey = require('../toBalancesKey')

function getPayout(channel, ev) {
function getPayout(channel, ev, session) {
if (ev.type === 'IMPRESSION' && ev.publisher) {
// add the minimum price for the event to the current amount
return [toBalancesKey(ev.publisher), new BN(channel.spec.minPerImpression || 1)]
const [minPrice, maxPrice] = getPriceBounds(channel.spec, ev.type)
const price = channel.spec.priceMultiplicationRules
? payout(channel.spec.priceMultiplicationRules, ev, session, maxPrice, minPrice)
: minPrice
return [toBalancesKey(ev.publisher), new BN(price.toString())]
}
if (ev.type === 'CLICK' && ev.publisher) {
return [
toBalancesKey(ev.publisher),
new BN((channel.spec.pricingBounds && channel.spec.pricingBounds.CLICK.min) || 0)
]
const [minPrice, maxPrice] = getPriceBounds(channel.spec, ev.type)
const price = channel.spec.priceMultiplicationRules
? payout(channel.spec.priceMultiplicationRules, ev, session, maxPrice, minPrice)
: minPrice
return [toBalancesKey(ev.publisher), new BN(price.toString())]
}
return null
}

function payout(rules, ev, session, maxPrice, startPrice) {
const match = isRuleMatching.bind(null, ev, session)
const matchingRules = rules.filter(match)
let finalPrice = startPrice

if (matchingRules.length > 0) {
const divisionExponent = new BN(10).pow(new BN(18, 10))
const firstFixed = matchingRules.find(x => x.amount)
const priceByRules = firstFixed
? new BN(firstFixed.amount)
: startPrice
.mul(
new BN(
(
matchingRules
.filter(x => x.multiplier)
.map(x => x.multiplier)
.reduce((a, b) => a * b, 1) * 1e18
).toString()
)
)
.div(divisionExponent)
finalPrice = BN.min(maxPrice, priceByRules)
}

return finalPrice
}

function isRuleMatching(ev, session, rule) {
return rule.eventType
? rule.eventType.includes(ev.type.toLowerCase())
: true && rule.publisher
? rule.publisher.includes(ev.publisher)
: true && rule.osType
? rule.osType.includes(session.os && session.os.toLowerCase())
: true && rule.country
? rule.country.includes(session.country && session.country.toLowerCase())
: true
}

function getPriceBounds(spec, evType) {
const { pricingBounds, minPerImpression, maxPerImpression } = spec
if (evType === 'IMPRESSION') {
return (
(pricingBounds &&
pricingBounds[evType] && [
new BN(pricingBounds[evType].min),
new BN(pricingBounds[evType].max)
]) || [new BN(minPerImpression || 1), new BN(maxPerImpression || 1)]
)
}
return (
(pricingBounds &&
pricingBounds[evType] && [
new BN(pricingBounds[evType].min),
new BN(pricingBounds[evType].max)
]) || [new BN(0), new BN(0)]
)
}
module.exports = getPayout
Loading