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',
|
||||
SubscriptionLength = 'subscription-length',
|
||||
RegistrationLength = 'registration-length',
|
||||
RegistrationToSubscriptionTime = 'registration-to-subscription-time',
|
||||
Refunds = 'refunds',
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/Offl
|
|||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||
import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface'
|
||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||
import { AnalyticsStoreInterface, Period } from '@standardnotes/analytics'
|
||||
import { AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics'
|
||||
import { AnalyticsEntity } from '../Analytics/AnalyticsEntity'
|
||||
import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
describe('SubscriptionPurchasedEventHandler', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
|
@ -35,6 +36,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
|||
let getUserAnalyticsId: GetUserAnalyticsId
|
||||
let analyticsStore: AnalyticsStoreInterface
|
||||
let timestamp: number
|
||||
let statisticsStore: StatisticsStoreInterface
|
||||
let timer: TimerInterface
|
||||
|
||||
const createHandler = () =>
|
||||
new SubscriptionPurchasedEventHandler(
|
||||
|
@ -45,6 +48,8 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
|||
subscriptionSettingService,
|
||||
getUserAnalyticsId,
|
||||
analyticsStore,
|
||||
statisticsStore,
|
||||
timer,
|
||||
logger,
|
||||
)
|
||||
|
||||
|
@ -66,7 +71,14 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
|||
userRepository.findOneByEmail = 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.countByUserUuid = jest.fn().mockReturnValue(0)
|
||||
userSubscriptionRepository.save = jest.fn().mockReturnValue(subscription)
|
||||
|
||||
offlineUserSubscription = {} as jest.Mocked<OfflineUserSubscription>
|
||||
|
@ -146,6 +158,15 @@ describe('SubscriptionPurchasedEventHandler', () => {
|
|||
updatedAt: expect.any(Number),
|
||||
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 () => {
|
||||
|
|
|
@ -13,8 +13,15 @@ import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription
|
|||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||
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 { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInterface {
|
||||
|
@ -27,6 +34,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
|||
@inject(TYPES.SubscriptionSettingService) private subscriptionSettingService: SubscriptionSettingServiceInterface,
|
||||
@inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId,
|
||||
@inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface,
|
||||
@inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface,
|
||||
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Logger) private logger: Logger,
|
||||
) {}
|
||||
|
||||
|
@ -52,6 +61,8 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
|||
return
|
||||
}
|
||||
|
||||
const previousSubscriptionCount = await this.userSubscriptionRepository.countByUserUuid(user.uuid)
|
||||
|
||||
const userSubscription = await this.createSubscription(
|
||||
event.payload.subscriptionId,
|
||||
event.payload.subscriptionName,
|
||||
|
@ -80,6 +91,14 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
|||
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> {
|
||||
|
|
|
@ -4,6 +4,7 @@ import { UserSubscriptionType } from './UserSubscriptionType'
|
|||
|
||||
export interface UserSubscriptionRepositoryInterface {
|
||||
findOneByUuid(uuid: Uuid): Promise<UserSubscription | null>
|
||||
countByUserUuid(userUuid: Uuid): Promise<number>
|
||||
findOneByUserUuid(userUuid: Uuid): Promise<UserSubscription | null>
|
||||
findOneByUserUuidAndSubscriptionId(userUuid: Uuid, subscriptionId: number): Promise<UserSubscription | null>
|
||||
findBySubscriptionIdAndType(subscriptionId: number, type: UserSubscriptionType): Promise<UserSubscription[]>
|
||||
|
|
|
@ -75,6 +75,21 @@ describe('MySQLUserSubscriptionRepository', () => {
|
|||
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 () => {
|
||||
subscription.cancelled = true
|
||||
|
||||
|
|
|
@ -14,6 +14,15 @@ export class MySQLUserSubscriptionRepository implements UserSubscriptionReposito
|
|||
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> {
|
||||
return this.ormRepository.save(subscription)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue