feat(auth): add measuring registration to subscription time statistics
This commit is contained in:
parent
8157f324a0
commit
b61825235e
6 changed files with 68 additions and 2 deletions
|
@ -2,5 +2,6 @@ export enum StatisticsMeasure {
|
||||||
Income = 'income',
|
Income = 'income',
|
||||||
SubscriptionLength = 'subscription-length',
|
SubscriptionLength = 'subscription-length',
|
||||||
RegistrationLength = 'registration-length',
|
RegistrationLength = 'registration-length',
|
||||||
|
RegistrationToSubscriptionTime = 'registration-to-subscription-time',
|
||||||
Refunds = 'refunds',
|
Refunds = 'refunds',
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,10 @@ import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/Offl
|
||||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||||
import { AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
import { AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
|
||||||
import { AnalyticsEntity } from '../Analytics/AnalyticsEntity'
|
import { AnalyticsEntity } from '../Analytics/AnalyticsEntity'
|
||||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||||
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
|
|
||||||
describe('SubscriptionPurchasedEventHandler', () => {
|
describe('SubscriptionPurchasedEventHandler', () => {
|
||||||
let userRepository: UserRepositoryInterface
|
let userRepository: UserRepositoryInterface
|
||||||
|
@ -35,6 +36,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||||
let getUserAnalyticsId: GetUserAnalyticsId
|
let getUserAnalyticsId: GetUserAnalyticsId
|
||||||
let analyticsStore: AnalyticsStoreInterface
|
let analyticsStore: AnalyticsStoreInterface
|
||||||
let timestamp: number
|
let timestamp: number
|
||||||
|
let statisticsStore: StatisticsStoreInterface
|
||||||
|
let timer: TimerInterface
|
||||||
|
|
||||||
const createHandler = () =>
|
const createHandler = () =>
|
||||||
new SubscriptionPurchasedEventHandler(
|
new SubscriptionPurchasedEventHandler(
|
||||||
|
@ -45,6 +48,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||||
subscriptionSettingService,
|
subscriptionSettingService,
|
||||||
getUserAnalyticsId,
|
getUserAnalyticsId,
|
||||||
analyticsStore,
|
analyticsStore,
|
||||||
|
statisticsStore,
|
||||||
|
timer,
|
||||||
logger,
|
logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,7 +71,14 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||||
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
|
userRepository.findOneByEmail = jest.fn().mockReturnValue(user)
|
||||||
userRepository.save = jest.fn().mockReturnValue(user)
|
userRepository.save = jest.fn().mockReturnValue(user)
|
||||||
|
|
||||||
|
statisticsStore = {} as jest.Mocked<StatisticsStoreInterface>
|
||||||
|
statisticsStore.incrementMeasure = jest.fn()
|
||||||
|
|
||||||
|
timer = {} as jest.Mocked<TimerInterface>
|
||||||
|
timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1)
|
||||||
|
|
||||||
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
|
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
|
||||||
|
userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(0)
|
||||||
userSubscriptionRepository.save = jest.fn().mockReturnValue(subscription)
|
userSubscriptionRepository.save = jest.fn().mockReturnValue(subscription)
|
||||||
|
|
||||||
offlineUserSubscription = {} as jest.Mocked<OfflineUserSubscription>
|
offlineUserSubscription = {} as jest.Mocked<OfflineUserSubscription>
|
||||||
|
@ -146,6 +158,15 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
||||||
updatedAt: expect.any(Number),
|
updatedAt: expect.any(Number),
|
||||||
cancelled: false,
|
cancelled: false,
|
||||||
})
|
})
|
||||||
|
expect(statisticsStore.incrementMeasure).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not measure registration to subscription time if this is not user's first subscription", async () => {
|
||||||
|
userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(1)
|
||||||
|
|
||||||
|
await createHandler().handle(event)
|
||||||
|
|
||||||
|
expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update analytics on limited discount offer purchasing', async () => {
|
it('should update analytics on limited discount offer purchasing', async () => {
|
||||||
|
|
|
@ -13,8 +13,15 @@ import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription
|
||||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||||
import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
import {
|
||||||
|
AnalyticsActivity,
|
||||||
|
AnalyticsStoreInterface,
|
||||||
|
Period,
|
||||||
|
StatisticsMeasure,
|
||||||
|
StatisticsStoreInterface,
|
||||||
|
} from '@standardnotes/analytics'
|
||||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||||
|
import { TimerInterface } from '@standardnotes/time'
|
||||||
|
|
||||||
@injectable()
|
@injectable()
|
||||||
export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInterface {
|
export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInterface {
|
||||||
|
@ -27,6 +34,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||||
@inject(TYPES.SubscriptionSettingService) private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
@inject(TYPES.SubscriptionSettingService) private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
||||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||||
|
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||||
|
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||||
@inject(TYPES.Logger) private logger: Logger,
|
@inject(TYPES.Logger) private logger: Logger,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@ -52,6 +61,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previousSubscriptionCount = await this.userSubscriptionRepository.countByUserUuid(user.uuid)
|
||||||
|
|
||||||
const userSubscription = await this.createSubscription(
|
const userSubscription = await this.createSubscription(
|
||||||
event.payload.subscriptionId,
|
event.payload.subscriptionId,
|
||||||
event.payload.subscriptionName,
|
event.payload.subscriptionName,
|
||||||
|
@ -80,6 +91,14 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||||
Period.Today,
|
Period.Today,
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previousSubscriptionCount === 0) {
|
||||||
|
await this.statisticsStore.incrementMeasure(
|
||||||
|
StatisticsMeasure.RegistrationToSubscriptionTime,
|
||||||
|
event.payload.timestamp - this.timer.convertDateToMicroseconds(user.createdAt),
|
||||||
|
[Period.Today, Period.ThisWeek, Period.ThisMonth],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async addUserRole(user: User, subscriptionName: SubscriptionName): Promise<void> {
|
private async addUserRole(user: User, subscriptionName: SubscriptionName): Promise<void> {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { UserSubscriptionType } from './UserSubscriptionType'
|
||||||
|
|
||||||
export interface UserSubscriptionRepositoryInterface {
|
export interface UserSubscriptionRepositoryInterface {
|
||||||
findOneByUuid(uuid: Uuid): Promise<UserSubscription | null>
|
findOneByUuid(uuid: Uuid): Promise<UserSubscription | null>
|
||||||
|
countByUserUuid(userUuid: Uuid): Promise<number>
|
||||||
findOneByUserUuid(userUuid: Uuid): Promise<UserSubscription | null>
|
findOneByUserUuid(userUuid: Uuid): Promise<UserSubscription | null>
|
||||||
findOneByUserUuidAndSubscriptionId(userUuid: Uuid, subscriptionId: number): Promise<UserSubscription | null>
|
findOneByUserUuidAndSubscriptionId(userUuid: Uuid, subscriptionId: number): Promise<UserSubscription | null>
|
||||||
findBySubscriptionIdAndType(subscriptionId: number, type: UserSubscriptionType): Promise<UserSubscription[]>
|
findBySubscriptionIdAndType(subscriptionId: number, type: UserSubscriptionType): Promise<UserSubscription[]>
|
||||||
|
|
|
@ -75,6 +75,21 @@ describe('MySQLUserSubscriptionRepository', () => {
|
||||||
expect(result).toEqual(subscription)
|
expect(result).toEqual(subscription)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should count by user uuid', async () => {
|
||||||
|
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => selectQueryBuilder)
|
||||||
|
|
||||||
|
selectQueryBuilder.where = jest.fn().mockReturnThis()
|
||||||
|
selectQueryBuilder.getCount = jest.fn().mockReturnValue(2)
|
||||||
|
|
||||||
|
const result = await createRepository().countByUserUuid('123')
|
||||||
|
|
||||||
|
expect(selectQueryBuilder.where).toHaveBeenCalledWith('user_uuid = :user_uuid', {
|
||||||
|
user_uuid: '123',
|
||||||
|
})
|
||||||
|
expect(selectQueryBuilder.getCount).toHaveBeenCalled()
|
||||||
|
expect(result).toEqual(2)
|
||||||
|
})
|
||||||
|
|
||||||
it('should find one, longest lasting subscription by user uuid if there are no ucanceled ones', async () => {
|
it('should find one, longest lasting subscription by user uuid if there are no ucanceled ones', async () => {
|
||||||
subscription.cancelled = true
|
subscription.cancelled = true
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,15 @@ export class MySQLUserSubscriptionRepository implements UserSubscriptionReposito
|
||||||
private ormRepository: Repository<UserSubscription>,
|
private ormRepository: Repository<UserSubscription>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||||
|
return await this.ormRepository
|
||||||
|
.createQueryBuilder()
|
||||||
|
.where('user_uuid = :user_uuid', {
|
||||||
|
user_uuid: userUuid,
|
||||||
|
})
|
||||||
|
.getCount()
|
||||||
|
}
|
||||||
|
|
||||||
async save(subscription: UserSubscription): Promise<UserSubscription> {
|
async save(subscription: UserSubscription): Promise<UserSubscription> {
|
||||||
return this.ormRepository.save(subscription)
|
return this.ormRepository.save(subscription)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue