From 2aa57f1f0ddace741b79e9375b87464eaaba6320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20S=C3=B3jko?= Date: Mon, 7 Nov 2022 07:11:10 +0100 Subject: [PATCH] feat(analytics): add subscription renewed event handler --- packages/analytics/src/Bootstrap/Container.ts | 4 ++ packages/analytics/src/Bootstrap/Types.ts | 1 + .../SubscriptionRenewedEventHandler.spec.ts | 43 +++++++++++++++++++ .../SubscriptionRenewedEventHandler.ts | 30 +++++++++++++ 4 files changed, 78 insertions(+) create mode 100644 packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts create mode 100644 packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts diff --git a/packages/analytics/src/Bootstrap/Container.ts b/packages/analytics/src/Bootstrap/Container.ts index 5b8cf53d2..4f1e9cc51 100644 --- a/packages/analytics/src/Bootstrap/Container.ts +++ b/packages/analytics/src/Bootstrap/Container.ts @@ -38,6 +38,7 @@ import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountD import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventHandler' import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler' import { SubscriptionCancelledEventHandler } from '../Domain/Handler/SubscriptionCancelledEventHandler' +import { SubscriptionRenewedEventHandler } from '../Domain/Handler/SubscriptionRenewedEventHandler' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -129,6 +130,9 @@ export class ContainerConfigLoader { container .bind(TYPES.SubscriptionCancelledEventHandler) .to(SubscriptionCancelledEventHandler) + container + .bind(TYPES.SubscriptionRenewedEventHandler) + .to(SubscriptionRenewedEventHandler) // Services container.bind(TYPES.DomainEventFactory).to(DomainEventFactory) diff --git a/packages/analytics/src/Bootstrap/Types.ts b/packages/analytics/src/Bootstrap/Types.ts index 4d29856f3..4be0eb545 100644 --- a/packages/analytics/src/Bootstrap/Types.ts +++ b/packages/analytics/src/Bootstrap/Types.ts @@ -23,6 +23,7 @@ const TYPES = { PaymentFailedEventHandler: Symbol.for('PaymentFailedEventHandler'), PaymentSuccessEventHandler: Symbol.for('PaymentSuccessEventHandler'), SubscriptionCancelledEventHandler: Symbol.for('SubscriptionCancelledEventHandler'), + SubscriptionRenewedEventHandler: Symbol.for('SubscriptionRenewedEventHandler'), // Services DomainEventPublisher: Symbol.for('DomainEventPublisher'), DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'), diff --git a/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts new file mode 100644 index 000000000..c5334da74 --- /dev/null +++ b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts @@ -0,0 +1,43 @@ +import 'reflect-metadata' + +import { SubscriptionName } from '@standardnotes/common' +import { SubscriptionRenewedEvent } from '@standardnotes/domain-events' + +import { SubscriptionRenewedEventHandler } from './SubscriptionRenewedEventHandler' +import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' +import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface' + +describe('SubscriptionRenewedEventHandler', () => { + let event: SubscriptionRenewedEvent + let getUserAnalyticsId: GetUserAnalyticsId + let analyticsStore: AnalyticsStoreInterface + + const createHandler = () => new SubscriptionRenewedEventHandler(getUserAnalyticsId, analyticsStore) + + beforeEach(() => { + event = {} as jest.Mocked + event.createdAt = new Date(1) + event.payload = { + subscriptionId: 1, + userEmail: 'test@test.com', + subscriptionName: SubscriptionName.ProPlan, + subscriptionExpiresAt: 2, + timestamp: 1, + offline: false, + } + + getUserAnalyticsId = {} as jest.Mocked + getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) + + analyticsStore = {} as jest.Mocked + analyticsStore.markActivity = jest.fn() + analyticsStore.unmarkActivity = jest.fn() + }) + + it('should track subscription renewed statistics', async () => { + await createHandler().handle(event) + + expect(analyticsStore.markActivity).toHaveBeenCalled() + expect(analyticsStore.unmarkActivity).toHaveBeenCalled() + }) +}) diff --git a/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts new file mode 100644 index 000000000..c734c4aa8 --- /dev/null +++ b/packages/analytics/src/Domain/Handler/SubscriptionRenewedEventHandler.ts @@ -0,0 +1,30 @@ +import { DomainEventHandlerInterface, SubscriptionRenewedEvent } from '@standardnotes/domain-events' +import { inject, injectable } from 'inversify' + +import TYPES from '../../Bootstrap/Types' +import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' +import { AnalyticsActivity } from '../Analytics/AnalyticsActivity' +import { AnalyticsStoreInterface } from '../Analytics/AnalyticsStoreInterface' +import { Period } from '../Time/Period' + +@injectable() +export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterface { + constructor( + @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, + @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, + ) {} + + async handle(event: SubscriptionRenewedEvent): Promise { + const { analyticsId } = await this.getUserAnalyticsId.execute({ userEmail: event.payload.userEmail }) + await this.analyticsStore.markActivity([AnalyticsActivity.SubscriptionRenewed], analyticsId, [ + Period.Today, + Period.ThisWeek, + Period.ThisMonth, + ]) + await this.analyticsStore.unmarkActivity( + [AnalyticsActivity.ExistingCustomersChurn, AnalyticsActivity.NewCustomersChurn], + analyticsId, + [Period.Today, Period.ThisWeek, Period.ThisMonth], + ) + } +}