Ghost/ghost/members-api/lib/repositories/event.js

265 lines
8.0 KiB
JavaScript

module.exports = class EventRepository {
constructor({
MemberSubscribeEvent,
MemberPaymentEvent,
MemberStatusEvent,
MemberLoginEvent,
MemberPaidSubscriptionEvent
}) {
this._MemberSubscribeEvent = MemberSubscribeEvent;
this._MemberPaidSubscriptionEvent = MemberPaidSubscriptionEvent;
this._MemberPaymentEvent = MemberPaymentEvent;
this._MemberStatusEvent = MemberStatusEvent;
this._MemberLoginEvent = MemberLoginEvent;
}
async registerPayment(data) {
await this._MemberPaymentEvent.add({
...data,
source: 'stripe'
});
}
async getNewsletterSubscriptionEvents(options = {}) {
options.withRelated = ['member'];
const {data: models, meta} = await this._MemberSubscribeEvent.findPage(options);
const data = models.map((data) => {
return {
type: 'newsletter_event',
data: data.toJSON(options)
};
});
return {
data,
meta
};
}
async getSubscriptionEvents(options = {}) {
options.withRelated = ['member'];
const {data: models, meta} = await this._MemberPaidSubscriptionEvent.findPage(options);
const data = models.map((data) => {
return {
type: 'subscription_event',
data: data.toJSON(options)
};
});
return {
data,
meta
};
}
async getPaymentEvents(options = {}) {
options.withRelated = ['member'];
const {data: models, meta} = await this._MemberPaymentEvent.findPage(options);
const data = models.map((data) => {
return {
type: 'payment_event',
data: data.toJSON(options)
};
});
return {
data,
meta
};
}
async getLoginEvents(options = {}) {
options.withRelated = ['member'];
const {data: models, meta} = await this._MemberLoginEvent.findPage(options);
const data = models.map((data) => {
return {
type: 'login_event',
data: data.toJSON(options)
};
});
return {
data,
meta
};
}
async getSignupEvents(options = {}) {
options.withRelated = ['member'];
options.filter = 'from_status:null';
const {data: models, meta} = await this._MemberStatusEvent.findPage(options);
const data = models.map((data) => {
return {
type: 'signup_event',
data: data.toJSON(options)
};
});
return {
data,
meta
};
}
async getEventTimeline(options = {}) {
if (!options.limit) {
options.limit = 10;
}
options.order = 'created_at desc';
const allEventPages = await Promise.all([
this.getNewsletterSubscriptionEvents(options),
this.getSubscriptionEvents(options),
this.getLoginEvents(options),
this.getSignupEvents(options)
]);
const allEvents = allEventPages.reduce((allEvents, page) => allEvents.concat(page.data), []);
return allEvents.sort((a, b) => {
return new Date(b.data.created_at) - new Date(a.data.created_at);
}).reduce((memo, event, i) => {
if (event.type === 'newsletter_event' && event.data.subscribed) {
const previousEvent = allEvents[i - 1];
const nextEvent = allEvents[i + 1];
const currentMember = event.data.member_id;
if (previousEvent && previousEvent.type === 'signup_event') {
const previousMember = previousEvent.data.member_id;
if (currentMember === previousMember) {
return memo;
}
}
if (nextEvent && nextEvent.type === 'signup_event') {
const nextMember = nextEvent.data.member_id;
if (currentMember === nextMember) {
return memo;
}
}
}
return memo.concat(event);
}, []).slice(0, options.limit);
}
async getSubscriptions() {
const results = await this._MemberSubscribeEvent.findAll({
aggregateSubscriptionDeltas: true
});
const resultsJSON = results.toJSON();
const cumulativeResults = resultsJSON.reduce((cumulativeResults, result, index) => {
if (index === 0) {
return [{
date: result.date,
subscribed: result.subscribed_delta
}];
}
return cumulativeResults.concat([{
date: result.date,
subscribed: result.subscribed_delta + cumulativeResults[index - 1].subscribed
}]);
}, []);
return cumulativeResults;
}
async getMRR() {
const results = await this._MemberPaidSubscriptionEvent.findAll({
aggregateMRRDeltas: true
});
const resultsJSON = results.toJSON();
const cumulativeResults = resultsJSON.reduce((cumulativeResults, result) => {
if (!cumulativeResults[result.currency]) {
return {
...cumulativeResults,
[result.currency]: [{
date: result.date,
mrr: result.mrr_delta,
currency: result.currency
}]
};
}
return {
...cumulativeResults,
[result.currency]: cumulativeResults[result.currency].concat([{
date: result.date,
mrr: result.mrr_delta + cumulativeResults[result.currency].slice(-1)[0].mrr,
currency: result.currency
}])
};
}, {});
return cumulativeResults;
}
async getVolume() {
const results = await this._MemberPaymentEvent.findAll({
aggregatePaymentVolume: true
});
const resultsJSON = results.toJSON();
const cumulativeResults = resultsJSON.reduce((cumulativeResults, result) => {
if (!cumulativeResults[result.currency]) {
return {
...cumulativeResults,
[result.currency]: [{
date: result.date,
volume: result.volume_delta,
currency: result.currency
}]
};
}
return {
...cumulativeResults,
[result.currency]: cumulativeResults[result.currency].concat([{
date: result.date,
volume: result.volume_delta + cumulativeResults[result.currency].slice(-1)[0].volume,
currency: result.currency
}])
};
}, {});
return cumulativeResults;
}
async getStatuses() {
const results = await this._MemberStatusEvent.findAll({
aggregateStatusCounts: true
});
const resultsJSON = results.toJSON();
const cumulativeResults = resultsJSON.reduce((cumulativeResults, result, index) => {
if (index === 0) {
return [{
date: result.date,
paid: result.paid_delta,
comped: result.comped_delta,
free: result.free_delta
}];
}
return cumulativeResults.concat([{
date: result.date,
paid: result.paid_delta + cumulativeResults[index - 1].paid,
comped: result.comped_delta + cumulativeResults[index - 1].comped,
free: result.free_delta + cumulativeResults[index - 1].free
}]);
}, []);
return cumulativeResults;
}
};