mirror of
https://github.com/standardnotes/server
synced 2026-01-18 08:04:28 -05:00
Compare commits
2 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eacd2abc00 | ||
|
|
7393954ff6 |
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.7.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.7.2...@standardnotes/analytics@2.7.3) (2022-11-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **analytics:** arhcitecture arrangements for use case execution ([7393954](https://github.com/standardnotes/server/commit/7393954ff6ece6143f7661104299172548db90ee))
|
||||
|
||||
## [2.7.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.7.1...@standardnotes/analytics@2.7.2) (2022-11-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.7.2",
|
||||
"version": "2.7.3",
|
||||
"engines": {
|
||||
"node": ">=14.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export class Result<T> {
|
||||
constructor(private isSuccess: boolean, private error?: T | string, private value?: T) {
|
||||
constructor(private isSuccess: boolean, private error?: string, private value?: T) {
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ export class Result<T> {
|
||||
|
||||
getValue(): T {
|
||||
if (!this.isSuccess) {
|
||||
throw new Error('Cannot get value of an unsuccessfull result')
|
||||
throw new Error(`Cannot get value of an unsuccessfull result: ${this.error}`)
|
||||
}
|
||||
|
||||
return this.value as T
|
||||
}
|
||||
|
||||
getError(): T | string {
|
||||
getError(): string {
|
||||
if (this.isSuccess || this.error === undefined) {
|
||||
throw new Error('Cannot get an error of a successfull result')
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export class Result<T> {
|
||||
return new Result<U>(true, undefined, value)
|
||||
}
|
||||
|
||||
static fail<U>(error: U | string): Result<U> {
|
||||
static fail<U>(error: string): Result<U> {
|
||||
return new Result<U>(false, error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Period } from '../Time/Period'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RevenueModification } from '../Revenue/RevenueModification'
|
||||
import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/SaveRevenueModification'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('SubscriptionCancelledEventHandler', () => {
|
||||
let event: SubscriptionCancelledEvent
|
||||
@@ -19,11 +20,21 @@ describe('SubscriptionCancelledEventHandler', () => {
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let statisticsStore: StatisticsStoreInterface
|
||||
let saveRevenueModification: SaveRevenueModification
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionCancelledEventHandler(getUserAnalyticsId, analyticsStore, statisticsStore, saveRevenueModification)
|
||||
new SubscriptionCancelledEventHandler(
|
||||
getUserAnalyticsId,
|
||||
analyticsStore,
|
||||
statisticsStore,
|
||||
saveRevenueModification,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
|
||||
getUserAnalyticsId = {} as jest.Mocked<GetUserAnalyticsId>
|
||||
getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 })
|
||||
|
||||
@@ -80,4 +91,14 @@ describe('SubscriptionCancelledEventHandler', () => {
|
||||
expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled()
|
||||
expect(saveRevenueModification.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should log failure to save revenue modification', async () => {
|
||||
saveRevenueModification.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
event.payload.timestamp = 1642395451516000
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(logger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DomainEventHandlerInterface, SubscriptionCancelledEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
|
||||
@@ -20,6 +21,7 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||
@inject(TYPES.SaveRevenueModification) private saveRevenueModification: SaveRevenueModification,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SubscriptionCancelledEvent): Promise<void> {
|
||||
@@ -32,7 +34,7 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
|
||||
|
||||
await this.trackSubscriptionStatistics(event)
|
||||
|
||||
await this.saveRevenueModification.execute({
|
||||
const result = await this.saveRevenueModification.execute({
|
||||
billingFrequency: event.payload.billingFrequency,
|
||||
eventType: SubscriptionEventType.create(event.type).getValue(),
|
||||
newSubscriber: event.payload.userExistingSubscriptionsCount === 1,
|
||||
@@ -42,6 +44,10 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
|
||||
userEmail: Email.create(event.payload.userEmail).getValue(),
|
||||
userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async trackSubscriptionStatistics(event: SubscriptionCancelledEvent) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
|
||||
import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/SaveRevenueModification'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RevenueModification } from '../Revenue/RevenueModification'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('SubscriptionExpiredEventHandler', () => {
|
||||
let event: SubscriptionExpiredEvent
|
||||
@@ -17,11 +18,21 @@ describe('SubscriptionExpiredEventHandler', () => {
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let statisticsStore: StatisticsStoreInterface
|
||||
let saveRevenueModification: SaveRevenueModification
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionExpiredEventHandler(getUserAnalyticsId, analyticsStore, statisticsStore, saveRevenueModification)
|
||||
new SubscriptionExpiredEventHandler(
|
||||
getUserAnalyticsId,
|
||||
analyticsStore,
|
||||
statisticsStore,
|
||||
saveRevenueModification,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<SubscriptionExpiredEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.type = 'SUBSCRIPTION_EXPIRED'
|
||||
@@ -57,4 +68,12 @@ describe('SubscriptionExpiredEventHandler', () => {
|
||||
expect(statisticsStore.setMeasure).toHaveBeenCalled()
|
||||
expect(saveRevenueModification.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should log failure to save revenue modification', async () => {
|
||||
saveRevenueModification.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(logger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DomainEventHandlerInterface, SubscriptionExpiredEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
|
||||
@@ -20,6 +21,7 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||
@inject(TYPES.SaveRevenueModification) private saveRevenueModification: SaveRevenueModification,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SubscriptionExpiredEvent): Promise<void> {
|
||||
@@ -36,7 +38,7 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf
|
||||
[Period.Today, Period.ThisWeek, Period.ThisMonth, Period.ThisYear],
|
||||
)
|
||||
|
||||
await this.saveRevenueModification.execute({
|
||||
const result = await this.saveRevenueModification.execute({
|
||||
billingFrequency: event.payload.billingFrequency,
|
||||
eventType: SubscriptionEventType.create(event.type).getValue(),
|
||||
newSubscriber: event.payload.userExistingSubscriptionsCount === 1,
|
||||
@@ -46,5 +48,9 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf
|
||||
userEmail: Email.create(event.payload.userEmail).getValue(),
|
||||
userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Period } from '../Time/Period'
|
||||
import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/SaveRevenueModification'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RevenueModification } from '../Revenue/RevenueModification'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('SubscriptionPurchasedEventHandler', () => {
|
||||
let event: SubscriptionPurchasedEvent
|
||||
@@ -19,11 +20,21 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let statisticsStore: StatisticsStoreInterface
|
||||
let saveRevenueModification: SaveRevenueModification
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionPurchasedEventHandler(getUserAnalyticsId, analyticsStore, statisticsStore, saveRevenueModification)
|
||||
new SubscriptionPurchasedEventHandler(
|
||||
getUserAnalyticsId,
|
||||
analyticsStore,
|
||||
statisticsStore,
|
||||
saveRevenueModification,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
|
||||
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
|
||||
statisticsStore.incrementMeasure = jest.fn()
|
||||
statisticsStore.setMeasure = jest.fn()
|
||||
@@ -80,4 +91,12 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||
|
||||
expect(analyticsStore.markActivity).toHaveBeenCalledWith(['limited-discount-offer-purchased'], 3, [Period.Today])
|
||||
})
|
||||
|
||||
it('should log failure to save revenue modification', async () => {
|
||||
saveRevenueModification.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(logger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DomainEventHandlerInterface, SubscriptionPurchasedEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
|
||||
@@ -20,6 +21,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||
@inject(TYPES.SaveRevenueModification) private saveRevenueModification: SaveRevenueModification,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SubscriptionPurchasedEvent): Promise<void> {
|
||||
@@ -60,7 +62,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||
)
|
||||
}
|
||||
|
||||
await this.saveRevenueModification.execute({
|
||||
const result = await this.saveRevenueModification.execute({
|
||||
billingFrequency: event.payload.billingFrequency,
|
||||
eventType: SubscriptionEventType.create(event.type).getValue(),
|
||||
newSubscriber: event.payload.newSubscriber,
|
||||
@@ -70,5 +72,9 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||
userEmail: Email.create(event.payload.userEmail).getValue(),
|
||||
userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Period } from '../Time/Period'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RevenueModification } from '../Revenue/RevenueModification'
|
||||
import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/SaveRevenueModification'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('SubscriptionRefundedEventHandler', () => {
|
||||
let event: SubscriptionRefundedEvent
|
||||
@@ -20,11 +21,21 @@ describe('SubscriptionRefundedEventHandler', () => {
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let statisticsStore: StatisticsStoreInterface
|
||||
let saveRevenueModification: SaveRevenueModification
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionRefundedEventHandler(getUserAnalyticsId, analyticsStore, statisticsStore, saveRevenueModification)
|
||||
new SubscriptionRefundedEventHandler(
|
||||
getUserAnalyticsId,
|
||||
analyticsStore,
|
||||
statisticsStore,
|
||||
saveRevenueModification,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<SubscriptionRefundedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.type = 'SUBSCRIPTION_REFUNDED'
|
||||
@@ -88,4 +99,12 @@ describe('SubscriptionRefundedEventHandler', () => {
|
||||
Period.ThisMonth,
|
||||
])
|
||||
})
|
||||
|
||||
it('should log failure to save revenue modification', async () => {
|
||||
saveRevenueModification.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(logger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DomainEventHandlerInterface, SubscriptionRefundedEvent } from '@standardnotes/domain-events'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { AnalyticsActivity } from '../Analytics/AnalyticsActivity'
|
||||
@@ -20,6 +21,7 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||
@inject(TYPES.SaveRevenueModification) private saveRevenueModification: SaveRevenueModification,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SubscriptionRefundedEvent): Promise<void> {
|
||||
@@ -32,7 +34,7 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter
|
||||
|
||||
await this.markChurnActivity(analyticsId, event)
|
||||
|
||||
await this.saveRevenueModification.execute({
|
||||
const result = await this.saveRevenueModification.execute({
|
||||
billingFrequency: event.payload.billingFrequency,
|
||||
eventType: SubscriptionEventType.create(event.type).getValue(),
|
||||
newSubscriber: event.payload.userExistingSubscriptionsCount === 1,
|
||||
@@ -42,6 +44,10 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter
|
||||
userEmail: Email.create(event.payload.userEmail).getValue(),
|
||||
userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async markChurnActivity(analyticsId: number, event: SubscriptionRefundedEvent): Promise<void> {
|
||||
|
||||
@@ -9,17 +9,22 @@ import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface'
|
||||
import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/SaveRevenueModification'
|
||||
import { RevenueModification } from '../Revenue/RevenueModification'
|
||||
import { Result } from '../Core/Result'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('SubscriptionRenewedEventHandler', () => {
|
||||
let event: SubscriptionRenewedEvent
|
||||
let getUserAnalyticsId: GetUserAnalyticsId
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let saveRevenueModification: SaveRevenueModification
|
||||
let logger: Logger
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionRenewedEventHandler(getUserAnalyticsId, analyticsStore, saveRevenueModification)
|
||||
new SubscriptionRenewedEventHandler(getUserAnalyticsId, analyticsStore, saveRevenueModification, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<SubscriptionRenewedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.type = 'SUBSCRIPTION_RENEWED'
|
||||
@@ -52,4 +57,12 @@ describe('SubscriptionRenewedEventHandler', () => {
|
||||
expect(analyticsStore.unmarkActivity).toHaveBeenCalled()
|
||||
expect(saveRevenueModification.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should log failure to save revenue modification', async () => {
|
||||
saveRevenueModification.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(logger.error).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SaveRevenueModification } from '../UseCase/SaveRevenueModification/Save
|
||||
import { Email } from '../Common/Email'
|
||||
import { SubscriptionEventType } from '../Subscription/SubscriptionEventType'
|
||||
import { SubscriptionPlanName } from '../Subscription/SubscriptionPlanName'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterface {
|
||||
@@ -17,6 +18,7 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.SaveRevenueModification) private saveRevenueModification: SaveRevenueModification,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: SubscriptionRenewedEvent): Promise<void> {
|
||||
@@ -32,7 +34,7 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf
|
||||
[Period.Today, Period.ThisWeek, Period.ThisMonth],
|
||||
)
|
||||
|
||||
await this.saveRevenueModification.execute({
|
||||
const result = await this.saveRevenueModification.execute({
|
||||
billingFrequency: event.payload.billingFrequency,
|
||||
eventType: SubscriptionEventType.create(event.type).getValue(),
|
||||
newSubscriber: false,
|
||||
@@ -42,5 +44,9 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf
|
||||
userEmail: Email.create(event.payload.userEmail).getValue(),
|
||||
userUuid,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,18 @@ import { SubscriptionEventType } from '../Subscription/SubscriptionEventType'
|
||||
@injectable()
|
||||
export class RevenueModificationMap implements MapInterface<RevenueModification, TypeORMRevenueModification> {
|
||||
toDomain(persistence: TypeORMRevenueModification): RevenueModification {
|
||||
const user = User.create(
|
||||
const userOrError = User.create(
|
||||
{
|
||||
email: Email.create(persistence.userEmail).getValue(),
|
||||
},
|
||||
new UniqueEntityId(persistence.userUuid),
|
||||
)
|
||||
const subscription = Subscription.create(
|
||||
if (userOrError.isFailed()) {
|
||||
throw new Error(`Could not create user: ${userOrError.getError()}`)
|
||||
}
|
||||
const user = userOrError.getValue()
|
||||
|
||||
const subscriptionOrError = Subscription.create(
|
||||
{
|
||||
billingFrequency: persistence.billingFrequency,
|
||||
isFirstSubscriptionForUser: persistence.isNewCustomer,
|
||||
@@ -29,18 +34,31 @@ export class RevenueModificationMap implements MapInterface<RevenueModification,
|
||||
},
|
||||
new UniqueEntityId(persistence.subscriptionId),
|
||||
)
|
||||
const previousMonthlyRevenueOrError = MonthlyRevenue.create(persistence.previousMonthlyRevenue)
|
||||
if (subscriptionOrError.isFailed()) {
|
||||
throw new Error(`Could not create subscription: ${subscriptionOrError.getError()}`)
|
||||
}
|
||||
const subscription = subscriptionOrError.getValue()
|
||||
|
||||
return RevenueModification.create(
|
||||
const previousMonthlyRevenueOrError = MonthlyRevenue.create(persistence.previousMonthlyRevenue)
|
||||
const newMonthlyRevenueOrError = MonthlyRevenue.create(persistence.newMonthlyRevenue)
|
||||
|
||||
const revenuModificationOrError = RevenueModification.create(
|
||||
{
|
||||
user,
|
||||
subscription,
|
||||
eventType: SubscriptionEventType.create(persistence.eventType).getValue(),
|
||||
previousMonthlyRevenue: previousMonthlyRevenueOrError.getValue(),
|
||||
newMonthlyRevenue: newMonthlyRevenueOrError.getValue(),
|
||||
createdAt: persistence.createdAt,
|
||||
},
|
||||
new UniqueEntityId(persistence.uuid),
|
||||
)
|
||||
|
||||
if (revenuModificationOrError.isFailed()) {
|
||||
throw new Error(`Could not map revenue modification to domain: ${revenuModificationOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revenuModificationOrError.getValue()
|
||||
}
|
||||
|
||||
toPersistence(domain: RevenueModification): TypeORMRevenueModification {
|
||||
@@ -50,7 +68,7 @@ export class RevenueModificationMap implements MapInterface<RevenueModification,
|
||||
persistence.billingFrequency = subscription.props.billingFrequency
|
||||
persistence.eventType = domain.props.eventType.value
|
||||
persistence.isNewCustomer = subscription.props.isFirstSubscriptionForUser
|
||||
persistence.newMonthlyRevenue = domain.newMonthlyRevenue.value
|
||||
persistence.newMonthlyRevenue = domain.props.newMonthlyRevenue.value
|
||||
persistence.previousMonthlyRevenue = domain.props.previousMonthlyRevenue.value
|
||||
persistence.subscriptionId = subscription.id.toValue() as number
|
||||
persistence.subscriptionPlan = subscription.props.planName.value
|
||||
|
||||
@@ -13,7 +13,7 @@ export class MonthlyRevenue extends ValueObject<MonthlyRevenueProps> {
|
||||
|
||||
static create(revenue: number): Result<MonthlyRevenue> {
|
||||
if (isNaN(revenue) || revenue < 0) {
|
||||
return Result.fail<MonthlyRevenue>('Monthly revenue must be a non-negative number')
|
||||
return Result.fail<MonthlyRevenue>(`Monthly revenue must be a non-negative number. Supplied: ${revenue}`)
|
||||
} else {
|
||||
return Result.ok<MonthlyRevenue>(new MonthlyRevenue({ value: revenue }))
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ describe('RevenueModification', () => {
|
||||
isFirstSubscriptionForUser: true,
|
||||
payedAmount: 123,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
})
|
||||
}).getValue()
|
||||
user = User.create({
|
||||
email: Email.create('test@test.te').getValue(),
|
||||
})
|
||||
}).getValue()
|
||||
})
|
||||
|
||||
it('should create an aggregate for purchased subscription', () => {
|
||||
@@ -27,37 +27,12 @@ describe('RevenueModification', () => {
|
||||
createdAt: 2,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
previousMonthlyRevenue: MonthlyRevenue.create(123).getValue(),
|
||||
newMonthlyRevenue: MonthlyRevenue.create(45).getValue(),
|
||||
subscription,
|
||||
user,
|
||||
})
|
||||
}).getValue()
|
||||
|
||||
expect(revenueModification.id.toString()).toHaveLength(36)
|
||||
expect(revenueModification.newMonthlyRevenue.value).toEqual(123 / 12)
|
||||
})
|
||||
|
||||
it('should create an aggregate for subscription expired', () => {
|
||||
const revenueModification = RevenueModification.create({
|
||||
createdAt: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_EXPIRED').getValue(),
|
||||
previousMonthlyRevenue: MonthlyRevenue.create(123).getValue(),
|
||||
subscription,
|
||||
user,
|
||||
})
|
||||
|
||||
expect(revenueModification.id.toString()).toHaveLength(36)
|
||||
expect(revenueModification.newMonthlyRevenue.value).toEqual(0)
|
||||
})
|
||||
|
||||
it('should create an aggregate for subscription cancelled', () => {
|
||||
const revenueModification = RevenueModification.create({
|
||||
createdAt: 2,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_CANCELLED').getValue(),
|
||||
previousMonthlyRevenue: MonthlyRevenue.create(123).getValue(),
|
||||
subscription,
|
||||
user,
|
||||
})
|
||||
|
||||
expect(revenueModification.id.toString()).toHaveLength(36)
|
||||
expect(revenueModification.newMonthlyRevenue.value).toEqual(123)
|
||||
expect(revenueModification.props.newMonthlyRevenue.value).toEqual(45)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Aggregate } from '../Core/Aggregate'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { MonthlyRevenue } from './MonthlyRevenue'
|
||||
import { RevenueModificationProps } from './RevenueModificationProps'
|
||||
|
||||
export class RevenueModification extends Aggregate<RevenueModificationProps> {
|
||||
@@ -8,31 +8,7 @@ export class RevenueModification extends Aggregate<RevenueModificationProps> {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: RevenueModificationProps, id?: UniqueEntityId): RevenueModification {
|
||||
return new RevenueModification(props, id)
|
||||
}
|
||||
|
||||
get newMonthlyRevenue(): MonthlyRevenue {
|
||||
const { subscription } = this.props
|
||||
|
||||
let revenue = 0
|
||||
switch (this.props.eventType.value) {
|
||||
case 'SUBSCRIPTION_PURCHASED':
|
||||
case 'SUBSCRIPTION_RENEWED':
|
||||
case 'SUBSCRIPTION_DATA_MIGRATED':
|
||||
revenue = subscription.props.payedAmount / subscription.props.billingFrequency
|
||||
break
|
||||
case 'SUBSCRIPTION_EXPIRED':
|
||||
case 'SUBSCRIPTION_REFUNDED':
|
||||
revenue = 0
|
||||
break
|
||||
case 'SUBSCRIPTION_CANCELLED':
|
||||
revenue = this.props.previousMonthlyRevenue.value
|
||||
break
|
||||
}
|
||||
|
||||
const monthlyRevenueOrError = MonthlyRevenue.create(revenue)
|
||||
|
||||
return monthlyRevenueOrError.getValue()
|
||||
static create(props: RevenueModificationProps, id?: UniqueEntityId): Result<RevenueModification> {
|
||||
return Result.ok<RevenueModification>(new RevenueModification(props, id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,5 +8,6 @@ export interface RevenueModificationProps {
|
||||
subscription: Subscription
|
||||
eventType: SubscriptionEventType
|
||||
previousMonthlyRevenue: MonthlyRevenue
|
||||
newMonthlyRevenue: MonthlyRevenue
|
||||
createdAt: number
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('Subscription', () => {
|
||||
isFirstSubscriptionForUser: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
})
|
||||
}).getValue()
|
||||
|
||||
expect(subscription.id.toString()).toHaveLength(36)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Entity } from '../Core/Entity'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { SubscriptionProps } from './SubscriptionProps'
|
||||
|
||||
@@ -11,7 +12,7 @@ export class Subscription extends Entity<SubscriptionProps> {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: SubscriptionProps, id?: UniqueEntityId): Subscription {
|
||||
return new Subscription(props, id)
|
||||
static create(props: SubscriptionProps, id?: UniqueEntityId): Result<Subscription> {
|
||||
return Result.ok<Subscription>(new Subscription(props, id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,28 +11,35 @@ import { RevenueModificationRepositoryInterface } from '../../Revenue/RevenueMod
|
||||
import { SubscriptionEventType } from '../../Subscription/SubscriptionEventType'
|
||||
import { SubscriptionPlanName } from '../../Subscription/SubscriptionPlanName'
|
||||
import { SaveRevenueModification } from './SaveRevenueModification'
|
||||
import { User } from '../../User/User'
|
||||
import { Result } from '../../Core/Result'
|
||||
import { Subscription } from '../../Subscription/Subscription'
|
||||
|
||||
describe('SaveRevenueModification', () => {
|
||||
let revenueModificationRepository: RevenueModificationRepositoryInterface
|
||||
let previousMonthlyRevenue: RevenueModification
|
||||
let previousMonthlyRevenueModification: RevenueModification
|
||||
let timer: TimerInterface
|
||||
|
||||
const createUseCase = () => new SaveRevenueModification(revenueModificationRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
previousMonthlyRevenue = {
|
||||
newMonthlyRevenue: MonthlyRevenue.create(2).getValue(),
|
||||
const previousMonthlyRevenue = {
|
||||
value: 2,
|
||||
} as jest.Mocked<MonthlyRevenue>
|
||||
previousMonthlyRevenueModification = {
|
||||
props: {},
|
||||
} as jest.Mocked<RevenueModification>
|
||||
previousMonthlyRevenueModification.props.newMonthlyRevenue = previousMonthlyRevenue
|
||||
|
||||
revenueModificationRepository = {} as jest.Mocked<RevenueModificationRepositoryInterface>
|
||||
revenueModificationRepository.findLastByUserUuid = jest.fn().mockReturnValue(previousMonthlyRevenue)
|
||||
revenueModificationRepository.findLastByUserUuid = jest.fn().mockReturnValue(previousMonthlyRevenueModification)
|
||||
revenueModificationRepository.save = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
|
||||
})
|
||||
|
||||
it('should persist a revenue modification', async () => {
|
||||
it('should persist a revenue modification for subscription purchased event', async () => {
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
@@ -43,8 +50,166 @@ describe('SaveRevenueModification', () => {
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeFalsy()
|
||||
const revenue = revenueOrError.getValue()
|
||||
expect(revenue.newMonthlyRevenue.value).toEqual(12.99)
|
||||
|
||||
expect(revenue.props.newMonthlyRevenue.value).toEqual(12.99)
|
||||
})
|
||||
|
||||
it('should persist a revenue modification for subscription expired event', async () => {
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_EXPIRED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeFalsy()
|
||||
const revenue = revenueOrError.getValue()
|
||||
|
||||
expect(revenue.props.newMonthlyRevenue.value).toEqual(0)
|
||||
})
|
||||
|
||||
it('should persist a revenue modification for subscription cancelled event', async () => {
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_CANCELLED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 2,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeFalsy()
|
||||
const revenue = revenueOrError.getValue()
|
||||
|
||||
expect(revenue.props.newMonthlyRevenue.value).toEqual(2)
|
||||
})
|
||||
|
||||
it('should persist a revenue modification for subscription purchased event if previous revenue modification did not exist', async () => {
|
||||
revenueModificationRepository.findLastByUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeFalsy()
|
||||
const revenue = revenueOrError.getValue()
|
||||
|
||||
expect(revenue.props.newMonthlyRevenue.value).toEqual(12.99)
|
||||
})
|
||||
|
||||
it('should not persist a revenue modification if failed to create user', async () => {
|
||||
const mock = jest.spyOn(User, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
|
||||
it('should not persist a revenue modification if failed to create a subscription', async () => {
|
||||
const mock = jest.spyOn(Subscription, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
|
||||
it('should not persist a revenue modification if failed to create a previous monthly revenue', async () => {
|
||||
const mock = jest.spyOn(MonthlyRevenue, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
|
||||
it('should not persist a revenue modification if failed to create a next monthly revenue', async () => {
|
||||
const mock = jest.spyOn(MonthlyRevenue, 'create')
|
||||
mock.mockReturnValueOnce(Result.ok()).mockReturnValueOnce(Result.fail('Oops'))
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
|
||||
it('should not persist a revenue modification if failed to create it', async () => {
|
||||
const mock = jest.spyOn(RevenueModification, 'create')
|
||||
mock.mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const revenueOrError = await createUseCase().execute({
|
||||
billingFrequency: 1,
|
||||
eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
|
||||
newSubscriber: true,
|
||||
payedAmount: 12.99,
|
||||
planName: SubscriptionPlanName.create('PRO_PLAN').getValue(),
|
||||
subscriptionId: 1234,
|
||||
userEmail: Email.create('test@test.te').getValue(),
|
||||
userUuid: Uuid.create('1-2-3').getValue(),
|
||||
})
|
||||
|
||||
expect(revenueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
mock.mockRestore()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { UniqueEntityId } from '../../Core/UniqueEntityId'
|
||||
import { MonthlyRevenue } from '../../Revenue/MonthlyRevenue'
|
||||
@@ -10,6 +11,7 @@ import { Result } from '../../Core/Result'
|
||||
import { DomainUseCaseInterface } from '../DomainUseCaseInterface'
|
||||
import { SaveRevenueModificationDTO } from './SaveRevenueModificationDTO'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { SubscriptionEventType } from '../../Subscription/SubscriptionEventType'
|
||||
|
||||
@injectable()
|
||||
export class SaveRevenueModification implements DomainUseCaseInterface<RevenueModification> {
|
||||
@@ -20,13 +22,18 @@ export class SaveRevenueModification implements DomainUseCaseInterface<RevenueMo
|
||||
) {}
|
||||
|
||||
async execute(dto: SaveRevenueModificationDTO): Promise<Result<RevenueModification>> {
|
||||
const user = User.create(
|
||||
const userOrError = User.create(
|
||||
{
|
||||
email: dto.userEmail,
|
||||
},
|
||||
new UniqueEntityId(dto.userUuid.value),
|
||||
)
|
||||
const subscription = Subscription.create(
|
||||
if (userOrError.isFailed()) {
|
||||
return Result.fail<RevenueModification>(userOrError.getError())
|
||||
}
|
||||
const user = userOrError.getValue()
|
||||
|
||||
const subscriptionOrError = Subscription.create(
|
||||
{
|
||||
isFirstSubscriptionForUser: dto.newSubscriber,
|
||||
payedAmount: dto.payedAmount,
|
||||
@@ -35,23 +42,77 @@ export class SaveRevenueModification implements DomainUseCaseInterface<RevenueMo
|
||||
},
|
||||
new UniqueEntityId(dto.subscriptionId),
|
||||
)
|
||||
if (subscriptionOrError.isFailed()) {
|
||||
return Result.fail<RevenueModification>(subscriptionOrError.getError())
|
||||
}
|
||||
const subscription = subscriptionOrError.getValue()
|
||||
|
||||
const previousMonthlyRevenueOrError = MonthlyRevenue.create(0)
|
||||
if (previousMonthlyRevenueOrError.isFailed()) {
|
||||
return Result.fail<RevenueModification>(previousMonthlyRevenueOrError.getError())
|
||||
}
|
||||
let previousMonthlyRevenue = previousMonthlyRevenueOrError.getValue()
|
||||
|
||||
let previousMonthlyRevenue = MonthlyRevenue.create(0).getValue()
|
||||
const previousRevenueModification = await this.revenueModificationRepository.findLastByUserUuid(dto.userUuid)
|
||||
if (previousRevenueModification !== null) {
|
||||
previousMonthlyRevenue = previousRevenueModification.newMonthlyRevenue
|
||||
previousMonthlyRevenue = previousRevenueModification.props.newMonthlyRevenue
|
||||
}
|
||||
const newMonthlyRevenueOrError = this.calculateNewMonthlyRevenue(
|
||||
subscription,
|
||||
previousMonthlyRevenue,
|
||||
dto.eventType,
|
||||
)
|
||||
if (newMonthlyRevenueOrError.isFailed()) {
|
||||
return Result.fail<RevenueModification>(newMonthlyRevenueOrError.getError())
|
||||
}
|
||||
const newMonthlyRevenue = newMonthlyRevenueOrError.getValue()
|
||||
|
||||
const revenueModification = RevenueModification.create({
|
||||
const revenueModificationOrError = RevenueModification.create({
|
||||
eventType: dto.eventType,
|
||||
subscription,
|
||||
user,
|
||||
previousMonthlyRevenue,
|
||||
newMonthlyRevenue,
|
||||
createdAt: this.timer.getTimestampInMicroseconds(),
|
||||
})
|
||||
|
||||
if (revenueModificationOrError.isFailed()) {
|
||||
return Result.fail<RevenueModification>(revenueModificationOrError.getError())
|
||||
}
|
||||
const revenueModification = revenueModificationOrError.getValue()
|
||||
|
||||
await this.revenueModificationRepository.save(revenueModification)
|
||||
|
||||
return Result.ok<RevenueModification>(revenueModification)
|
||||
}
|
||||
|
||||
private calculateNewMonthlyRevenue(
|
||||
subscription: Subscription,
|
||||
previousMonthlyRevenue: MonthlyRevenue,
|
||||
eventType: SubscriptionEventType,
|
||||
): Result<MonthlyRevenue> {
|
||||
let revenue = 0
|
||||
switch (eventType.value) {
|
||||
case 'SUBSCRIPTION_PURCHASED':
|
||||
case 'SUBSCRIPTION_RENEWED':
|
||||
case 'SUBSCRIPTION_DATA_MIGRATED':
|
||||
revenue = subscription.props.payedAmount / subscription.props.billingFrequency
|
||||
break
|
||||
case 'SUBSCRIPTION_EXPIRED':
|
||||
case 'SUBSCRIPTION_REFUNDED':
|
||||
revenue = 0
|
||||
break
|
||||
case 'SUBSCRIPTION_CANCELLED':
|
||||
revenue = previousMonthlyRevenue.value
|
||||
break
|
||||
}
|
||||
|
||||
const monthlyRevenueOrError = MonthlyRevenue.create(revenue)
|
||||
|
||||
if (monthlyRevenueOrError.isFailed()) {
|
||||
return Result.fail<MonthlyRevenue>(monthlyRevenueOrError.getError())
|
||||
}
|
||||
|
||||
return Result.ok<MonthlyRevenue>(monthlyRevenueOrError.getValue())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ describe('User', () => {
|
||||
it('should create an entity', () => {
|
||||
const user = User.create({
|
||||
email: Email.create('test@test.te').getValue(),
|
||||
})
|
||||
}).getValue()
|
||||
|
||||
expect(user.id.toString()).toHaveLength(36)
|
||||
})
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Entity } from '../Core/Entity'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { UserProps } from './UserProps'
|
||||
|
||||
@@ -11,7 +12,7 @@ export class User extends Entity<UserProps> {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
public static create(props: UserProps, id?: UniqueEntityId): User {
|
||||
return new User(props, id)
|
||||
public static create(props: UserProps, id?: UniqueEntityId): Result<User> {
|
||||
return Result.ok<User>(new User(props, id))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user