Quellcode durchsuchen

fix(analytics): add missing created at column

Karol Sójko vor 2 Jahren
Ursprung
Commit
89502bed63

+ 13 - 0
packages/analytics/migrations/1667994036734-add_missing_created_at.ts

@@ -0,0 +1,13 @@
+import { MigrationInterface, QueryRunner } from 'typeorm'
+
+export class addMissingCreatedAt1667994036734 implements MigrationInterface {
+  name = 'addMissingCreatedAt1667994036734'
+
+  public async up(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `revenue_modifications` ADD `created_at` bigint NOT NULL')
+  }
+
+  public async down(queryRunner: QueryRunner): Promise<void> {
+    await queryRunner.query('ALTER TABLE `revenue_modifications` DROP COLUMN `created_at`')
+  }
+}

+ 2 - 0
packages/analytics/src/Domain/Map/RevenueModificationMap.ts

@@ -37,6 +37,7 @@ export class RevenueModificationMap implements MapInterface<RevenueModification,
         subscription,
         eventType: SubscriptionEventType.create(persistence.eventType).getValue(),
         previousMonthlyRevenue: previousMonthlyRevenueOrError.getValue(),
+        createdAt: persistence.createdAt,
       },
       new UniqueEntityId(persistence.uuid),
     )
@@ -55,6 +56,7 @@ export class RevenueModificationMap implements MapInterface<RevenueModification,
     persistence.subscriptionPlan = subscription.props.planName.value
     persistence.userEmail = user.props.email.value
     persistence.userUuid = user.id.toString()
+    persistence.createdAt = domain.props.createdAt
 
     return persistence
   }

+ 3 - 1
packages/analytics/src/Domain/Revenue/RevenueModification.spec.ts

@@ -24,6 +24,7 @@ describe('RevenueModification', () => {
 
   it('should create an aggregate for purchased subscription', () => {
     const revenueModification = RevenueModification.create({
+      createdAt: 2,
       eventType: SubscriptionEventType.create('SUBSCRIPTION_PURCHASED').getValue(),
       previousMonthlyRevenue: MonthlyRevenue.create(123).getValue(),
       subscription,
@@ -36,7 +37,7 @@ describe('RevenueModification', () => {
 
   it('should create an aggregate for subscription expired', () => {
     const revenueModification = RevenueModification.create({
-      createdAt: new Date(1),
+      createdAt: 1,
       eventType: SubscriptionEventType.create('SUBSCRIPTION_EXPIRED').getValue(),
       previousMonthlyRevenue: MonthlyRevenue.create(123).getValue(),
       subscription,
@@ -49,6 +50,7 @@ describe('RevenueModification', () => {
 
   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,

+ 2 - 9
packages/analytics/src/Domain/Revenue/RevenueModification.ts

@@ -9,15 +9,7 @@ export class RevenueModification extends Aggregate<RevenueModificationProps> {
   }
 
   static create(props: RevenueModificationProps, id?: UniqueEntityId): RevenueModification {
-    const revenueModification = new RevenueModification(
-      {
-        ...props,
-        createdAt: props.createdAt ? props.createdAt : new Date(),
-      },
-      id,
-    )
-
-    return revenueModification
+    return new RevenueModification(props, id)
   }
 
   get newMonthlyRevenue(): MonthlyRevenue {
@@ -27,6 +19,7 @@ export class RevenueModification extends Aggregate<RevenueModificationProps> {
     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':

+ 1 - 1
packages/analytics/src/Domain/Revenue/RevenueModificationProps.ts

@@ -8,5 +8,5 @@ export interface RevenueModificationProps {
   subscription: Subscription
   eventType: SubscriptionEventType
   previousMonthlyRevenue: MonthlyRevenue
-  createdAt?: Date
+  createdAt: number
 }

+ 1 - 0
packages/analytics/src/Domain/Subscription/SubscriptionEventType.ts

@@ -19,6 +19,7 @@ export class SubscriptionEventType extends ValueObject<SubscriptionEventTypeProp
         'SUBSCRIPTION_EXPIRED',
         'SUBSCRIPTION_REFUNDED',
         'SUBSCRIPTION_CANCELLED',
+        'SUBSCRIPTION_DATA_MIGRATED',
       ].includes(subscriptionEventType)
     ) {
       return Result.fail<SubscriptionEventType>(`Invalid subscription event type ${subscriptionEventType}`)

+ 7 - 1
packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.spec.ts

@@ -1,5 +1,7 @@
 import 'reflect-metadata'
 
+import { TimerInterface } from '@standardnotes/time'
+
 import { Email } from '../../Common/Email'
 import { Uuid } from '../../Common/Uuid'
 import { MonthlyRevenue } from '../../Revenue/MonthlyRevenue'
@@ -13,8 +15,9 @@ import { SaveRevenueModification } from './SaveRevenueModification'
 describe('SaveRevenueModification', () => {
   let revenueModificationRepository: RevenueModificationRepositoryInterface
   let previousMonthlyRevenue: RevenueModification
+  let timer: TimerInterface
 
-  const createUseCase = () => new SaveRevenueModification(revenueModificationRepository)
+  const createUseCase = () => new SaveRevenueModification(revenueModificationRepository, timer)
 
   beforeEach(() => {
     previousMonthlyRevenue = {
@@ -24,6 +27,9 @@ describe('SaveRevenueModification', () => {
     revenueModificationRepository = {} as jest.Mocked<RevenueModificationRepositoryInterface>
     revenueModificationRepository.findLastByUserUuid = jest.fn().mockReturnValue(previousMonthlyRevenue)
     revenueModificationRepository.save = jest.fn()
+
+    timer = {} as jest.Mocked<TimerInterface>
+    timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
   })
 
   it('should persist a revenue modification', async () => {

+ 3 - 0
packages/analytics/src/Domain/UseCase/SaveRevenueModification/SaveRevenueModification.ts

@@ -9,12 +9,14 @@ import { User } from '../../User/User'
 import { Result } from '../../Core/Result'
 import { DomainUseCaseInterface } from '../DomainUseCaseInterface'
 import { SaveRevenueModificationDTO } from './SaveRevenueModificationDTO'
+import { TimerInterface } from '@standardnotes/time'
 
 @injectable()
 export class SaveRevenueModification implements DomainUseCaseInterface<RevenueModification> {
   constructor(
     @inject(TYPES.RevenueModificationRepository)
     private revenueModificationRepository: RevenueModificationRepositoryInterface,
+    @inject(TYPES.Timer) private timer: TimerInterface,
   ) {}
 
   async execute(dto: SaveRevenueModificationDTO): Promise<Result<RevenueModification>> {
@@ -45,6 +47,7 @@ export class SaveRevenueModification implements DomainUseCaseInterface<RevenueMo
       subscription,
       user,
       previousMonthlyRevenue,
+      createdAt: this.timer.getTimestampInMicroseconds(),
     })
 
     await this.revenueModificationRepository.save(revenueModification)

+ 6 - 0
packages/analytics/src/Infra/TypeORM/TypeORMRevenueModification.ts

@@ -56,4 +56,10 @@ export class TypeORMRevenueModification {
     name: 'new_mrr',
   })
   declare newMonthlyRevenue: number
+
+  @Column({
+    name: 'created_at',
+    type: 'bigint',
+  })
+  declare createdAt: number
 }