diff --git a/.pnp.cjs b/.pnp.cjs index 7e7575ba8..ba3106996 100755 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -2651,7 +2651,6 @@ const RAW_RUNTIME_STATE = ["@standardnotes/auth-server", "workspace:packages/auth"],\ ["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.0"],\ ["@sentry/node", "npm:7.5.0"],\ - ["@standardnotes/analytics", "workspace:packages/analytics"],\ ["@standardnotes/api", "npm:1.19.0"],\ ["@standardnotes/common", "workspace:packages/common"],\ ["@standardnotes/domain-events", "workspace:packages/domain-events"],\ diff --git a/packages/analytics/bin/report.ts b/packages/analytics/bin/report.ts index 21cea5323..d2dd94917 100644 --- a/packages/analytics/bin/report.ts +++ b/packages/analytics/bin/report.ts @@ -34,8 +34,6 @@ const requestReport = async ( }> = [] const thirtyDaysAnalyticsNames = [ - AnalyticsActivity.GeneralActivity, - AnalyticsActivity.EditingItems, AnalyticsActivity.SubscriptionPurchased, AnalyticsActivity.Register, AnalyticsActivity.SubscriptionRenewed, @@ -80,9 +78,6 @@ const requestReport = async ( }> = [] const yesterdayActivityNames = [ AnalyticsActivity.LimitedDiscountOfferPurchased, - AnalyticsActivity.GeneralActivity, - AnalyticsActivity.GeneralActivityFreeUsers, - AnalyticsActivity.GeneralActivityPaidUsers, AnalyticsActivity.PaymentFailed, AnalyticsActivity.PaymentSuccess, AnalyticsActivity.NewCustomersChurn, @@ -116,9 +111,6 @@ const requestReport = async ( StatisticsMeasure.SubscriptionLength, StatisticsMeasure.RegistrationToSubscriptionTime, StatisticsMeasure.RemainingSubscriptionTimePercentage, - StatisticsMeasure.NotesCountFreeUsers, - StatisticsMeasure.NotesCountPaidUsers, - StatisticsMeasure.FilesCount, StatisticsMeasure.NewCustomers, StatisticsMeasure.TotalCustomers, ] @@ -141,29 +133,6 @@ const requestReport = async ( } } - const periodKeys = periodKeyGenerator.getDiscretePeriodKeys(Period.Last7Days) - const retentionOverDays: Array<{ - firstPeriodKey: string - secondPeriodKey: string - value: number - }> = [] - for (let i = 0; i < periodKeys.length; i++) { - for (let j = 0; j < periodKeys.length - i; j++) { - const dailyRetention = await analyticsStore.calculateActivitiesRetention({ - firstActivity: AnalyticsActivity.Register, - firstActivityPeriodKey: periodKeys[i], - secondActivity: AnalyticsActivity.GeneralActivity, - secondActivityPeriodKey: periodKeys[i + j], - }) - - retentionOverDays.push({ - firstPeriodKey: periodKeys[i], - secondPeriodKey: periodKeys[i + j], - value: dailyRetention, - }) - } - } - const monthlyPeriodKeys = periodKeyGenerator.getDiscretePeriodKeys(Period.ThisYear) const churnRates: Array<{ rate: number @@ -207,16 +176,7 @@ const requestReport = async ( activityStatistics: yesterdayActivityStatistics, activityStatisticsOverTime: analyticsOverTime, statisticMeasures, - retentionStatistics: [ - { - firstActivity: AnalyticsActivity.Register, - secondActivity: AnalyticsActivity.GeneralActivity, - retention: { - periodKeys, - values: retentionOverDays, - }, - }, - ], + retentionStatistics: [], churn: { periodKeys: monthlyPeriodKeys, values: churnRates, diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 097c3ef18..b125b667c 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -1,17 +1,14 @@ { "name": "@standardnotes/analytics", - "version": "1.52.0", + "version": "2.0.0", "engines": { "node": ">=14.0.0 <17.0.0" }, + "private": true, "description": "Analytics tools for Standard Notes projects", "main": "dist/src/index.js", "author": "Standard Notes", "types": "dist/src/index.d.ts", - "files": [ - "dist/src/**/*.js", - "dist/src/**/*.d.ts" - ], "publishConfig": { "access": "public" }, diff --git a/packages/analytics/src/Bootstrap/Container.ts b/packages/analytics/src/Bootstrap/Container.ts index d31d7bf40..23d5f9c76 100644 --- a/packages/analytics/src/Bootstrap/Container.ts +++ b/packages/analytics/src/Bootstrap/Container.ts @@ -43,6 +43,7 @@ import { SubscriptionRefundedEventHandler } from '../Domain/Handler/Subscription import { SubscriptionPurchasedEventHandler } from '../Domain/Handler/SubscriptionPurchasedEventHandler' import { SubscriptionExpiredEventHandler } from '../Domain/Handler/SubscriptionExpiredEventHandler' import { SubscriptionReactivatedEventHandler } from '../Domain/Handler/SubscriptionReactivatedEventHandler' +import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEventHandler' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -149,6 +150,7 @@ export class ContainerConfigLoader { container .bind(TYPES.SubscriptionReactivatedEventHandler) .to(SubscriptionReactivatedEventHandler) + container.bind(TYPES.RefundProcessedEventHandler).to(RefundProcessedEventHandler) // Services container.bind(TYPES.DomainEventFactory).to(DomainEventFactory) @@ -175,6 +177,16 @@ export class ContainerConfigLoader { const eventHandlers: Map = new Map([ ['USER_REGISTERED', container.get(TYPES.UserRegisteredEventHandler)], + ['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)], + ['PAYMENT_FAILED', container.get(TYPES.PaymentFailedEventHandler)], + ['PAYMENT_SUCCESS', container.get(TYPES.PaymentSuccessEventHandler)], + ['SUBSCRIPTION_CANCELLED', container.get(TYPES.SubscriptionCancelledEventHandler)], + ['SUBSCRIPTION_RENEWED', container.get(TYPES.SubscriptionRenewedEventHandler)], + ['SUBSCRIPTION_REFUNDED', container.get(TYPES.SubscriptionRefundedEventHandler)], + ['SUBSCRIPTION_PURCHASED', container.get(TYPES.SubscriptionPurchasedEventHandler)], + ['SUBSCRIPTION_EXPIRED', container.get(TYPES.SubscriptionExpiredEventHandler)], + ['SUBSCRIPTION_REACTIVATED', container.get(TYPES.SubscriptionReactivatedEventHandler)], + ['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)], ]) if (env.get('SQS_QUEUE_URL', true)) { diff --git a/packages/analytics/src/Bootstrap/Types.ts b/packages/analytics/src/Bootstrap/Types.ts index b9195563f..ec45c9e00 100644 --- a/packages/analytics/src/Bootstrap/Types.ts +++ b/packages/analytics/src/Bootstrap/Types.ts @@ -28,6 +28,7 @@ const TYPES = { SubscriptionPurchasedEventHandler: Symbol.for('SubscriptionPurchasedEventHandler'), SubscriptionExpiredEventHandler: Symbol.for('SubscriptionExpiredEventHandler'), SubscriptionReactivatedEventHandler: Symbol.for('SubscriptionReactivatedEventHandler'), + RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'), // Services DomainEventPublisher: Symbol.for('DomainEventPublisher'), DomainEventSubscriberFactory: Symbol.for('DomainEventSubscriberFactory'), diff --git a/packages/analytics/src/Domain/Analytics/AnalyticsActivity.ts b/packages/analytics/src/Domain/Analytics/AnalyticsActivity.ts index ef1afcd38..467fc159f 100644 --- a/packages/analytics/src/Domain/Analytics/AnalyticsActivity.ts +++ b/packages/analytics/src/Domain/Analytics/AnalyticsActivity.ts @@ -1,10 +1,4 @@ export enum AnalyticsActivity { - GeneralActivity = 'general-activity', - GeneralActivityFreeUsers = 'general-activity-free-users', - GeneralActivityPaidUsers = 'general-activity-paid-users', - EditingItems = 'editing-items', - CheckingIntegrity = 'checking-integrity', - Login = 'login', Register = 'register', DeleteAccount = 'DeleteAccount', SubscriptionPurchased = 'subscription-purchased', @@ -13,8 +7,6 @@ export enum AnalyticsActivity { SubscriptionCancelled = 'subscription-cancelled', SubscriptionExpired = 'subscription-expired', SubscriptionReactivated = 'subscription-reactivated', - EmailUnbackedUpData = 'email-unbacked-up-data', - EmailBackup = 'email-backup', LimitedDiscountOfferPurchased = 'limited-discount-offer-purchased', PaymentFailed = 'payment-failed', PaymentSuccess = 'payment-success', diff --git a/packages/analytics/src/Domain/Event/DomainEventFactory.spec.ts b/packages/analytics/src/Domain/Event/DomainEventFactory.spec.ts index 732fc1d30..4ecead003 100644 --- a/packages/analytics/src/Domain/Event/DomainEventFactory.spec.ts +++ b/packages/analytics/src/Domain/Event/DomainEventFactory.spec.ts @@ -64,22 +64,7 @@ describe('DomainEventFactory', () => { }, ], outOfSyncIncidents: 324, - retentionStatistics: [ - { - firstActivity: AnalyticsActivity.Register, - secondActivity: AnalyticsActivity.Login, - retention: { - periodKeys: ['2022-10-9'], - values: [ - { - firstPeriodKey: AnalyticsActivity.Register, - secondPeriodKey: AnalyticsActivity.Login, - value: 12, - }, - ], - }, - }, - ], + retentionStatistics: [], churn: { periodKeys: ['2022-10-9'], values: [ @@ -136,22 +121,7 @@ describe('DomainEventFactory', () => { ], }, outOfSyncIncidents: 324, - retentionStatistics: [ - { - firstActivity: 'register', - retention: { - periodKeys: ['2022-10-9'], - values: [ - { - firstPeriodKey: 'register', - secondPeriodKey: 'login', - value: 12, - }, - ], - }, - secondActivity: 'login', - }, - ], + retentionStatistics: [], snjsStatistics: [ { count: 2, diff --git a/packages/auth/src/Domain/Handler/RefundProcessedEventHandler.spec.ts b/packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.spec.ts similarity index 83% rename from packages/auth/src/Domain/Handler/RefundProcessedEventHandler.spec.ts rename to packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.spec.ts index 116543193..459f767a7 100644 --- a/packages/auth/src/Domain/Handler/RefundProcessedEventHandler.spec.ts +++ b/packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.spec.ts @@ -1,9 +1,11 @@ import 'reflect-metadata' import { RefundProcessedEvent } from '@standardnotes/domain-events' -import { Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics' import { RefundProcessedEventHandler } from './RefundProcessedEventHandler' +import { StatisticsMeasure } from '../Statistics/StatisticsMeasure' +import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface' +import { Period } from '../Time/Period' describe('RefundProcessedEventHandler', () => { let event: RefundProcessedEvent diff --git a/packages/auth/src/Domain/Handler/RefundProcessedEventHandler.ts b/packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.ts similarity index 76% rename from packages/auth/src/Domain/Handler/RefundProcessedEventHandler.ts rename to packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.ts index 877a19591..68a57946c 100644 --- a/packages/auth/src/Domain/Handler/RefundProcessedEventHandler.ts +++ b/packages/analytics/src/Domain/Handler/RefundProcessedEventHandler.ts @@ -1,8 +1,10 @@ -import { Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics' import { DomainEventHandlerInterface, RefundProcessedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import TYPES from '../../Bootstrap/Types' +import { StatisticsMeasure } from '../Statistics/StatisticsMeasure' +import { StatisticsStoreInterface } from '../Statistics/StatisticsStoreInterface' +import { Period } from '../Time/Period' @injectable() export class RefundProcessedEventHandler implements DomainEventHandlerInterface { diff --git a/packages/analytics/src/Domain/Statistics/StatisticsMeasure.ts b/packages/analytics/src/Domain/Statistics/StatisticsMeasure.ts index 4f71f2d7d..1e707e652 100644 --- a/packages/analytics/src/Domain/Statistics/StatisticsMeasure.ts +++ b/packages/analytics/src/Domain/Statistics/StatisticsMeasure.ts @@ -13,9 +13,6 @@ export enum StatisticsMeasure { RegistrationToSubscriptionTime = 'registration-to-subscription-time', RemainingSubscriptionTimePercentage = 'remaining-subscription-time-percentage', Refunds = 'refunds', - NotesCountFreeUsers = 'notes-count-free-users', - NotesCountPaidUsers = 'notes-count-paid-users', - FilesCount = 'files-count', NewCustomers = 'new-customers', TotalCustomers = 'total-customers', } diff --git a/packages/analytics/src/Domain/index.ts b/packages/analytics/src/Domain/index.ts deleted file mode 100644 index c965b5601..000000000 --- a/packages/analytics/src/Domain/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from './Analytics/AnalyticsActivity' -export * from './Analytics/AnalyticsStoreInterface' -export * from './Statistics/StatisticsMeasure' -export * from './Statistics/StatisticsStoreInterface' -export * from './Time/Period' -export * from './Time/PeriodKeyGenerator' -export * from './Time/PeriodKeyGeneratorInterface' diff --git a/packages/analytics/src/Infra/Redis/RedisAnalyticsStore.spec.ts b/packages/analytics/src/Infra/Redis/RedisAnalyticsStore.spec.ts index 1fdb85dd6..639394962 100644 --- a/packages/analytics/src/Infra/Redis/RedisAnalyticsStore.spec.ts +++ b/packages/analytics/src/Infra/Redis/RedisAnalyticsStore.spec.ts @@ -1,6 +1,6 @@ import * as IORedis from 'ioredis' -import { Period } from '../../Domain' import { AnalyticsActivity } from '../../Domain/Analytics/AnalyticsActivity' +import { Period } from '../../Domain/Time/Period' import { PeriodKeyGeneratorInterface } from '../../Domain/Time/PeriodKeyGeneratorInterface' import { RedisAnalyticsStore } from './RedisAnalyticsStore' @@ -35,24 +35,24 @@ describe('RedisAnalyticsStore', () => { periodKeyGenerator.getDiscretePeriodKeys = jest.fn().mockReturnValue(['2022-4-24', '2022-4-25', '2022-4-26']) - await createStore().calculateActivityTotalCountOverTime(AnalyticsActivity.EditingItems, Period.Last30Days) + await createStore().calculateActivityTotalCountOverTime(AnalyticsActivity.Register, Period.Last30Days) expect(redisClient.bitop).toHaveBeenCalledTimes(1) expect(redisClient.bitop).toHaveBeenNthCalledWith( 1, 'OR', - 'bitmap:action:editing-items:timespan:2022-4-24-2022-4-26', - 'bitmap:action:editing-items:timespan:2022-4-24', - 'bitmap:action:editing-items:timespan:2022-4-25', - 'bitmap:action:editing-items:timespan:2022-4-26', + 'bitmap:action:register:timespan:2022-4-24-2022-4-26', + 'bitmap:action:register:timespan:2022-4-24', + 'bitmap:action:register:timespan:2022-4-25', + 'bitmap:action:register:timespan:2022-4-26', ) - expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:2022-4-24-2022-4-26') + expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:register:timespan:2022-4-24-2022-4-26') }) it('should not calculate total count over time of activities if period is unsupported', async () => { let caughtError = null try { - await createStore().calculateActivityTotalCountOverTime(AnalyticsActivity.EditingItems, Period.LastWeek) + await createStore().calculateActivityTotalCountOverTime(AnalyticsActivity.Register, Period.LastWeek) } catch (error) { caughtError = error } @@ -66,7 +66,7 @@ describe('RedisAnalyticsStore', () => { redisClient.bitcount = jest.fn().mockReturnValueOnce(70).mockReturnValueOnce(71).mockReturnValueOnce(72) expect( - await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.EditingItems, Period.Last30Days), + await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.Register, Period.Last30Days), ).toEqual([ { periodKey: '2022-4-24', @@ -82,9 +82,9 @@ describe('RedisAnalyticsStore', () => { }, ]) - expect(redisClient.bitcount).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:2022-4-24') - expect(redisClient.bitcount).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:2022-4-25') - expect(redisClient.bitcount).toHaveBeenNthCalledWith(3, 'bitmap:action:editing-items:timespan:2022-4-26') + expect(redisClient.bitcount).toHaveBeenNthCalledWith(1, 'bitmap:action:register:timespan:2022-4-24') + expect(redisClient.bitcount).toHaveBeenNthCalledWith(2, 'bitmap:action:register:timespan:2022-4-25') + expect(redisClient.bitcount).toHaveBeenNthCalledWith(3, 'bitmap:action:register:timespan:2022-4-26') }) it('should throw error on calculating total count changes of activities on unsupported period', async () => { @@ -94,7 +94,7 @@ describe('RedisAnalyticsStore', () => { let caughtError = null try { - await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.EditingItems, Period.LastWeek) + await createStore().calculateActivityChangesTotalCount(AnalyticsActivity.Register, Period.LastWeek) } catch (error) { caughtError = error } @@ -105,19 +105,17 @@ describe('RedisAnalyticsStore', () => { it('should calculate total count of activities by period', async () => { redisClient.bitcount = jest.fn().mockReturnValue(70) - expect(await createStore().calculateActivityTotalCount(AnalyticsActivity.EditingItems, Period.Yesterday)).toEqual( - 70, - ) + expect(await createStore().calculateActivityTotalCount(AnalyticsActivity.Register, Period.Yesterday)).toEqual(70) - expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:period-key') + expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:register:timespan:period-key') }) it('should calculate total count of activities by period key', async () => { redisClient.bitcount = jest.fn().mockReturnValue(70) - expect(await createStore().calculateActivityTotalCount(AnalyticsActivity.EditingItems, '2022-10-03')).toEqual(70) + expect(await createStore().calculateActivityTotalCount(AnalyticsActivity.Register, '2022-10-03')).toEqual(70) - expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:2022-10-03') + expect(redisClient.bitcount).toHaveBeenCalledWith('bitmap:action:register:timespan:2022-10-03') }) it('should calculate activity retention', async () => { @@ -125,7 +123,7 @@ describe('RedisAnalyticsStore', () => { expect( await createStore().calculateActivityRetention( - AnalyticsActivity.EditingItems, + AnalyticsActivity.Register, Period.DayBeforeYesterday, Period.Yesterday, ), @@ -133,44 +131,44 @@ describe('RedisAnalyticsStore', () => { expect(redisClient.bitop).toHaveBeenCalledWith( 'AND', - 'bitmap:action:editing-items-editing-items:timespan:period-key', - 'bitmap:action:editing-items:timespan:period-key', - 'bitmap:action:editing-items:timespan:period-key', + 'bitmap:action:register-register:timespan:period-key', + 'bitmap:action:register:timespan:period-key', + 'bitmap:action:register:timespan:period-key', ) }) it('shoud tell if activity was done', async () => { - await createStore().wasActivityDone(AnalyticsActivity.EditingItems, 123, Period.Yesterday) + await createStore().wasActivityDone(AnalyticsActivity.Register, 123, Period.Yesterday) - expect(redisClient.getbit).toHaveBeenCalledWith('bitmap:action:editing-items:timespan:period-key', 123) + expect(redisClient.getbit).toHaveBeenCalledWith('bitmap:action:register:timespan:period-key', 123) }) it('should mark activity as done', async () => { - await createStore().markActivity([AnalyticsActivity.EditingItems], 123, [Period.Today]) + await createStore().markActivity([AnalyticsActivity.Register], 123, [Period.Today]) expect(pipeline.setbit).toBeCalledTimes(1) - expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 1) + expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:register:timespan:period-key', 123, 1) expect(pipeline.exec).toHaveBeenCalled() }) it('should mark activities as done', async () => { - await createStore().markActivity([AnalyticsActivity.EditingItems, AnalyticsActivity.EmailUnbackedUpData], 123, [ + await createStore().markActivity([AnalyticsActivity.Register, AnalyticsActivity.SubscriptionPurchased], 123, [ Period.Today, Period.ThisWeek, ]) expect(pipeline.setbit).toBeCalledTimes(4) - expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 1) - expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:period-key', 123, 1) + expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:register:timespan:period-key', 123, 1) + expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:register:timespan:period-key', 123, 1) expect(pipeline.setbit).toHaveBeenNthCalledWith( 3, - 'bitmap:action:email-unbacked-up-data:timespan:period-key', + 'bitmap:action:subscription-purchased:timespan:period-key', 123, 1, ) expect(pipeline.setbit).toHaveBeenNthCalledWith( 4, - 'bitmap:action:email-unbacked-up-data:timespan:period-key', + 'bitmap:action:subscription-purchased:timespan:period-key', 123, 1, ) @@ -178,31 +176,31 @@ describe('RedisAnalyticsStore', () => { }) it('should unmark activity as done', async () => { - await createStore().unmarkActivity([AnalyticsActivity.EditingItems], 123, [Period.Today]) + await createStore().unmarkActivity([AnalyticsActivity.Register], 123, [Period.Today]) expect(pipeline.setbit).toBeCalledTimes(1) - expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 0) + expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:register:timespan:period-key', 123, 0) expect(pipeline.exec).toHaveBeenCalled() }) it('should unmark activities as done', async () => { - await createStore().unmarkActivity([AnalyticsActivity.EditingItems, AnalyticsActivity.EmailUnbackedUpData], 123, [ + await createStore().unmarkActivity([AnalyticsActivity.Register, AnalyticsActivity.SubscriptionPurchased], 123, [ Period.Today, Period.ThisWeek, ]) expect(pipeline.setbit).toBeCalledTimes(4) - expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:editing-items:timespan:period-key', 123, 0) - expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:editing-items:timespan:period-key', 123, 0) + expect(pipeline.setbit).toHaveBeenNthCalledWith(1, 'bitmap:action:register:timespan:period-key', 123, 0) + expect(pipeline.setbit).toHaveBeenNthCalledWith(2, 'bitmap:action:register:timespan:period-key', 123, 0) expect(pipeline.setbit).toHaveBeenNthCalledWith( 3, - 'bitmap:action:email-unbacked-up-data:timespan:period-key', + 'bitmap:action:subscription-purchased:timespan:period-key', 123, 0, ) expect(pipeline.setbit).toHaveBeenNthCalledWith( 4, - 'bitmap:action:email-unbacked-up-data:timespan:period-key', + 'bitmap:action:subscription-purchased:timespan:period-key', 123, 0, ) diff --git a/packages/analytics/src/Infra/Redis/RedisStatisticsStore.spec.ts b/packages/analytics/src/Infra/Redis/RedisStatisticsStore.spec.ts index 4236de3dc..33914a74e 100644 --- a/packages/analytics/src/Infra/Redis/RedisStatisticsStore.spec.ts +++ b/packages/analytics/src/Infra/Redis/RedisStatisticsStore.spec.ts @@ -1,7 +1,8 @@ import * as IORedis from 'ioredis' -import { Period, PeriodKeyGeneratorInterface } from '../../Domain' import { StatisticsMeasure } from '../../Domain/Statistics/StatisticsMeasure' +import { Period } from '../../Domain/Time/Period' +import { PeriodKeyGeneratorInterface } from '../../Domain/Time/PeriodKeyGeneratorInterface' import { RedisStatisticsStore } from './RedisStatisticsStore' diff --git a/packages/analytics/src/Infra/Redis/RedisStatisticsStore.ts b/packages/analytics/src/Infra/Redis/RedisStatisticsStore.ts index ac7175f28..90c14713f 100644 --- a/packages/analytics/src/Infra/Redis/RedisStatisticsStore.ts +++ b/packages/analytics/src/Infra/Redis/RedisStatisticsStore.ts @@ -1,9 +1,10 @@ import * as IORedis from 'ioredis' -import { Period, PeriodKeyGeneratorInterface } from '../../Domain' import { StatisticsMeasure } from '../../Domain/Statistics/StatisticsMeasure' import { StatisticsStoreInterface } from '../../Domain/Statistics/StatisticsStoreInterface' +import { Period } from '../../Domain/Time/Period' +import { PeriodKeyGeneratorInterface } from '../../Domain/Time/PeriodKeyGeneratorInterface' export class RedisStatisticsStore implements StatisticsStoreInterface { constructor(private periodKeyGenerator: PeriodKeyGeneratorInterface, private redisClient: IORedis.Redis) {} diff --git a/packages/analytics/src/Infra/index.ts b/packages/analytics/src/Infra/index.ts deleted file mode 100644 index fb303e395..000000000 --- a/packages/analytics/src/Infra/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Redis/RedisAnalyticsStore' -export * from './Redis/RedisStatisticsStore' diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts deleted file mode 100644 index 8d4e50e74..000000000 --- a/packages/analytics/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Domain' -export * from './Infra' diff --git a/packages/api-gateway/package.json b/packages/api-gateway/package.json index 9e604dbab..c0372c508 100644 --- a/packages/api-gateway/package.json +++ b/packages/api-gateway/package.json @@ -22,7 +22,6 @@ "dependencies": { "@newrelic/winston-enricher": "^4.0.0", "@sentry/node": "^7.3.0", - "@standardnotes/analytics": "workspace:*", "@standardnotes/common": "workspace:^", "@standardnotes/domain-events": "workspace:*", "@standardnotes/domain-events-infra": "workspace:*", diff --git a/packages/api-gateway/src/Bootstrap/Container.ts b/packages/api-gateway/src/Bootstrap/Container.ts index ecd1269fd..4c4d8d49f 100644 --- a/packages/api-gateway/src/Bootstrap/Container.ts +++ b/packages/api-gateway/src/Bootstrap/Container.ts @@ -2,14 +2,6 @@ import * as winston from 'winston' import axios, { AxiosInstance } from 'axios' import Redis from 'ioredis' import { Container } from 'inversify' -import { - AnalyticsStoreInterface, - PeriodKeyGenerator, - PeriodKeyGeneratorInterface, - RedisAnalyticsStore, - RedisStatisticsStore, - StatisticsStoreInterface, -} from '@standardnotes/analytics' import { Timer, TimerInterface } from '@standardnotes/time' import { Env } from './Env' @@ -18,7 +10,6 @@ import { AuthMiddleware } from '../Controller/AuthMiddleware' import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface' import { HttpService } from '../Service/Http/HttpService' import { SubscriptionTokenAuthMiddleware } from '../Controller/SubscriptionTokenAuthMiddleware' -import { StatisticsMiddleware } from '../Controller/StatisticsMiddleware' import { CrossServiceTokenCacheInterface } from '../Service/Cache/CrossServiceTokenCacheInterface' import { RedisCrossServiceTokenCache } from '../Infra/Redis/RedisCrossServiceTokenCache' import { WebSocketAuthMiddleware } from '../Controller/WebSocketAuthMiddleware' @@ -79,17 +70,9 @@ export class ContainerConfigLoader { container .bind(TYPES.SubscriptionTokenAuthMiddleware) .to(SubscriptionTokenAuthMiddleware) - container.bind(TYPES.StatisticsMiddleware).to(StatisticsMiddleware) // Services container.bind(TYPES.HTTPService).to(HttpService) - container.bind(TYPES.PeriodKeyGenerator).toConstantValue(new PeriodKeyGenerator()) - container - .bind(TYPES.AnalyticsStore) - .toConstantValue(new RedisAnalyticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis))) - container - .bind(TYPES.StatisticsStore) - .toConstantValue(new RedisStatisticsStore(container.get(TYPES.PeriodKeyGenerator), container.get(TYPES.Redis))) container.bind(TYPES.CrossServiceTokenCache).to(RedisCrossServiceTokenCache) container.bind(TYPES.Timer).toConstantValue(new Timer()) diff --git a/packages/api-gateway/src/Bootstrap/Types.ts b/packages/api-gateway/src/Bootstrap/Types.ts index f393272ef..23dee537e 100644 --- a/packages/api-gateway/src/Bootstrap/Types.ts +++ b/packages/api-gateway/src/Bootstrap/Types.ts @@ -15,17 +15,13 @@ const TYPES = { REDIS_EVENTS_CHANNEL: Symbol.for('REDIS_EVENTS_CHANNEL'), CROSS_SERVICE_TOKEN_CACHE_TTL: Symbol.for('CROSS_SERVICE_TOKEN_CACHE_TTL'), // Middleware - StatisticsMiddleware: Symbol.for('StatisticsMiddleware'), AuthMiddleware: Symbol.for('AuthMiddleware'), WebSocketAuthMiddleware: Symbol.for('WebSocketAuthMiddleware'), SubscriptionTokenAuthMiddleware: Symbol.for('SubscriptionTokenAuthMiddleware'), // Services HTTPService: Symbol.for('HTTPService'), CrossServiceTokenCache: Symbol.for('CrossServiceTokenCache'), - AnalyticsStore: Symbol.for('AnalyticsStore'), - StatisticsStore: Symbol.for('StatisticsStore'), Timer: Symbol.for('Timer'), - PeriodKeyGenerator: Symbol.for('PeriodKeyGenerator'), } export default TYPES diff --git a/packages/api-gateway/src/Controller/AuthMiddleware.ts b/packages/api-gateway/src/Controller/AuthMiddleware.ts index 60f809aad..4990eaaa0 100644 --- a/packages/api-gateway/src/Controller/AuthMiddleware.ts +++ b/packages/api-gateway/src/Controller/AuthMiddleware.ts @@ -1,6 +1,5 @@ import { CrossServiceTokenData } from '@standardnotes/security' import { RoleName } from '@standardnotes/common' -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { TimerInterface } from '@standardnotes/time' import { NextFunction, Request, Response } from 'express' import { inject, injectable } from 'inversify' @@ -21,7 +20,6 @@ export class AuthMiddleware extends BaseMiddleware { @inject(TYPES.CROSS_SERVICE_TOKEN_CACHE_TTL) private crossServiceTokenCacheTTL: number, @inject(TYPES.CrossServiceTokenCache) private crossServiceTokenCache: CrossServiceTokenCacheInterface, @inject(TYPES.Timer) private timer: TimerInterface, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.Logger) private logger: Logger, ) { super() @@ -80,17 +78,6 @@ export class AuthMiddleware extends BaseMiddleware { decodedToken.roles.length === 1 && decodedToken.roles.find((role) => role.name === RoleName.CoreUser) !== undefined - await this.analyticsStore.markActivity( - [ - AnalyticsActivity.GeneralActivity, - response.locals.freeUser - ? AnalyticsActivity.GeneralActivityFreeUsers - : AnalyticsActivity.GeneralActivityPaidUsers, - ], - decodedToken.analyticsId as number, - [Period.Today], - ) - if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) { await this.crossServiceTokenCache.set({ authorizationHeaderValue: authHeaderValue, diff --git a/packages/api-gateway/src/Controller/LegacyController.ts b/packages/api-gateway/src/Controller/LegacyController.ts index 7b5ac8bee..3e1c202e9 100644 --- a/packages/api-gateway/src/Controller/LegacyController.ts +++ b/packages/api-gateway/src/Controller/LegacyController.ts @@ -4,7 +4,7 @@ import { controller, all, BaseHttpController, httpPost, httpGet, results, httpDe import TYPES from '../Bootstrap/Types' import { HttpServiceInterface } from '../Service/Http/HttpServiceInterface' -@controller('', TYPES.StatisticsMiddleware) +@controller('') export class LegacyController extends BaseHttpController { private AUTH_ROUTES: Map private PARAMETRIZED_AUTH_ROUTES: Map diff --git a/packages/api-gateway/src/Controller/StatisticsMiddleware.ts b/packages/api-gateway/src/Controller/StatisticsMiddleware.ts deleted file mode 100644 index 18ac209ec..000000000 --- a/packages/api-gateway/src/Controller/StatisticsMiddleware.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { NextFunction, Request, Response } from 'express' -import { inject, injectable } from 'inversify' -import { BaseMiddleware } from 'inversify-express-utils' -import { Logger } from 'winston' -import { StatisticsStoreInterface } from '@standardnotes/analytics' - -import TYPES from '../Bootstrap/Types' - -@injectable() -export class StatisticsMiddleware extends BaseMiddleware { - constructor( - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, - @inject(TYPES.Logger) private logger: Logger, - ) { - super() - } - - async handler(request: Request, _response: Response, next: NextFunction): Promise { - try { - const snjsVersion = request.headers['x-snjs-version'] ?? 'unknown' - await this.statisticsStore.incrementSNJSVersionUsage(snjsVersion as string) - - const applicationVersion = request.headers['x-application-version'] ?? 'unknown' - await this.statisticsStore.incrementApplicationVersionUsage(applicationVersion as string) - } catch (error) { - this.logger.error(`Could not store analytics data: ${(error as Error).message}`) - } - - return next() - } -} diff --git a/packages/api-gateway/src/Controller/v1/ActionsController.ts b/packages/api-gateway/src/Controller/v1/ActionsController.ts index 85f692110..371bce9ed 100644 --- a/packages/api-gateway/src/Controller/v1/ActionsController.ts +++ b/packages/api-gateway/src/Controller/v1/ActionsController.ts @@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-exp import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1', TYPES.StatisticsMiddleware) +@controller('/v1') export class ActionsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/FilesController.ts b/packages/api-gateway/src/Controller/v1/FilesController.ts index 6fd15f904..0f4b69530 100644 --- a/packages/api-gateway/src/Controller/v1/FilesController.ts +++ b/packages/api-gateway/src/Controller/v1/FilesController.ts @@ -5,7 +5,7 @@ import { BaseHttpController, controller, httpPost } from 'inversify-express-util import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/files', TYPES.StatisticsMiddleware) +@controller('/v1/files') export class FilesController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/InvoicesController.ts b/packages/api-gateway/src/Controller/v1/InvoicesController.ts index ad4e454c0..fd2ac9c49 100644 --- a/packages/api-gateway/src/Controller/v1/InvoicesController.ts +++ b/packages/api-gateway/src/Controller/v1/InvoicesController.ts @@ -4,7 +4,7 @@ import { inject } from 'inversify' import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1', TYPES.StatisticsMiddleware) +@controller('/v1') export class InvoicesController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/ItemsController.ts b/packages/api-gateway/src/Controller/v1/ItemsController.ts index 14209ee2b..67ffddb8a 100644 --- a/packages/api-gateway/src/Controller/v1/ItemsController.ts +++ b/packages/api-gateway/src/Controller/v1/ItemsController.ts @@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-exp import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/items', TYPES.StatisticsMiddleware, TYPES.AuthMiddleware) +@controller('/v1/items', TYPES.AuthMiddleware) export class ItemsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/OfflineController.ts b/packages/api-gateway/src/Controller/v1/OfflineController.ts index 881332c1e..822b32c92 100644 --- a/packages/api-gateway/src/Controller/v1/OfflineController.ts +++ b/packages/api-gateway/src/Controller/v1/OfflineController.ts @@ -5,7 +5,7 @@ import { BaseHttpController, controller, httpGet, httpPost } from 'inversify-exp import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/offline', TYPES.StatisticsMiddleware) +@controller('/v1/offline') export class OfflineController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/PaymentsController.ts b/packages/api-gateway/src/Controller/v1/PaymentsController.ts index 1490862a5..6e81d9072 100644 --- a/packages/api-gateway/src/Controller/v1/PaymentsController.ts +++ b/packages/api-gateway/src/Controller/v1/PaymentsController.ts @@ -4,7 +4,7 @@ import { all, BaseHttpController, controller, httpDelete, httpGet, httpPost } fr import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1', TYPES.StatisticsMiddleware) +@controller('/v1') export class PaymentsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/RevisionsController.ts b/packages/api-gateway/src/Controller/v1/RevisionsController.ts index 9e961f552..0fad29b22 100644 --- a/packages/api-gateway/src/Controller/v1/RevisionsController.ts +++ b/packages/api-gateway/src/Controller/v1/RevisionsController.ts @@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-e import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/items/:item_id/revisions', TYPES.StatisticsMiddleware, TYPES.AuthMiddleware) +@controller('/v1/items/:item_id/revisions', TYPES.AuthMiddleware) export class RevisionsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/SessionsController.ts b/packages/api-gateway/src/Controller/v1/SessionsController.ts index b1fcca6f3..4f8522510 100644 --- a/packages/api-gateway/src/Controller/v1/SessionsController.ts +++ b/packages/api-gateway/src/Controller/v1/SessionsController.ts @@ -4,7 +4,7 @@ import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'i import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/sessions', TYPES.StatisticsMiddleware) +@controller('/v1/sessions') export class SessionsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts b/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts index e0691c484..959e2d0dd 100644 --- a/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts +++ b/packages/api-gateway/src/Controller/v1/SubscriptionInvitesController.ts @@ -5,7 +5,7 @@ import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'i import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/subscription-invites', TYPES.StatisticsMiddleware) +@controller('/v1/subscription-invites') export class SubscriptionInvitesController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/TokensController.ts b/packages/api-gateway/src/Controller/v1/TokensController.ts index 5257b0c41..4e5f6823f 100644 --- a/packages/api-gateway/src/Controller/v1/TokensController.ts +++ b/packages/api-gateway/src/Controller/v1/TokensController.ts @@ -5,7 +5,7 @@ import { BaseHttpController, controller, httpPost } from 'inversify-express-util import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/subscription-tokens', TYPES.StatisticsMiddleware) +@controller('/v1/subscription-tokens') export class TokensController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v1/UsersController.ts b/packages/api-gateway/src/Controller/v1/UsersController.ts index f404b4735..908af27e0 100644 --- a/packages/api-gateway/src/Controller/v1/UsersController.ts +++ b/packages/api-gateway/src/Controller/v1/UsersController.ts @@ -16,7 +16,7 @@ import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' import { TokenAuthenticationMethod } from '../TokenAuthenticationMethod' -@controller('/v1/users', TYPES.StatisticsMiddleware) +@controller('/v1/users') export class UsersController extends BaseHttpController { constructor( @inject(TYPES.HTTPService) private httpService: HttpServiceInterface, diff --git a/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts b/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts index ccf4741d9..81e8aa850 100644 --- a/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts +++ b/packages/api-gateway/src/Controller/v2/ActionsControllerV2.ts @@ -5,7 +5,7 @@ import { BaseHttpController, controller, httpPost } from 'inversify-express-util import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v2', TYPES.StatisticsMiddleware) +@controller('/v2') export class ActionsControllerV2 extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts b/packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts index 9f424b8a6..6e3c07200 100644 --- a/packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts +++ b/packages/api-gateway/src/Controller/v2/PaymentsControllerV2.ts @@ -4,7 +4,7 @@ import { inject } from 'inversify' import TYPES from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v2', TYPES.StatisticsMiddleware) +@controller('/v2') export class PaymentsControllerV2 extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() diff --git a/packages/auth/.env.sample b/packages/auth/.env.sample index 1549bed9b..399a562ff 100644 --- a/packages/auth/.env.sample +++ b/packages/auth/.env.sample @@ -67,6 +67,3 @@ VALET_TOKEN_SECRET= VALET_TOKEN_TTL= WEB_SOCKET_CONNECTION_TOKEN_SECRET= - -# (Optional) Analytics -ANALYTICS_ENABLED=false diff --git a/packages/auth/bin/backup.ts b/packages/auth/bin/backup.ts index 286be9dd1..e0f6e524f 100644 --- a/packages/auth/bin/backup.ts +++ b/packages/auth/bin/backup.ts @@ -7,7 +7,6 @@ import { Stream } from 'stream' import { Logger } from 'winston' import * as dayjs from 'dayjs' import * as utc from 'dayjs/plugin/utc' -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { ContainerConfigLoader } from '../src/Bootstrap/Container' import TYPES from '../src/Bootstrap/Types' @@ -19,44 +18,17 @@ import { MuteFailedBackupsEmailsOption, MuteFailedCloudBackupsEmailsOption, Sett import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface' import { PermissionName } from '@standardnotes/features' import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface' -import { AnalyticsEntityRepositoryInterface } from '../src/Domain/Analytics/AnalyticsEntityRepositoryInterface' const inputArgs = process.argv.slice(2) const backupProvider = inputArgs[0] const backupFrequency = inputArgs[1] -const shouldEmailBackupBeTriggered = async ( - analyticsId: number, - analyticsStore: AnalyticsStoreInterface, -): Promise => { - let periods = [Period.Today, Period.Yesterday] - if (backupFrequency === 'weekly') { - periods = [Period.ThisWeek, Period.LastWeek] - } - - for (const period of periods) { - const wasUnBackedUpDataCreatedInPeriod = await analyticsStore.wasActivityDone( - AnalyticsActivity.EmailUnbackedUpData, - analyticsId, - period, - ) - if (wasUnBackedUpDataCreatedInPeriod) { - return true - } - } - - return false -} - const requestBackups = async ( settingRepository: SettingRepositoryInterface, roleService: RoleServiceInterface, settingService: SettingServiceInterface, domainEventFactory: DomainEventFactoryInterface, domainEventPublisher: DomainEventPublisherInterface, - analyticsEntityRepository: AnalyticsEntityRepositoryInterface, - analyticsStore: AnalyticsStoreInterface, - logger: Logger, ): Promise => { let settingName: SettingName, permissionName: PermissionName, @@ -123,23 +95,6 @@ const requestBackups = async ( } if (backupProvider === 'email') { - const analyticsEntity = await analyticsEntityRepository.findOneByUserUuid(setting.setting_user_uuid) - if (analyticsEntity === null) { - callback() - - return - } - - const emailBackupsShouldBeTriggered = await shouldEmailBackupBeTriggered( - analyticsEntity.id, - analyticsStore, - ) - if (!emailBackupsShouldBeTriggered) { - logger.info( - `Email backup for user ${setting.setting_user_uuid} should not be triggered due to inactivity. It will be triggered until further changes.`, - ) - } - await domainEventPublisher.publish( domainEventFactory.createEmailBackupRequestedEvent( setting.setting_user_uuid, @@ -148,15 +103,6 @@ const requestBackups = async ( ), ) - await analyticsStore.markActivity([AnalyticsActivity.EmailBackup], analyticsEntity.id, [ - Period.Today, - Period.ThisWeek, - ]) - await analyticsStore.unmarkActivity([AnalyticsActivity.EmailUnbackedUpData], analyticsEntity.id, [ - Period.Today, - Period.ThisWeek, - ]) - callback() return @@ -206,20 +152,9 @@ void container.load().then((container) => { const settingService: SettingServiceInterface = container.get(TYPES.SettingService) const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory) const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher) - const analyticsEntityRepository: AnalyticsEntityRepositoryInterface = container.get(TYPES.AnalyticsEntityRepository) - const analyticsStore: AnalyticsStoreInterface = container.get(TYPES.AnalyticsStore) Promise.resolve( - requestBackups( - settingRepository, - roleService, - settingService, - domainEventFactory, - domainEventPublisher, - analyticsEntityRepository, - analyticsStore, - logger, - ), + requestBackups(settingRepository, roleService, settingService, domainEventFactory, domainEventPublisher), ) .then(() => { logger.info(`${backupFrequency} ${backupProvider} backup requesting complete`) diff --git a/packages/auth/bin/user_email_backup.ts b/packages/auth/bin/user_email_backup.ts index 348d15f18..ce3288c14 100644 --- a/packages/auth/bin/user_email_backup.ts +++ b/packages/auth/bin/user_email_backup.ts @@ -5,7 +5,6 @@ import 'newrelic' import { Logger } from 'winston' import * as dayjs from 'dayjs' import * as utc from 'dayjs/plugin/utc' -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { ContainerConfigLoader } from '../src/Bootstrap/Container' import TYPES from '../src/Bootstrap/Types' @@ -16,7 +15,6 @@ import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingReposit import { MuteFailedBackupsEmailsOption, SettingName } from '@standardnotes/settings' import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface' import { PermissionName } from '@standardnotes/features' -import { AnalyticsEntityRepositoryInterface } from '../src/Domain/Analytics/AnalyticsEntityRepositoryInterface' import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface' const inputArgs = process.argv.slice(2) @@ -28,8 +26,6 @@ const requestBackups = async ( roleService: RoleServiceInterface, domainEventFactory: DomainEventFactoryInterface, domainEventPublisher: DomainEventPublisherInterface, - analyticsEntityRepository: AnalyticsEntityRepositoryInterface, - analyticsStore: AnalyticsStoreInterface, ): Promise => { const permissionName = PermissionName.DailyEmailBackup const muteEmailsSettingName = SettingName.MuteFailedBackupsEmails @@ -55,11 +51,6 @@ const requestBackups = async ( userHasEmailsMuted = emailsMutedSetting.value === muteEmailsSettingValue } - const analyticsEntity = await analyticsEntityRepository.findOneByUserUuid(user.uuid) - if (analyticsEntity === null) { - return - } - await domainEventPublisher.publish( domainEventFactory.createEmailBackupRequestedEvent( user.uuid, @@ -68,15 +59,6 @@ const requestBackups = async ( ), ) - await analyticsStore.markActivity([AnalyticsActivity.EmailBackup], analyticsEntity.id, [ - Period.Today, - Period.ThisWeek, - ]) - await analyticsStore.unmarkActivity([AnalyticsActivity.EmailUnbackedUpData], analyticsEntity.id, [ - Period.Today, - Period.ThisWeek, - ]) - return } @@ -96,19 +78,9 @@ void container.load().then((container) => { const roleService: RoleServiceInterface = container.get(TYPES.RoleService) const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory) const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher) - const analyticsEntityRepository: AnalyticsEntityRepositoryInterface = container.get(TYPES.AnalyticsEntityRepository) - const analyticsStore: AnalyticsStoreInterface = container.get(TYPES.AnalyticsStore) Promise.resolve( - requestBackups( - userRepository, - settingRepository, - roleService, - domainEventFactory, - domainEventPublisher, - analyticsEntityRepository, - analyticsStore, - ), + requestBackups(userRepository, settingRepository, roleService, domainEventFactory, domainEventPublisher), ) .then(() => { logger.info(`Email backup requesting complete for ${backupEmail}`) diff --git a/packages/auth/package.json b/packages/auth/package.json index 1d12ed7c1..88ffa0bb0 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -32,7 +32,6 @@ "dependencies": { "@newrelic/winston-enricher": "^4.0.0", "@sentry/node": "^7.3.0", - "@standardnotes/analytics": "workspace:*", "@standardnotes/api": "^1.19.0", "@standardnotes/common": "workspace:*", "@standardnotes/domain-events": "workspace:*", diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index b841a18b1..6ea46914a 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -9,13 +9,6 @@ import { } from '@standardnotes/domain-events' import { TimerInterface, Timer } from '@standardnotes/time' import { UAParser } from 'ua-parser-js' -import { - AnalyticsStoreInterface, - PeriodKeyGenerator, - RedisAnalyticsStore, - RedisStatisticsStore, - StatisticsStoreInterface, -} from '@standardnotes/analytics' import { Env } from './Env' import TYPES from './Types' @@ -191,21 +184,13 @@ import { RoleRepositoryInterface } from '../Domain/Role/RoleRepositoryInterface' import { RevokedSessionRepositoryInterface } from '../Domain/Session/RevokedSessionRepositoryInterface' import { SessionRepositoryInterface } from '../Domain/Session/SessionRepositoryInterface' import { UserRepositoryInterface } from '../Domain/User/UserRepositoryInterface' -import { AnalyticsEntity } from '../Domain/Analytics/AnalyticsEntity' -import { AnalyticsEntityRepositoryInterface } from '../Domain/Analytics/AnalyticsEntityRepositoryInterface' -import { MySQLAnalyticsEntityRepository } from '../Infra/MySQL/MySQLAnalyticsEntityRepository' -import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId' import { AuthController } from '../Controller/AuthController' import { VerifyPredicate } from '../Domain/UseCase/VerifyPredicate/VerifyPredicate' import { PredicateVerificationRequestedEventHandler } from '../Domain/Handler/PredicateVerificationRequestedEventHandler' -import { PaymentFailedEventHandler } from '../Domain/Handler/PaymentFailedEventHandler' -import { PaymentSuccessEventHandler } from '../Domain/Handler/PaymentSuccessEventHandler' -import { RefundProcessedEventHandler } from '../Domain/Handler/RefundProcessedEventHandler' import { SubscriptionInvitesController } from '../Controller/SubscriptionInvitesController' import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken' import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/ProcessUserRequest' import { UserRequestsController } from '../Controller/UserRequestsController' -import { SubscriptionReactivatedEventHandler } from '../Domain/Handler/SubscriptionReactivatedEventHandler' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -301,9 +286,6 @@ export class ContainerConfigLoader { .bind(TYPES.SharedSubscriptionInvitationRepository) .to(MySQLSharedSubscriptionInvitationRepository) container.bind(TYPES.PKCERepository).to(RedisPKCERepository) - container - .bind(TYPES.AnalyticsEntityRepository) - .to(MySQLAnalyticsEntityRepository) // ORM container @@ -332,9 +314,6 @@ export class ContainerConfigLoader { container .bind>(TYPES.ORMUserSubscriptionRepository) .toConstantValue(AppDataSource.getRepository(UserSubscription)) - container - .bind>(TYPES.ORMAnalyticsEntityRepository) - .toConstantValue(AppDataSource.getRepository(AnalyticsEntity)) // Middleware container.bind(TYPES.AuthMiddleware).to(AuthMiddleware) @@ -379,7 +358,6 @@ export class ContainerConfigLoader { container .bind(TYPES.DISABLE_USER_REGISTRATION) .toConstantValue(env.get('DISABLE_USER_REGISTRATION', true) === 'true') - container.bind(TYPES.ANALYTICS_ENABLED).toConstantValue(env.get('ANALYTICS_ENABLED', true) === 'true') container.bind(TYPES.SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true)) container.bind(TYPES.SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true)) container.bind(TYPES.SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true)) @@ -439,7 +417,6 @@ export class ContainerConfigLoader { .bind(TYPES.ListSharedSubscriptionInvitations) .to(ListSharedSubscriptionInvitations) container.bind(TYPES.GetSubscriptionSetting).to(GetSubscriptionSetting) - container.bind(TYPES.GetUserAnalyticsId).to(GetUserAnalyticsId) container.bind(TYPES.VerifyPredicate).to(VerifyPredicate) container.bind(TYPES.CreateCrossServiceToken).to(CreateCrossServiceToken) container.bind(TYPES.ProcessUserRequest).to(ProcessUserRequest) @@ -491,12 +468,6 @@ export class ContainerConfigLoader { container .bind(TYPES.PredicateVerificationRequestedEventHandler) .to(PredicateVerificationRequestedEventHandler) - container.bind(TYPES.PaymentFailedEventHandler).to(PaymentFailedEventHandler) - container.bind(TYPES.PaymentSuccessEventHandler).to(PaymentSuccessEventHandler) - container.bind(TYPES.RefundProcessedEventHandler).to(RefundProcessedEventHandler) - container - .bind(TYPES.SubscriptionReactivatedEventHandler) - .to(SubscriptionReactivatedEventHandler) // Services container.bind(TYPES.DeviceDetector).toConstantValue(new UAParser()) @@ -562,13 +533,6 @@ export class ContainerConfigLoader { .bind>(TYPES.BooleanSelector) .toConstantValue(new DeterministicSelector()) container.bind(TYPES.UserSubscriptionService).to(UserSubscriptionService) - const periodKeyGenerator = new PeriodKeyGenerator() - container - .bind(TYPES.AnalyticsStore) - .toConstantValue(new RedisAnalyticsStore(periodKeyGenerator, container.get(TYPES.Redis))) - container - .bind(TYPES.StatisticsStore) - .toConstantValue(new RedisStatisticsStore(periodKeyGenerator, container.get(TYPES.Redis))) container.bind>(TYPES.UuidValidator).toConstantValue(new UuidValidator()) if (env.get('SNS_TOPIC_ARN', true)) { @@ -605,10 +569,6 @@ export class ContainerConfigLoader { ], ['SHARED_SUBSCRIPTION_INVITATION_CREATED', container.get(TYPES.SharedSubscriptionInvitationCreatedEventHandler)], ['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.PredicateVerificationRequestedEventHandler)], - ['PAYMENT_FAILED', container.get(TYPES.PaymentFailedEventHandler)], - ['PAYMENT_SUCCESS', container.get(TYPES.PaymentSuccessEventHandler)], - ['REFUND_PROCESSED', container.get(TYPES.RefundProcessedEventHandler)], - ['SUBSCRIPTION_REACTIVATED', container.get(TYPES.SubscriptionReactivatedEventHandler)], ]) if (env.get('SQS_QUEUE_URL', true)) { diff --git a/packages/auth/src/Bootstrap/DataSource.ts b/packages/auth/src/Bootstrap/DataSource.ts index 39678b653..0aa2165c5 100644 --- a/packages/auth/src/Bootstrap/DataSource.ts +++ b/packages/auth/src/Bootstrap/DataSource.ts @@ -1,5 +1,4 @@ import { DataSource, LoggerOptions } from 'typeorm' -import { AnalyticsEntity } from '../Domain/Analytics/AnalyticsEntity' import { Permission } from '../Domain/Permission/Permission' import { Role } from '../Domain/Role/Role' import { RevokedSession } from '../Domain/Session/RevokedSession' @@ -57,7 +56,6 @@ export const AppDataSource = new DataSource({ OfflineSetting, SharedSubscriptionInvitation, SubscriptionSetting, - AnalyticsEntity, ], migrations: [env.get('DB_MIGRATIONS_PATH', true) ?? 'dist/migrations/*.js'], migrationsRun: true, diff --git a/packages/auth/src/Bootstrap/Types.ts b/packages/auth/src/Bootstrap/Types.ts index a9b7070a4..9667e2db2 100644 --- a/packages/auth/src/Bootstrap/Types.ts +++ b/packages/auth/src/Bootstrap/Types.ts @@ -23,7 +23,6 @@ const TYPES = { OfflineSubscriptionTokenRepository: Symbol.for('OfflineSubscriptionTokenRepository'), SharedSubscriptionInvitationRepository: Symbol.for('SharedSubscriptionInvitationRepository'), PKCERepository: Symbol.for('PKCERepository'), - AnalyticsEntityRepository: Symbol.for('AnalyticsEntityRepository'), // ORM ORMOfflineSettingRepository: Symbol.for('ORMOfflineSettingRepository'), ORMOfflineUserSubscriptionRepository: Symbol.for('ORMOfflineUserSubscriptionRepository'), @@ -35,7 +34,6 @@ const TYPES = { ORMSubscriptionSettingRepository: Symbol.for('ORMSubscriptionSettingRepository'), ORMUserRepository: Symbol.for('ORMUserRepository'), ORMUserSubscriptionRepository: Symbol.for('ORMUserSubscriptionRepository'), - ORMAnalyticsEntityRepository: Symbol.for('ORMAnalyticsEntityRepository'), // Middleware AuthMiddleware: Symbol.for('AuthMiddleware'), ApiGatewayAuthMiddleware: Symbol.for('ApiGatewayAuthMiddleware'), @@ -71,7 +69,6 @@ const TYPES = { PSEUDO_KEY_PARAMS_KEY: Symbol.for('PSEUDO_KEY_PARAMS_KEY'), REDIS_URL: Symbol.for('REDIS_URL'), DISABLE_USER_REGISTRATION: Symbol.for('DISABLE_USER_REGISTRATION'), - ANALYTICS_ENABLED: Symbol.for('ANALYTICS_ENABLED'), SNS_TOPIC_ARN: Symbol.for('SNS_TOPIC_ARN'), SNS_AWS_REGION: Symbol.for('SNS_AWS_REGION'), SQS_QUEUE_URL: Symbol.for('SQS_QUEUE_URL'), @@ -119,7 +116,6 @@ const TYPES = { CancelSharedSubscriptionInvitation: Symbol.for('CancelSharedSubscriptionInvitation'), ListSharedSubscriptionInvitations: Symbol.for('ListSharedSubscriptionInvitations'), GetSubscriptionSetting: Symbol.for('GetSubscriptionSetting'), - GetUserAnalyticsId: Symbol.for('GetUserAnalyticsId'), VerifyPredicate: Symbol.for('VerifyPredicate'), CreateCrossServiceToken: Symbol.for('CreateCrossServiceToken'), ProcessUserRequest: Symbol.for('ProcessUserRequest'), @@ -142,10 +138,6 @@ const TYPES = { UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for('UserDisabledSessionUserAgentLoggingEventHandler'), SharedSubscriptionInvitationCreatedEventHandler: Symbol.for('SharedSubscriptionInvitationCreatedEventHandler'), PredicateVerificationRequestedEventHandler: Symbol.for('PredicateVerificationRequestedEventHandler'), - PaymentFailedEventHandler: Symbol.for('PaymentFailedEventHandler'), - PaymentSuccessEventHandler: Symbol.for('PaymentSuccessEventHandler'), - RefundProcessedEventHandler: Symbol.for('RefundProcessedEventHandler'), - SubscriptionReactivatedEventHandler: Symbol.for('SubscriptionReactivatedEventHandler'), // Services DeviceDetector: Symbol.for('DeviceDetector'), SessionService: Symbol.for('SessionService'), @@ -187,8 +179,6 @@ const TYPES = { ProtocolVersionSelector: Symbol.for('ProtocolVersionSelector'), BooleanSelector: Symbol.for('BooleanSelector'), UserSubscriptionService: Symbol.for('UserSubscriptionService'), - AnalyticsStore: Symbol.for('AnalyticsStore'), - StatisticsStore: Symbol.for('StatisticsStore'), UuidValidator: Symbol.for('UuidValidator'), } diff --git a/packages/auth/src/Controller/SubscriptionTokensController.spec.ts b/packages/auth/src/Controller/SubscriptionTokensController.spec.ts index 95e25fe22..2c54c00ce 100644 --- a/packages/auth/src/Controller/SubscriptionTokensController.spec.ts +++ b/packages/auth/src/Controller/SubscriptionTokensController.spec.ts @@ -13,7 +13,6 @@ import { Role } from '../Domain/Role/Role' import { SettingServiceInterface } from '../Domain/Setting/SettingServiceInterface' import { Setting } from '../Domain/Setting/Setting' import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security' -import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId' describe('SubscriptionTokensController', () => { let createSubscriptionToken: CreateSubscriptionToken @@ -24,7 +23,6 @@ describe('SubscriptionTokensController', () => { let settingService: SettingServiceInterface let extensionKeySetting: Setting let tokenEncoder: TokenEncoderInterface - let getUserAnalyticsId: GetUserAnalyticsId let request: express.Request let response: express.Response @@ -39,7 +37,6 @@ describe('SubscriptionTokensController', () => { userProjector, roleProjector, tokenEncoder, - getUserAnalyticsId, jwtTTL, ) @@ -78,9 +75,6 @@ describe('SubscriptionTokensController', () => { tokenEncoder = {} as jest.Mocked> tokenEncoder.encodeExpirableToken = jest.fn().mockReturnValue('foobar') - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 123 }) - request = { headers: {}, body: {}, @@ -137,7 +131,6 @@ describe('SubscriptionTokensController', () => { expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( { - analyticsId: 123, extensionKey: 'abc123', roles: [ { diff --git a/packages/auth/src/Controller/SubscriptionTokensController.ts b/packages/auth/src/Controller/SubscriptionTokensController.ts index 2742cd977..106773db6 100644 --- a/packages/auth/src/Controller/SubscriptionTokensController.ts +++ b/packages/auth/src/Controller/SubscriptionTokensController.ts @@ -16,7 +16,6 @@ import { Role } from '../Domain/Role/Role' import { SettingServiceInterface } from '../Domain/Setting/SettingServiceInterface' import { AuthenticateSubscriptionToken } from '../Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken' import { CreateSubscriptionToken } from '../Domain/UseCase/CreateSubscriptionToken/CreateSubscriptionToken' -import { GetUserAnalyticsId } from '../Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId' import { User } from '../Domain/User/User' import { ProjectorInterface } from '../Projection/ProjectorInterface' @@ -29,7 +28,6 @@ export class SubscriptionTokensController extends BaseHttpController { @inject(TYPES.UserProjector) private userProjector: ProjectorInterface, @inject(TYPES.RoleProjector) private roleProjector: ProjectorInterface, @inject(TYPES.CrossServiceTokenEncoder) private tokenEncoder: TokenEncoderInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, @inject(TYPES.AUTH_JWT_TTL) private jwtTTL: number, ) { super() @@ -88,13 +86,10 @@ export class SubscriptionTokensController extends BaseHttpController { const roles = await user.roles - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - const authTokenData: CrossServiceTokenData = { user: await this.projectUser(user), roles: await this.projectRoles(roles), extensionKey, - analyticsId, } const authToken = this.tokenEncoder.encodeExpirableToken(authTokenData, this.jwtTTL) diff --git a/packages/auth/src/Domain/Analytics/AnalyticsEntity.ts b/packages/auth/src/Domain/Analytics/AnalyticsEntity.ts deleted file mode 100644 index 638a9d501..000000000 --- a/packages/auth/src/Domain/Analytics/AnalyticsEntity.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm' -import { User } from '../User/User' - -@Entity({ name: 'analytics_entities' }) -export class AnalyticsEntity { - @PrimaryGeneratedColumn() - declare id: number - - @OneToOne( - /* istanbul ignore next */ - () => User, - /* istanbul ignore next */ - (user) => user.analyticsEntity, - /* istanbul ignore next */ - { onDelete: 'CASCADE', nullable: false, lazy: true, eager: false }, - ) - @JoinColumn({ name: 'user_uuid', referencedColumnName: 'uuid' }) - declare user: Promise -} diff --git a/packages/auth/src/Domain/Analytics/AnalyticsEntityRepositoryInterface.ts b/packages/auth/src/Domain/Analytics/AnalyticsEntityRepositoryInterface.ts deleted file mode 100644 index 64e6efdd4..000000000 --- a/packages/auth/src/Domain/Analytics/AnalyticsEntityRepositoryInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Uuid } from '@standardnotes/common' -import { AnalyticsEntity } from './AnalyticsEntity' - -export interface AnalyticsEntityRepositoryInterface { - save(analyticsEntity: AnalyticsEntity): Promise - findOneByUserUuid(userUuid: Uuid): Promise -} diff --git a/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts index 4a91b2811..87c9cea96 100644 --- a/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.spec.ts @@ -11,9 +11,6 @@ import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterfac import { User } from '../User/User' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { AnalyticsStoreInterface, StatisticsStoreInterface } from '@standardnotes/analytics' -import { TimerInterface } from '@standardnotes/time' describe('AccountDeletionRequestedEventHandler', () => { let userRepository: UserRepositoryInterface @@ -26,10 +23,6 @@ describe('AccountDeletionRequestedEventHandler', () => { let revokedSession: RevokedSession let user: User let event: AccountDeletionRequestedEvent - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - let statisticsStore: StatisticsStoreInterface - let timer: TimerInterface const createHandler = () => new AccountDeletionRequestedEventHandler( @@ -37,10 +30,6 @@ describe('AccountDeletionRequestedEventHandler', () => { sessionRepository, ephemeralSessionRepository, revokedSessionRepository, - getUserAnalyticsId, - analyticsStore, - statisticsStore, - timer, logger, ) @@ -84,22 +73,9 @@ describe('AccountDeletionRequestedEventHandler', () => { regularSubscriptionUuid: '2-3-4', } - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - logger = {} as jest.Mocked logger.info = jest.fn() logger.warn = jest.fn() - - statisticsStore = {} as jest.Mocked - statisticsStore.incrementMeasure = jest.fn() - - timer = {} as jest.Mocked - timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(123) - timer.convertDateToMicroseconds = jest.fn().mockReturnValue(100) }) it('should remove a user', async () => { diff --git a/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts b/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts index a88974d87..e7c6e45b4 100644 --- a/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/AccountDeletionRequestedEventHandler.ts @@ -1,19 +1,10 @@ -import { - AnalyticsActivity, - AnalyticsStoreInterface, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events' -import { TimerInterface } from '@standardnotes/time' import { inject, injectable } from 'inversify' import { Logger } from 'winston' import TYPES from '../../Bootstrap/Types' import { EphemeralSessionRepositoryInterface } from '../Session/EphemeralSessionRepositoryInterface' import { RevokedSessionRepositoryInterface } from '../Session/RevokedSessionRepositoryInterface' import { SessionRepositoryInterface } from '../Session/SessionRepositoryInterface' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' @injectable() @@ -23,10 +14,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI @inject(TYPES.SessionRepository) private sessionRepository: SessionRepositoryInterface, @inject(TYPES.EphemeralSessionRepository) private ephemeralSessionRepository: EphemeralSessionRepositoryInterface, @inject(TYPES.RevokedSessionRepository) private revokedSessionRepository: RevokedSessionRepositoryInterface, - @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, ) {} @@ -41,21 +28,6 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI await this.removeSessions(event.payload.userUuid) - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.DeleteAccount], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - - const registrationLength = - this.timer.getTimestampInMicroseconds() - this.timer.convertDateToMicroseconds(user.createdAt) - await this.statisticsStore.incrementMeasure(StatisticsMeasure.RegistrationLength, registrationLength, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - await this.userRepository.remove(user) this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`) diff --git a/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.spec.ts deleted file mode 100644 index 75f1420ba..000000000 --- a/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.spec.ts +++ /dev/null @@ -1,51 +0,0 @@ -import 'reflect-metadata' - -import { PaymentFailedEvent } from '@standardnotes/domain-events' -import { AnalyticsStoreInterface } from '@standardnotes/analytics' - -import { PaymentFailedEventHandler } from './PaymentFailedEventHandler' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' -import { User } from '../User/User' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' - -describe('PaymentFailedEventHandler', () => { - let userRepository: UserRepositoryInterface - let event: PaymentFailedEvent - let user: User - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - - const createHandler = () => new PaymentFailedEventHandler(userRepository, getUserAnalyticsId, analyticsStore) - - beforeEach(() => { - user = {} as jest.Mocked - - userRepository = {} as jest.Mocked - userRepository.findOneByEmail = jest.fn().mockReturnValue(user) - - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - - event = {} as jest.Mocked - event.payload = { - userEmail: 'test@test.com', - } - }) - - it('should mark payment failed for analytics', async () => { - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalled() - }) - - it('should not mark payment failed for analytics if user is not found', async () => { - userRepository.findOneByEmail = jest.fn().mockReturnValue(null) - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).not.toHaveBeenCalled() - }) -}) diff --git a/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.ts b/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.ts deleted file mode 100644 index a3c4ee3c5..000000000 --- a/packages/auth/src/Domain/Handler/PaymentFailedEventHandler.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' -import { DomainEventHandlerInterface, PaymentFailedEvent } from '@standardnotes/domain-events' -import { inject, injectable } from 'inversify' - -import TYPES from '../../Bootstrap/Types' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' - -@injectable() -export class PaymentFailedEventHandler implements DomainEventHandlerInterface { - constructor( - @inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - ) {} - - async handle(event: PaymentFailedEvent): Promise { - const user = await this.userRepository.findOneByEmail(event.payload.userEmail) - if (user === null) { - return - } - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.PaymentFailed], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - } -} diff --git a/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.spec.ts b/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.spec.ts deleted file mode 100644 index 7f3763da0..000000000 --- a/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.spec.ts +++ /dev/null @@ -1,90 +0,0 @@ -import 'reflect-metadata' - -import { PaymentSuccessEvent } from '@standardnotes/domain-events' -import { AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics' - -import { PaymentSuccessEventHandler } from './PaymentSuccessEventHandler' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' -import { User } from '../User/User' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { Logger } from 'winston' - -describe('PaymentSuccessEventHandler', () => { - let userRepository: UserRepositoryInterface - let event: PaymentSuccessEvent - let user: User - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - let statisticsStore: StatisticsStoreInterface - let logger: Logger - - const createHandler = () => - new PaymentSuccessEventHandler(userRepository, getUserAnalyticsId, analyticsStore, statisticsStore, logger) - - beforeEach(() => { - user = {} as jest.Mocked - - userRepository = {} as jest.Mocked - userRepository.findOneByEmail = jest.fn().mockReturnValue(user) - - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - - statisticsStore = {} as jest.Mocked - statisticsStore.incrementMeasure = jest.fn() - - event = {} as jest.Mocked - event.payload = { - userEmail: 'test@test.com', - amount: 12.45, - billingFrequency: 12, - paymentType: 'initial', - subscriptionName: 'PRO_PLAN', - } - - logger = {} as jest.Mocked - logger.warn = jest.fn() - }) - - it('should mark payment success for analytics', async () => { - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalled() - expect(statisticsStore.incrementMeasure).toHaveBeenNthCalledWith( - 2, - 'pro-subscription-initial-annual-payments-income', - 12.45, - [Period.Today, Period.ThisWeek, Period.ThisMonth], - ) - }) - - it('should mark non-detailed payment success statistics for analytics', async () => { - event.payload = { - userEmail: 'test@test.com', - amount: 12.45, - billingFrequency: 13, - paymentType: 'initial', - subscriptionName: 'PRO_PLAN', - } - - await createHandler().handle(event) - - expect(statisticsStore.incrementMeasure).toBeCalledTimes(1) - expect(statisticsStore.incrementMeasure).toHaveBeenNthCalledWith(1, 'income', 12.45, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - }) - - it('should not mark payment failed for analytics if user is not found', async () => { - userRepository.findOneByEmail = jest.fn().mockReturnValue(null) - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).not.toHaveBeenCalled() - }) -}) diff --git a/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.ts b/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.ts deleted file mode 100644 index f536f4176..000000000 --- a/packages/auth/src/Domain/Handler/PaymentSuccessEventHandler.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { - AnalyticsActivity, - AnalyticsStoreInterface, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' -import { PaymentType, SubscriptionBillingFrequency, SubscriptionName } from '@standardnotes/common' -import { DomainEventHandlerInterface, PaymentSuccessEvent } from '@standardnotes/domain-events' -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' - -import TYPES from '../../Bootstrap/Types' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' - -@injectable() -export class PaymentSuccessEventHandler implements DomainEventHandlerInterface { - private readonly DETAILED_MEASURES = new Map([ - [ - SubscriptionName.PlusPlan, - new Map([ - [ - PaymentType.Initial, - new Map([ - [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.PlusSubscriptionInitialMonthlyPaymentsIncome], - [SubscriptionBillingFrequency.Annual, StatisticsMeasure.PlusSubscriptionInitialAnnualPaymentsIncome], - ]), - ], - [ - PaymentType.Renewal, - new Map([ - [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.PlusSubscriptionRenewingMonthlyPaymentsIncome], - [SubscriptionBillingFrequency.Annual, StatisticsMeasure.PlusSubscriptionRenewingAnnualPaymentsIncome], - ]), - ], - ]), - ], - [ - SubscriptionName.ProPlan, - new Map([ - [ - PaymentType.Initial, - new Map([ - [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.ProSubscriptionInitialMonthlyPaymentsIncome], - [SubscriptionBillingFrequency.Annual, StatisticsMeasure.ProSubscriptionInitialAnnualPaymentsIncome], - ]), - ], - [ - PaymentType.Renewal, - new Map([ - [SubscriptionBillingFrequency.Monthly, StatisticsMeasure.ProSubscriptionRenewingMonthlyPaymentsIncome], - [SubscriptionBillingFrequency.Annual, StatisticsMeasure.ProSubscriptionRenewingAnnualPaymentsIncome], - ]), - ], - ]), - ], - ]) - - constructor( - @inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async handle(event: PaymentSuccessEvent): Promise { - const user = await this.userRepository.findOneByEmail(event.payload.userEmail) - if (user === null) { - return - } - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.PaymentSuccess], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - - const statisticMeasures = [StatisticsMeasure.Income] - - const detailedMeasure = this.DETAILED_MEASURES.get(event.payload.subscriptionName as SubscriptionName) - ?.get(event.payload.paymentType as PaymentType) - ?.get(event.payload.billingFrequency as SubscriptionBillingFrequency) - if (detailedMeasure !== undefined) { - statisticMeasures.push(detailedMeasure) - } else { - this.logger.warn( - `Could not find detailed measure for: subscription - ${event.payload.subscriptionName}, payment type - ${event.payload.paymentType}, billing frequency - ${event.payload.billingFrequency}`, - ) - } - - for (const measure of statisticMeasures) { - await this.statisticsStore.incrementMeasure(measure, event.payload.amount, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - } - } -} diff --git a/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.spec.ts index 495ff7450..2ed72f3ca 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.spec.ts @@ -8,54 +8,19 @@ import * as dayjs from 'dayjs' import { SubscriptionCancelledEventHandler } from './SubscriptionCancelledEventHandler' import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' -import { AnalyticsStoreInterface, Period, StatisticsMeasure, StatisticsStoreInterface } from '@standardnotes/analytics' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' -import { User } from '../User/User' -import { UserSubscription } from '../Subscription/UserSubscription' describe('SubscriptionCancelledEventHandler', () => { let userSubscriptionRepository: UserSubscriptionRepositoryInterface let offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface let event: SubscriptionCancelledEvent - let userRepository: UserRepositoryInterface - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - let statisticsStore: StatisticsStoreInterface let timestamp: number const createHandler = () => - new SubscriptionCancelledEventHandler( - userSubscriptionRepository, - offlineUserSubscriptionRepository, - userRepository, - getUserAnalyticsId, - analyticsStore, - statisticsStore, - ) + new SubscriptionCancelledEventHandler(userSubscriptionRepository, offlineUserSubscriptionRepository) beforeEach(() => { - const user = { uuid: '1-2-3' } as jest.Mocked - - userRepository = {} as jest.Mocked - userRepository.findOneByEmail = jest.fn().mockReturnValue(user) - - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - - statisticsStore = {} as jest.Mocked - statisticsStore.incrementMeasure = jest.fn() - - const userSubscription = { - createdAt: 1642395451515000, - } as jest.Mocked - userSubscriptionRepository = {} as jest.Mocked userSubscriptionRepository.updateCancelled = jest.fn() - userSubscriptionRepository.findBySubscriptionId = jest.fn().mockReturnValue([userSubscription]) offlineUserSubscriptionRepository = {} as jest.Mocked offlineUserSubscriptionRepository.updateCancelled = jest.fn() @@ -83,35 +48,6 @@ describe('SubscriptionCancelledEventHandler', () => { await createHandler().handle(event) expect(userSubscriptionRepository.updateCancelled).toHaveBeenCalledWith(1, true, 1642395451516000) - expect(analyticsStore.markActivity).toHaveBeenCalled() - expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith(StatisticsMeasure.SubscriptionLength, 1000, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - }) - - it('should not track statistics for subscriptions that are in a legacy 5 year plan', async () => { - event.payload.timestamp = 1642395451516000 - - const userSubscription = { - createdAt: 1642395451515000, - endsAt: 1642395451515000 + 126_230_400_000_001, - } as jest.Mocked - userSubscriptionRepository.findBySubscriptionId = jest.fn().mockReturnValue([userSubscription]) - - await createHandler().handle(event) - - expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled() - }) - - it('should update subscription cancelled - user not found', async () => { - userRepository.findOneByEmail = jest.fn().mockReturnValue(null) - - await createHandler().handle(event) - - expect(userSubscriptionRepository.updateCancelled).toHaveBeenCalledWith(1, true, timestamp) - expect(analyticsStore.markActivity).not.toHaveBeenCalled() }) it('should update offline subscription cancelled', async () => { diff --git a/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.ts index 9953efc90..be3490ac5 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionCancelledEventHandler.ts @@ -1,19 +1,9 @@ import { DomainEventHandlerInterface, SubscriptionCancelledEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' -import { - AnalyticsActivity, - AnalyticsStoreInterface, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' import TYPES from '../../Bootstrap/Types' import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { UserSubscription } from '../Subscription/UserSubscription' @injectable() export class SubscriptionCancelledEventHandler implements DomainEventHandlerInterface { @@ -21,24 +11,9 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte @inject(TYPES.UserSubscriptionRepository) private userSubscriptionRepository: UserSubscriptionRepositoryInterface, @inject(TYPES.OfflineUserSubscriptionRepository) private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface, - @inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, ) {} + async handle(event: SubscriptionCancelledEvent): Promise { - const user = await this.userRepository.findOneByEmail(event.payload.userEmail) - if (user !== null) { - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.SubscriptionCancelled], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - } - - await this.trackSubscriptionStatistics(event) - if (event.payload.offline) { await this.updateOfflineSubscriptionCancelled(event.payload.subscriptionId, event.payload.timestamp) @@ -55,39 +30,4 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte private async updateOfflineSubscriptionCancelled(subscriptionId: number, timestamp: number): Promise { await this.offlineUserSubscriptionRepository.updateCancelled(subscriptionId, true, timestamp) } - - private async trackSubscriptionStatistics(event: SubscriptionCancelledEvent) { - const subscriptions = await this.userSubscriptionRepository.findBySubscriptionId(event.payload.subscriptionId) - if (subscriptions.length !== 0) { - const lastSubscription = subscriptions.shift() as UserSubscription - if (this.isLegacy5yearSubscriptionPlan(lastSubscription)) { - return - } - - const subscriptionLength = event.payload.timestamp - lastSubscription.createdAt - await this.statisticsStore.incrementMeasure(StatisticsMeasure.SubscriptionLength, subscriptionLength, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - - const lastPurchaseTime = lastSubscription.renewedAt ?? lastSubscription.updatedAt - const remainingSubscriptionTime = lastSubscription.endsAt - event.payload.timestamp - const totalSubscriptionTime = lastSubscription.endsAt - lastPurchaseTime - - const remainingSubscriptionPercentage = Math.floor((remainingSubscriptionTime / totalSubscriptionTime) * 100) - - await this.statisticsStore.incrementMeasure( - StatisticsMeasure.RemainingSubscriptionTimePercentage, - remainingSubscriptionPercentage, - [Period.Today, Period.ThisWeek, Period.ThisMonth], - ) - } - } - - private isLegacy5yearSubscriptionPlan(subscription: UserSubscription) { - const fourYearsInMicroseconds = 126_230_400_000_000 - - return subscription.endsAt - subscription.createdAt > fourYearsInMicroseconds - } } diff --git a/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.spec.ts index 52d9289d9..f81c2d3d8 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.spec.ts @@ -13,8 +13,6 @@ import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscri import { RoleServiceInterface } from '../Role/RoleServiceInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' import { UserSubscription } from '../Subscription/UserSubscription' -import { AnalyticsStoreInterface, StatisticsStoreInterface } from '@standardnotes/analytics' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' describe('SubscriptionExpiredEventHandler', () => { let userRepository: UserRepositoryInterface @@ -25,9 +23,6 @@ describe('SubscriptionExpiredEventHandler', () => { let user: User let event: SubscriptionExpiredEvent let timestamp: number - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - let statisticsStore: StatisticsStoreInterface const createHandler = () => new SubscriptionExpiredEventHandler( @@ -35,9 +30,6 @@ describe('SubscriptionExpiredEventHandler', () => { userSubscriptionRepository, offlineUserSubscriptionRepository, roleService, - getUserAnalyticsId, - analyticsStore, - statisticsStore, logger, ) @@ -82,15 +74,6 @@ describe('SubscriptionExpiredEventHandler', () => { totalActiveSubscriptionsCount: 123, } - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - - statisticsStore = {} as jest.Mocked - statisticsStore.setMeasure = jest.fn() - logger = {} as jest.Mocked logger.info = jest.fn() logger.warn = jest.fn() diff --git a/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.ts index 0be74234c..6531f5d3a 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionExpiredEventHandler.ts @@ -8,14 +8,6 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' -import { - AnalyticsStoreInterface, - AnalyticsActivity, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' @injectable() export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterface { @@ -25,9 +17,6 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf @inject(TYPES.OfflineUserSubscriptionRepository) private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface, @inject(TYPES.RoleService) private roleService: RoleServiceInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, @inject(TYPES.Logger) private logger: Logger, ) {} @@ -47,21 +36,6 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf await this.updateSubscriptionEndsAt(event.payload.subscriptionId, event.payload.timestamp) await this.removeRoleFromSubscriptionUsers(event.payload.subscriptionId, event.payload.subscriptionName) - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity( - [AnalyticsActivity.SubscriptionExpired, AnalyticsActivity.ExistingCustomersChurn], - analyticsId, - [Period.Today, Period.ThisWeek, Period.ThisMonth], - ) - - const activeSubscriptions = await this.userSubscriptionRepository.countActiveSubscriptions() - await this.statisticsStore.setMeasure(StatisticsMeasure.TotalCustomers, activeSubscriptions, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - Period.ThisYear, - ]) } private async removeRoleFromSubscriptionUsers( diff --git a/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.spec.ts index 6839f334e..5e70372a1 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.spec.ts @@ -16,10 +16,6 @@ import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/Offl import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription' import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface' import { UserSubscriptionType } from '../Subscription/UserSubscriptionType' -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 @@ -33,11 +29,7 @@ describe('SubscriptionPurchasedEventHandler', () => { let event: SubscriptionPurchasedEvent let subscriptionExpiresAt: number let subscriptionSettingService: SubscriptionSettingServiceInterface - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface let timestamp: number - let statisticsStore: StatisticsStoreInterface - let timer: TimerInterface const createHandler = () => new SubscriptionPurchasedEventHandler( @@ -46,10 +38,6 @@ describe('SubscriptionPurchasedEventHandler', () => { offlineUserSubscriptionRepository, roleService, subscriptionSettingService, - getUserAnalyticsId, - analyticsStore, - statisticsStore, - timer, logger, ) @@ -71,13 +59,6 @@ describe('SubscriptionPurchasedEventHandler', () => { userRepository.findOneByEmail = jest.fn().mockReturnValue(user) userRepository.save = jest.fn().mockReturnValue(user) - statisticsStore = {} as jest.Mocked - statisticsStore.incrementMeasure = jest.fn() - statisticsStore.setMeasure = jest.fn() - - timer = {} as jest.Mocked - timer.convertDateToMicroseconds = jest.fn().mockReturnValue(1) - userSubscriptionRepository = {} as jest.Mocked userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(0) userSubscriptionRepository.countActiveSubscriptions = jest.fn().mockReturnValue(13) @@ -114,13 +95,6 @@ describe('SubscriptionPurchasedEventHandler', () => { subscriptionSettingService = {} as jest.Mocked subscriptionSettingService.applyDefaultSubscriptionSettingsForSubscription = jest.fn() - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - analyticsStore.unmarkActivity = jest.fn() - logger = {} as jest.Mocked logger.info = jest.fn() logger.warn = jest.fn() @@ -166,37 +140,6 @@ 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 () => { - const analyticsEntity = { id: 3 } as jest.Mocked - - user = { - uuid: '123', - email: 'test@test.com', - roles: Promise.resolve([ - { - name: RoleName.CoreUser, - }, - ]), - analyticsEntity: Promise.resolve(analyticsEntity), - } as jest.Mocked - userRepository.findOneByEmail = jest.fn().mockReturnValue(user) - - event.payload.discountCode = 'limited-10' - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalledWith(['limited-discount-offer-purchased'], 3, [Period.Today]) }) it('should create an offline subscription', async () => { diff --git a/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts index 11d1c41a1..21af90117 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionPurchasedEventHandler.ts @@ -13,15 +13,6 @@ import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' import { UserSubscriptionType } from '../Subscription/UserSubscriptionType' import { SubscriptionSettingServiceInterface } from '../Setting/SubscriptionSettingServiceInterface' -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 { @@ -32,10 +23,6 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface, @inject(TYPES.RoleService) private roleService: RoleServiceInterface, @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, ) {} @@ -61,8 +48,6 @@ 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, @@ -78,48 +63,6 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte event.payload.subscriptionName, user.uuid, ) - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.SubscriptionPurchased], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - await this.analyticsStore.unmarkActivity( - [AnalyticsActivity.ExistingCustomersChurn, AnalyticsActivity.NewCustomersChurn], - analyticsId, - [Period.Today, Period.ThisWeek, Period.ThisMonth], - ) - - const limitedDiscountPurchased = ['limited-10', 'limited-20', 'exit-20'].includes( - event.payload.discountCode as string, - ) - if (limitedDiscountPurchased) { - await this.analyticsStore.markActivity([AnalyticsActivity.LimitedDiscountOfferPurchased], analyticsId, [ - 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], - ) - await this.statisticsStore.incrementMeasure(StatisticsMeasure.NewCustomers, 1, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - Period.ThisYear, - ]) - const activeSubscriptions = await this.userSubscriptionRepository.countActiveSubscriptions() - await this.statisticsStore.setMeasure(StatisticsMeasure.TotalCustomers, activeSubscriptions, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - Period.ThisYear, - ]) - } } private async addUserRole(user: User, subscriptionName: SubscriptionName): Promise { diff --git a/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.spec.ts deleted file mode 100644 index ea6015178..000000000 --- a/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.spec.ts +++ /dev/null @@ -1,78 +0,0 @@ -import 'reflect-metadata' - -import { RoleName, SubscriptionName } from '@standardnotes/common' -import { SubscriptionReactivatedEvent } from '@standardnotes/domain-events' -import { Logger } from 'winston' - -import { User } from '../User/User' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' -import { SubscriptionReactivatedEventHandler } from './SubscriptionReactivatedEventHandler' -import { AnalyticsStoreInterface, Period } from '@standardnotes/analytics' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' - -describe('SubscriptionReactivatedEventHandler', () => { - let userRepository: UserRepositoryInterface - let logger: Logger - let user: User - let event: SubscriptionReactivatedEvent - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - - const createHandler = () => - new SubscriptionReactivatedEventHandler(userRepository, analyticsStore, getUserAnalyticsId, logger) - - beforeEach(() => { - user = { - uuid: '123', - email: 'test@test.com', - roles: Promise.resolve([ - { - name: RoleName.ProUser, - }, - ]), - } as jest.Mocked - - userRepository = {} as jest.Mocked - userRepository.findOneByEmail = jest.fn().mockReturnValue(user) - userRepository.save = jest.fn().mockReturnValue(user) - - event = {} as jest.Mocked - event.createdAt = new Date(1) - event.payload = { - previousSubscriptionId: 1, - currentSubscriptionId: 2, - userEmail: 'test@test.com', - subscriptionName: SubscriptionName.PlusPlan, - subscriptionExpiresAt: 5, - discountCode: 'exit-20', - } - - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - - logger = {} as jest.Mocked - logger.info = jest.fn() - logger.warn = jest.fn() - }) - - it('should mark subscription reactivated activity for analytics', async () => { - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalledWith(['subscription-reactivated'], 3, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - }) - - it('should not do anything if no user is found for specified email', async () => { - userRepository.findOneByEmail = jest.fn().mockReturnValue(null) - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).not.toHaveBeenCalled() - }) -}) diff --git a/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.ts deleted file mode 100644 index aa2dd7cc8..000000000 --- a/packages/auth/src/Domain/Handler/SubscriptionReactivatedEventHandler.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' -import { DomainEventHandlerInterface, SubscriptionReactivatedEvent } from '@standardnotes/domain-events' -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' - -import TYPES from '../../Bootstrap/Types' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { UserRepositoryInterface } from '../User/UserRepositoryInterface' - -@injectable() -export class SubscriptionReactivatedEventHandler implements DomainEventHandlerInterface { - constructor( - @inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async handle(event: SubscriptionReactivatedEvent): Promise { - const user = await this.userRepository.findOneByEmail(event.payload.userEmail) - - if (user === null) { - this.logger.warn(`Could not find user with email: ${event.payload.userEmail}`) - return - } - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.SubscriptionReactivated], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - } -} diff --git a/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.spec.ts index 7d8f1e3f9..084c59e26 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.spec.ts @@ -13,8 +13,6 @@ import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscri import { RoleServiceInterface } from '../Role/RoleServiceInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' import { UserSubscription } from '../Subscription/UserSubscription' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { AnalyticsActivity, AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics' describe('SubscriptionRefundedEventHandler', () => { let userRepository: UserRepositoryInterface @@ -25,9 +23,6 @@ describe('SubscriptionRefundedEventHandler', () => { let user: User let event: SubscriptionRefundedEvent let timestamp: number - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface - let statisticsStore: StatisticsStoreInterface const createHandler = () => new SubscriptionRefundedEventHandler( @@ -35,9 +30,6 @@ describe('SubscriptionRefundedEventHandler', () => { userSubscriptionRepository, offlineUserSubscriptionRepository, roleService, - getUserAnalyticsId, - analyticsStore, - statisticsStore, logger, ) @@ -84,16 +76,6 @@ describe('SubscriptionRefundedEventHandler', () => { totalActiveSubscriptionsCount: 1, } - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - analyticsStore.wasActivityDone = jest.fn().mockReturnValue(true) - - statisticsStore = {} as jest.Mocked - statisticsStore.setMeasure = jest.fn() - logger = {} as jest.Mocked logger.info = jest.fn() logger.warn = jest.fn() @@ -129,33 +111,4 @@ describe('SubscriptionRefundedEventHandler', () => { expect(roleService.removeUserRole).not.toHaveBeenCalled() expect(userSubscriptionRepository.updateEndsAt).not.toHaveBeenCalled() }) - - it('should mark churn for new customer', async () => { - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalledWith([AnalyticsActivity.NewCustomersChurn], 3, [ - Period.ThisMonth, - ]) - }) - - it('should mark churn for existing customer', async () => { - userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(3) - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).toHaveBeenCalledWith([AnalyticsActivity.ExistingCustomersChurn], 3, [ - Period.ThisMonth, - ]) - }) - - it('should not mark churn if customer did not purchase subscription in defined analytic periods', async () => { - userSubscriptionRepository.countByUserUuid = jest.fn().mockReturnValue(3) - analyticsStore.wasActivityDone = jest.fn().mockReturnValue(false) - - await createHandler().handle(event) - - expect(analyticsStore.markActivity).not.toHaveBeenCalledWith([AnalyticsActivity.ExistingCustomersChurn], 3, [ - Period.ThisMonth, - ]) - }) }) diff --git a/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.ts index 4c93cdb3b..952a784ce 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionRefundedEventHandler.ts @@ -1,4 +1,4 @@ -import { SubscriptionName, Uuid } from '@standardnotes/common' +import { SubscriptionName } from '@standardnotes/common' import { DomainEventHandlerInterface, SubscriptionRefundedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' import { Logger } from 'winston' @@ -8,14 +8,6 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface' import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface' -import { - AnalyticsActivity, - AnalyticsStoreInterface, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' @injectable() export class SubscriptionRefundedEventHandler implements DomainEventHandlerInterface { @@ -25,9 +17,6 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter @inject(TYPES.OfflineUserSubscriptionRepository) private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface, @inject(TYPES.RoleService) private roleService: RoleServiceInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, @inject(TYPES.Logger) private logger: Logger, ) {} @@ -47,15 +36,6 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter await this.updateSubscriptionEndsAt(event.payload.subscriptionId, event.payload.timestamp) await this.removeRoleFromSubscriptionUsers(event.payload.subscriptionId, event.payload.subscriptionName) - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.SubscriptionRefunded], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - - await this.markChurnActivity(analyticsId, user.uuid) } private async removeRoleFromSubscriptionUsers( @@ -75,30 +55,4 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter private async updateOfflineSubscriptionEndsAt(subscriptionId: number, timestamp: number): Promise { await this.offlineUserSubscriptionRepository.updateEndsAt(subscriptionId, timestamp, timestamp) } - - private async markChurnActivity(analyticsId: number, userUuid: Uuid): Promise { - const existingSubscriptionsCount = await this.userSubscriptionRepository.countByUserUuid(userUuid) - - const churnActivity = - existingSubscriptionsCount > 1 ? AnalyticsActivity.ExistingCustomersChurn : AnalyticsActivity.NewCustomersChurn - - for (const period of [Period.ThisMonth, Period.ThisWeek, Period.Today]) { - const customerPurchasedInPeriod = await this.analyticsStore.wasActivityDone( - AnalyticsActivity.SubscriptionPurchased, - analyticsId, - period, - ) - if (customerPurchasedInPeriod) { - await this.analyticsStore.markActivity([churnActivity], analyticsId, [period]) - } - } - - const activeSubscriptions = await this.userSubscriptionRepository.countActiveSubscriptions() - await this.statisticsStore.setMeasure(StatisticsMeasure.TotalCustomers, activeSubscriptions, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - Period.ThisYear, - ]) - } } diff --git a/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts b/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts index c24076179..3a7300b9f 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.spec.ts @@ -13,8 +13,6 @@ import { UserSubscription } from '../Subscription/UserSubscription' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription' import { RoleServiceInterface } from '../Role/RoleServiceInterface' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { AnalyticsStoreInterface } from '@standardnotes/analytics' describe('SubscriptionRenewedEventHandler', () => { let userRepository: UserRepositoryInterface @@ -28,8 +26,6 @@ describe('SubscriptionRenewedEventHandler', () => { let event: SubscriptionRenewedEvent let subscriptionExpiresAt: number let timestamp: number - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface const createHandler = () => new SubscriptionRenewedEventHandler( @@ -37,8 +33,6 @@ describe('SubscriptionRenewedEventHandler', () => { userSubscriptionRepository, offlineUserSubscriptionRepository, roleService, - getUserAnalyticsId, - analyticsStore, logger, ) @@ -89,13 +83,6 @@ describe('SubscriptionRenewedEventHandler', () => { 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() - logger = {} as jest.Mocked logger.warn = jest.fn() }) diff --git a/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.ts b/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.ts index b54ec4cf2..1e05208cb 100644 --- a/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.ts +++ b/packages/auth/src/Domain/Handler/SubscriptionRenewedEventHandler.ts @@ -1,6 +1,5 @@ import { DomainEventHandlerInterface, SubscriptionRenewedEvent } from '@standardnotes/domain-events' import { inject, injectable } from 'inversify' -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import TYPES from '../../Bootstrap/Types' import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface' @@ -10,7 +9,6 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { Logger } from 'winston' import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' @injectable() export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterface { @@ -20,8 +18,6 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf @inject(TYPES.OfflineUserSubscriptionRepository) private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface, @inject(TYPES.RoleService) private roleService: RoleServiceInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.Logger) private logger: Logger, ) {} @@ -63,18 +59,6 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf } await this.addRoleToSubscriptionUsers(event.payload.subscriptionId, event.payload.subscriptionName) - - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - 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], - ) } private async addRoleToSubscriptionUsers(subscriptionId: number, subscriptionName: SubscriptionName): Promise { diff --git a/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.spec.ts b/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.spec.ts index 44629e96f..e8439f063 100644 --- a/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.spec.ts +++ b/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.spec.ts @@ -4,8 +4,6 @@ import { Logger } from 'winston' import { UserRegisteredEventHandler } from './UserRegisteredEventHandler' import { AxiosInstance } from 'axios' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' -import { AnalyticsStoreInterface } from '@standardnotes/analytics' import { ProtocolVersion } from '@standardnotes/common' describe('UserRegisteredEventHandler', () => { @@ -13,19 +11,10 @@ describe('UserRegisteredEventHandler', () => { const userServerRegistrationUrl = 'https://user-server/registration' const userServerAuthKey = 'auth-key' let event: UserRegisteredEvent - let getUserAnalyticsId: GetUserAnalyticsId - let analyticsStore: AnalyticsStoreInterface let logger: Logger const createHandler = () => - new UserRegisteredEventHandler( - httpClient, - userServerRegistrationUrl, - userServerAuthKey, - getUserAnalyticsId, - analyticsStore, - logger, - ) + new UserRegisteredEventHandler(httpClient, userServerRegistrationUrl, userServerAuthKey, logger) beforeEach(() => { httpClient = {} as jest.Mocked @@ -39,12 +28,6 @@ describe('UserRegisteredEventHandler', () => { protocolVersion: ProtocolVersion.V004, } - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 3 }) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() - logger = {} as jest.Mocked logger.debug = jest.fn() }) @@ -71,14 +54,7 @@ describe('UserRegisteredEventHandler', () => { }) it('should not send a request to the user management server about a registration if url is not defined', async () => { - const handler = new UserRegisteredEventHandler( - httpClient, - '', - userServerAuthKey, - getUserAnalyticsId, - analyticsStore, - logger, - ) + const handler = new UserRegisteredEventHandler(httpClient, '', userServerAuthKey, logger) await handler.handle(event) expect(httpClient.request).not.toHaveBeenCalled() diff --git a/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.ts b/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.ts index 6727cd47d..c4ba7e9fe 100644 --- a/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.ts +++ b/packages/auth/src/Domain/Handler/UserRegisteredEventHandler.ts @@ -1,11 +1,9 @@ -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { DomainEventHandlerInterface, UserRegisteredEvent } from '@standardnotes/domain-events' import { AxiosInstance } from 'axios' import { inject, injectable } from 'inversify' import { Logger } from 'winston' import TYPES from '../../Bootstrap/Types' -import { GetUserAnalyticsId } from '../UseCase/GetUserAnalyticsId/GetUserAnalyticsId' @injectable() export class UserRegisteredEventHandler implements DomainEventHandlerInterface { @@ -13,8 +11,6 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface { @inject(TYPES.HTTPClient) private httpClient: AxiosInstance, @inject(TYPES.USER_SERVER_REGISTRATION_URL) private userServerRegistrationUrl: string, @inject(TYPES.USER_SERVER_AUTH_KEY) private userServerAuthKey: string, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, @inject(TYPES.Logger) private logger: Logger, ) {} @@ -24,13 +20,6 @@ export class UserRegisteredEventHandler implements DomainEventHandlerInterface { return } - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: event.payload.userUuid }) - await this.analyticsStore.markActivity([AnalyticsActivity.Register], analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - await this.httpClient.request({ method: 'POST', url: this.userServerRegistrationUrl, diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts index 4dcf718b0..4f74f6d69 100644 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts +++ b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts @@ -6,7 +6,6 @@ import { Session } from '../../Session/Session' import { User } from '../../User/User' import { Role } from '../../Role/Role' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' -import { GetUserAnalyticsId } from '../GetUserAnalyticsId/GetUserAnalyticsId' import { CreateCrossServiceToken } from './CreateCrossServiceToken' @@ -15,7 +14,6 @@ describe('CreateCrossServiceToken', () => { let sessionProjector: ProjectorInterface let roleProjector: ProjectorInterface let tokenEncoder: TokenEncoderInterface - let getUserAnalyticsId: GetUserAnalyticsId let userRepository: UserRepositoryInterface const jwtTTL = 60 @@ -23,17 +21,8 @@ describe('CreateCrossServiceToken', () => { let user: User let role: Role - const createUseCase = (analyticsEnabled = true) => - new CreateCrossServiceToken( - userProjector, - sessionProjector, - roleProjector, - tokenEncoder, - getUserAnalyticsId, - userRepository, - analyticsEnabled, - jwtTTL, - ) + const createUseCase = () => + new CreateCrossServiceToken(userProjector, sessionProjector, roleProjector, tokenEncoder, userRepository, jwtTTL) beforeEach(() => { session = {} as jest.Mocked @@ -54,9 +43,6 @@ describe('CreateCrossServiceToken', () => { tokenEncoder = {} as jest.Mocked> tokenEncoder.encodeExpirableToken = jest.fn().mockReturnValue('foobar') - getUserAnalyticsId = {} as jest.Mocked - getUserAnalyticsId.execute = jest.fn().mockReturnValue({ analyticsId: 123 }) - userRepository = {} as jest.Mocked userRepository.findOneByUuid = jest.fn().mockReturnValue(user) }) @@ -67,32 +53,6 @@ describe('CreateCrossServiceToken', () => { session, }) - expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( - { - analyticsId: 123, - roles: [ - { - name: 'role1', - uuid: '1-3-4', - }, - ], - session: { - test: 'test', - }, - user: { - bar: 'baz', - }, - }, - 60, - ) - }) - - it('should create a cross service token for user - analytics disabled', async () => { - await createUseCase(false).execute({ - user, - session, - }) - expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( { roles: [ @@ -119,7 +79,6 @@ describe('CreateCrossServiceToken', () => { expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( { - analyticsId: 123, roles: [ { name: 'role1', @@ -141,7 +100,6 @@ describe('CreateCrossServiceToken', () => { expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( { - analyticsId: 123, roles: [ { name: 'role1', diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts index bbda080c5..b0a37a902 100644 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts +++ b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts @@ -8,7 +8,6 @@ import { Role } from '../../Role/Role' import { Session } from '../../Session/Session' import { User } from '../../User/User' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' -import { GetUserAnalyticsId } from '../GetUserAnalyticsId/GetUserAnalyticsId' import { UseCaseInterface } from '../UseCaseInterface' import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO' import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse' @@ -20,9 +19,7 @@ export class CreateCrossServiceToken implements UseCaseInterface { @inject(TYPES.SessionProjector) private sessionProjector: ProjectorInterface, @inject(TYPES.RoleProjector) private roleProjector: ProjectorInterface, @inject(TYPES.CrossServiceTokenEncoder) private tokenEncoder: TokenEncoderInterface, - @inject(TYPES.GetUserAnalyticsId) private getUserAnalyticsId: GetUserAnalyticsId, @inject(TYPES.UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.ANALYTICS_ENABLED) private analyticsEnabled: boolean, @inject(TYPES.AUTH_JWT_TTL) private jwtTTL: number, ) {} @@ -43,11 +40,6 @@ export class CreateCrossServiceToken implements UseCaseInterface { roles: this.projectRoles(roles), } - if (this.analyticsEnabled) { - const { analyticsId } = await this.getUserAnalyticsId.execute({ userUuid: user.uuid }) - authTokenData.analyticsId = analyticsId - } - if (dto.session !== undefined) { authTokenData.session = this.projectSession(dto.session) } diff --git a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts b/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts deleted file mode 100644 index c4e9c3b74..000000000 --- a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import 'reflect-metadata' - -import { AnalyticsEntity } from '../../Analytics/AnalyticsEntity' -import { AnalyticsEntityRepositoryInterface } from '../../Analytics/AnalyticsEntityRepositoryInterface' - -import { GetUserAnalyticsId } from './GetUserAnalyticsId' - -describe('GetUserAnalyticsId', () => { - let analyticsEntityRepository: AnalyticsEntityRepositoryInterface - let analyticsEntity: AnalyticsEntity - - const createUseCase = () => new GetUserAnalyticsId(analyticsEntityRepository) - - beforeEach(() => { - analyticsEntity = { id: 123 } as jest.Mocked - - analyticsEntityRepository = {} as jest.Mocked - analyticsEntityRepository.findOneByUserUuid = jest.fn().mockReturnValue(analyticsEntity) - }) - - it('should return analytics id for a user', async () => { - expect(await createUseCase().execute({ userUuid: '1-2-3' })).toEqual({ analyticsId: 123 }) - }) - - it('should throw error if user is missing analytics entity', async () => { - analyticsEntityRepository.findOneByUserUuid = jest.fn().mockReturnValue(null) - let error = null - - try { - await createUseCase().execute({ userUuid: '1-2-3' }) - } catch (caughtError) { - error = caughtError - } - - expect(error).not.toBeNull() - }) -}) diff --git a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts b/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts deleted file mode 100644 index 5937e1fd5..000000000 --- a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsId.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { inject, injectable } from 'inversify' -import TYPES from '../../../Bootstrap/Types' -import { AnalyticsEntityRepositoryInterface } from '../../Analytics/AnalyticsEntityRepositoryInterface' -import { UseCaseInterface } from '../UseCaseInterface' -import { GetUserAnalyticsIdDTO } from './GetUserAnalyticsIdDTO' -import { GetUserAnalyticsIdResponse } from './GetUserAnalyticsIdResponse' - -@injectable() -export class GetUserAnalyticsId implements UseCaseInterface { - constructor( - @inject(TYPES.AnalyticsEntityRepository) private analyticsEntityRepository: AnalyticsEntityRepositoryInterface, - ) {} - - async execute(dto: GetUserAnalyticsIdDTO): Promise { - const analyticsEntity = await this.analyticsEntityRepository.findOneByUserUuid(dto.userUuid) - - if (analyticsEntity === null) { - throw new Error(`Could not find analytics entity for user ${dto.userUuid}`) - } - - return { - analyticsId: analyticsEntity.id, - } - } -} diff --git a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdDTO.ts b/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdDTO.ts deleted file mode 100644 index 19c054e8f..000000000 --- a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdDTO.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Uuid } from '@standardnotes/common' - -export type GetUserAnalyticsIdDTO = { - userUuid: Uuid -} diff --git a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts b/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts deleted file mode 100644 index 4eedb6f23..000000000 --- a/packages/auth/src/Domain/UseCase/GetUserAnalyticsId/GetUserAnalyticsIdResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type GetUserAnalyticsIdResponse = { - analyticsId: number -} diff --git a/packages/auth/src/Domain/UseCase/Register.spec.ts b/packages/auth/src/Domain/UseCase/Register.spec.ts index 731f2361b..21296336a 100644 --- a/packages/auth/src/Domain/UseCase/Register.spec.ts +++ b/packages/auth/src/Domain/UseCase/Register.spec.ts @@ -9,7 +9,6 @@ import { User } from '../User/User' import { UserRepositoryInterface } from '../User/UserRepositoryInterface' import { Register } from './Register' import { SettingServiceInterface } from '../Setting/SettingServiceInterface' -import { AnalyticsEntityRepositoryInterface } from '../Analytics/AnalyticsEntityRepositoryInterface' import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115' describe('Register', () => { @@ -20,19 +19,9 @@ describe('Register', () => { let user: User let crypter: CrypterInterface let timer: TimerInterface - let analyticsEntityRepository: AnalyticsEntityRepositoryInterface const createUseCase = () => - new Register( - userRepository, - roleRepository, - authResponseFactory, - crypter, - false, - settingService, - timer, - analyticsEntityRepository, - ) + new Register(userRepository, roleRepository, authResponseFactory, crypter, false, settingService, timer) beforeEach(() => { userRepository = {} as jest.Mocked @@ -55,9 +44,6 @@ describe('Register', () => { timer = {} as jest.Mocked timer.getUTCDate = jest.fn().mockReturnValue(new Date(1)) - - analyticsEntityRepository = {} as jest.Mocked - analyticsEntityRepository.save = jest.fn() }) it('should register a new user', async () => { @@ -91,8 +77,6 @@ describe('Register', () => { }) expect(settingService.applyDefaultSettingsUponRegistration).toHaveBeenCalled() - - expect(analyticsEntityRepository.save).toHaveBeenCalled() }) it('should register a new user with default role', async () => { @@ -187,7 +171,6 @@ describe('Register', () => { true, settingService, timer, - analyticsEntityRepository, ).execute({ email: 'test@test.te', password: 'asdzxc', diff --git a/packages/auth/src/Domain/UseCase/Register.ts b/packages/auth/src/Domain/UseCase/Register.ts index 2416a7650..6588dbca6 100644 --- a/packages/auth/src/Domain/UseCase/Register.ts +++ b/packages/auth/src/Domain/UseCase/Register.ts @@ -14,8 +14,6 @@ import { RoleRepositoryInterface } from '../Role/RoleRepositoryInterface' import { CrypterInterface } from '../Encryption/CrypterInterface' import { TimerInterface } from '@standardnotes/time' import { SettingServiceInterface } from '../Setting/SettingServiceInterface' -import { AnalyticsEntityRepositoryInterface } from '../Analytics/AnalyticsEntityRepositoryInterface' -import { AnalyticsEntity } from '../Analytics/AnalyticsEntity' import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115' import { AuthResponse20200115 } from '../Auth/AuthResponse20200115' @@ -29,7 +27,6 @@ export class Register implements UseCaseInterface { @inject(TYPES.DISABLE_USER_REGISTRATION) private disableUserRegistration: boolean, @inject(TYPES.SettingService) private settingService: SettingServiceInterface, @inject(TYPES.Timer) private timer: TimerInterface, - @inject(TYPES.AnalyticsEntityRepository) private analyticsEntityRepository: AnalyticsEntityRepositoryInterface, ) {} async execute(dto: RegisterDTO): Promise { @@ -77,10 +74,6 @@ export class Register implements UseCaseInterface { await this.settingService.applyDefaultSettingsUponRegistration(user) - const analyticsEntity = new AnalyticsEntity() - analyticsEntity.user = Promise.resolve(user) - await this.analyticsEntityRepository.save(analyticsEntity) - return { success: true, authResponse: (await this.authResponseFactory20200115.createResponse({ diff --git a/packages/auth/src/Domain/User/User.ts b/packages/auth/src/Domain/User/User.ts index 4c6bcf7b7..15e3326fd 100644 --- a/packages/auth/src/Domain/User/User.ts +++ b/packages/auth/src/Domain/User/User.ts @@ -1,9 +1,8 @@ -import { Column, Entity, Index, JoinTable, ManyToMany, OneToMany, OneToOne, PrimaryGeneratedColumn } from 'typeorm' +import { Column, Entity, Index, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn } from 'typeorm' import { RevokedSession } from '../Session/RevokedSession' import { Role } from '../Role/Role' import { Setting } from '../Setting/Setting' import { UserSubscription } from '../Subscription/UserSubscription' -import { AnalyticsEntity } from '../Analytics/AnalyticsEntity' import { ProtocolVersion } from '@standardnotes/common' @Entity({ name: 'users' }) @@ -182,16 +181,6 @@ export class User { ) declare subscriptions: Promise - @OneToOne( - /* istanbul ignore next */ - () => AnalyticsEntity, - /* istanbul ignore next */ - (analyticsEntity) => analyticsEntity.user, - /* istanbul ignore next */ - { lazy: true, eager: false }, - ) - declare analyticsEntity: Promise - supportsSessions(): boolean { return parseInt(this.version) >= parseInt(ProtocolVersion.V004) } diff --git a/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.spec.ts b/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.spec.ts deleted file mode 100644 index f345f4a93..000000000 --- a/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -import 'reflect-metadata' - -import { Repository, SelectQueryBuilder } from 'typeorm' - -import { AnalyticsEntity } from '../../Domain/Analytics/AnalyticsEntity' - -import { MySQLAnalyticsEntityRepository } from './MySQLAnalyticsEntityRepository' - -describe('MySQLAnalyticsEntityRepository', () => { - let ormRepository: Repository - let analyticsEntity: AnalyticsEntity - let queryBuilder: SelectQueryBuilder - - const createRepository = () => new MySQLAnalyticsEntityRepository(ormRepository) - - beforeEach(() => { - analyticsEntity = {} as jest.Mocked - - queryBuilder = {} as jest.Mocked> - - ormRepository = {} as jest.Mocked> - ormRepository.save = jest.fn() - ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => queryBuilder) - }) - - it('should save', async () => { - await createRepository().save(analyticsEntity) - - expect(ormRepository.save).toHaveBeenCalledWith(analyticsEntity) - }) - - it('should find one by user uuid', async () => { - queryBuilder.where = jest.fn().mockReturnThis() - queryBuilder.getOne = jest.fn().mockReturnValue(analyticsEntity) - - const result = await createRepository().findOneByUserUuid('123') - - expect(queryBuilder.where).toHaveBeenCalledWith('analytics_entity.user_uuid = :userUuid', { userUuid: '123' }) - - expect(result).toEqual(analyticsEntity) - }) -}) diff --git a/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.ts b/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.ts deleted file mode 100644 index 2a0fe0d56..000000000 --- a/packages/auth/src/Infra/MySQL/MySQLAnalyticsEntityRepository.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Uuid } from '@standardnotes/common' -import { inject, injectable } from 'inversify' -import { Repository } from 'typeorm' - -import TYPES from '../../Bootstrap/Types' -import { AnalyticsEntity } from '../../Domain/Analytics/AnalyticsEntity' -import { AnalyticsEntityRepositoryInterface } from '../../Domain/Analytics/AnalyticsEntityRepositoryInterface' - -@injectable() -export class MySQLAnalyticsEntityRepository implements AnalyticsEntityRepositoryInterface { - constructor( - @inject(TYPES.ORMAnalyticsEntityRepository) - private ormRepository: Repository, - ) {} - - async findOneByUserUuid(userUuid: Uuid): Promise { - return this.ormRepository - .createQueryBuilder('analytics_entity') - .where('analytics_entity.user_uuid = :userUuid', { userUuid }) - .getOne() - } - - async save(analyticsEntity: AnalyticsEntity): Promise { - return this.ormRepository.save(analyticsEntity) - } -} diff --git a/packages/security/src/Domain/Token/CrossServiceTokenData.ts b/packages/security/src/Domain/Token/CrossServiceTokenData.ts index 660a4d8cb..0153a82a1 100644 --- a/packages/security/src/Domain/Token/CrossServiceTokenData.ts +++ b/packages/security/src/Domain/Token/CrossServiceTokenData.ts @@ -19,5 +19,4 @@ export type CrossServiceTokenData = { refresh_expiration: string } extensionKey?: string - analyticsId?: number } diff --git a/packages/syncing-server/package.json b/packages/syncing-server/package.json index 8a6eac12d..1f2c13e7a 100644 --- a/packages/syncing-server/package.json +++ b/packages/syncing-server/package.json @@ -25,7 +25,6 @@ "dependencies": { "@newrelic/winston-enricher": "^4.0.0", "@sentry/node": "^7.3.0", - "@standardnotes/analytics": "workspace:*", "@standardnotes/common": "workspace:*", "@standardnotes/domain-events": "workspace:*", "@standardnotes/domain-events-infra": "workspace:*", diff --git a/packages/syncing-server/src/Bootstrap/Container.ts b/packages/syncing-server/src/Bootstrap/Container.ts index 27003e66d..fb3db41b8 100644 --- a/packages/syncing-server/src/Bootstrap/Container.ts +++ b/packages/syncing-server/src/Bootstrap/Container.ts @@ -7,13 +7,6 @@ import { DomainEventMessageHandlerInterface, DomainEventSubscriberFactoryInterface, } from '@standardnotes/domain-events' -import { - AnalyticsStoreInterface, - PeriodKeyGenerator, - RedisAnalyticsStore, - RedisStatisticsStore, - StatisticsStoreInterface, -} from '@standardnotes/analytics' import { Env } from './Env' import TYPES from './Types' @@ -242,13 +235,6 @@ export class ContainerConfigLoader { container.bind(TYPES.ExtensionsHttpService).to(ExtensionsHttpService) container.bind(TYPES.ItemBackupService).to(S3ItemBackupService) container.bind(TYPES.RevisionService).to(RevisionService) - const periodKeyGenerator = new PeriodKeyGenerator() - container - .bind(TYPES.AnalyticsStore) - .toConstantValue(new RedisAnalyticsStore(periodKeyGenerator, container.get(TYPES.Redis))) - container - .bind(TYPES.StatisticsStore) - .toConstantValue(new RedisStatisticsStore(periodKeyGenerator, container.get(TYPES.Redis))) if (env.get('SNS_TOPIC_ARN', true)) { container diff --git a/packages/syncing-server/src/Bootstrap/Types.ts b/packages/syncing-server/src/Bootstrap/Types.ts index b3146dbe8..a29dec233 100644 --- a/packages/syncing-server/src/Bootstrap/Types.ts +++ b/packages/syncing-server/src/Bootstrap/Types.ts @@ -71,8 +71,6 @@ const TYPES = { ContentTypeFilter: Symbol.for('ContentTypeFilter'), ContentFilter: Symbol.for('ContentFilter'), ItemFactory: Symbol.for('ItemFactory'), - AnalyticsStore: Symbol.for('AnalyticsStore'), - StatisticsStore: Symbol.for('StatisticsStore'), ItemTransferCalculator: Symbol.for('ItemTransferCalculator'), } diff --git a/packages/syncing-server/src/Controller/AuthMiddleware.spec.ts b/packages/syncing-server/src/Controller/AuthMiddleware.spec.ts index dc20aed86..c46bbbcd7 100644 --- a/packages/syncing-server/src/Controller/AuthMiddleware.spec.ts +++ b/packages/syncing-server/src/Controller/AuthMiddleware.spec.ts @@ -50,7 +50,6 @@ describe('AuthMiddleware', () => { name: RoleName.ProUser, }, ], - analyticsId: 123, permissions: [], }, jwtSecret, @@ -65,7 +64,6 @@ describe('AuthMiddleware', () => { expect(response.locals.roleNames).toEqual(['CORE_USER', 'PRO_USER']) expect(response.locals.session).toEqual({ uuid: '234' }) expect(response.locals.readOnlyAccess).toBeFalsy() - expect(response.locals.analyticsId).toEqual(123) expect(response.locals.freeUser).toEqual(false) expect(next).toHaveBeenCalled() @@ -82,7 +80,6 @@ describe('AuthMiddleware', () => { name: RoleName.CoreUser, }, ], - analyticsId: 123, permissions: [], }, jwtSecret, @@ -116,7 +113,6 @@ describe('AuthMiddleware', () => { name: RoleName.ProUser, }, ], - analyticsId: 123, permissions: [], }, jwtSecret, @@ -131,7 +127,6 @@ describe('AuthMiddleware', () => { expect(response.locals.roleNames).toEqual(['CORE_USER', 'PRO_USER']) expect(response.locals.session).toEqual({ uuid: '234', readonly_access: true }) expect(response.locals.readOnlyAccess).toBeTruthy() - expect(response.locals.analyticsId).toEqual(123) expect(next).toHaveBeenCalled() }) diff --git a/packages/syncing-server/src/Controller/AuthMiddleware.ts b/packages/syncing-server/src/Controller/AuthMiddleware.ts index 87802e526..029df9551 100644 --- a/packages/syncing-server/src/Controller/AuthMiddleware.ts +++ b/packages/syncing-server/src/Controller/AuthMiddleware.ts @@ -32,7 +32,6 @@ export class AuthMiddleware extends BaseMiddleware { response.locals.roleNames.length === 1 && response.locals.roleNames[0] === RoleName.CoreUser response.locals.session = decodedToken.session response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false - response.locals.analyticsId = decodedToken.analyticsId return next() } catch (error) { diff --git a/packages/syncing-server/src/Controller/ItemsController.spec.ts b/packages/syncing-server/src/Controller/ItemsController.spec.ts index 4f7a0eb8d..14b3a2623 100644 --- a/packages/syncing-server/src/Controller/ItemsController.spec.ts +++ b/packages/syncing-server/src/Controller/ItemsController.spec.ts @@ -74,7 +74,6 @@ describe('ItemsController', () => { response.locals.user = { uuid: '123', } - response.locals.analyticsId = 123 response.locals.freeUser = false syncResponse = {} as jest.Mocked @@ -133,7 +132,6 @@ describe('ItemsController', () => { }, ], userUuid: '123', - analyticsId: 123, freeUser: false, }) @@ -150,7 +148,6 @@ describe('ItemsController', () => { expect(checkIntegrity.execute).toHaveBeenCalledWith({ integrityPayloads: [], userUuid: '123', - analyticsId: 123, freeUser: false, }) @@ -183,7 +180,6 @@ describe('ItemsController', () => { limit: 150, syncToken: 'MjoxNjE3MTk1MzQyLjc1ODEyMTc=', userUuid: '123', - analyticsId: 123, sessionUuid: null, }) @@ -215,7 +211,6 @@ describe('ItemsController', () => { limit: 150, syncToken: 'MjoxNjE3MTk1MzQyLjc1ODEyMTc=', userUuid: '123', - analyticsId: 123, sessionUuid: null, }) @@ -236,7 +231,6 @@ describe('ItemsController', () => { limit: 150, syncToken: 'MjoxNjE3MTk1MzQyLjc1ODEyMTc=', userUuid: '123', - analyticsId: 123, sessionUuid: '2-3-4', }) diff --git a/packages/syncing-server/src/Controller/ItemsController.ts b/packages/syncing-server/src/Controller/ItemsController.ts index 131a72f43..48f9a1a53 100644 --- a/packages/syncing-server/src/Controller/ItemsController.ts +++ b/packages/syncing-server/src/Controller/ItemsController.ts @@ -41,7 +41,6 @@ export class ItemsController extends BaseHttpController { contentType: request.body.content_type, apiVersion: request.body.api ?? ApiVersion.v20161215, readOnlyAccess: response.locals.readOnlyAccess, - analyticsId: response.locals.analyticsId, sessionUuid: response.locals.session ? response.locals.session.uuid : null, }) @@ -62,7 +61,6 @@ export class ItemsController extends BaseHttpController { const result = await this.checkIntegrity.execute({ userUuid: response.locals.user.uuid, integrityPayloads, - analyticsId: response.locals.analyticsId, freeUser: response.locals.freeUser, }) diff --git a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.spec.ts b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.spec.ts index f1eb2d6ae..a1bce6656 100644 --- a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.spec.ts @@ -1,7 +1,5 @@ import 'reflect-metadata' -import { AnalyticsStoreInterface, Period, StatisticsStoreInterface } from '@standardnotes/analytics' - import { ItemRepositoryInterface } from '../../Item/ItemRepositoryInterface' import { CheckIntegrity } from './CheckIntegrity' @@ -9,10 +7,8 @@ import { ContentType } from '@standardnotes/common' describe('CheckIntegrity', () => { let itemRepository: ItemRepositoryInterface - let statisticsStore: StatisticsStoreInterface - let analyticsStore: AnalyticsStoreInterface - const createUseCase = () => new CheckIntegrity(itemRepository, statisticsStore, analyticsStore) + const createUseCase = () => new CheckIntegrity(itemRepository) beforeEach(() => { itemRepository = {} as jest.Mocked @@ -43,21 +39,12 @@ describe('CheckIntegrity', () => { content_type: ContentType.File, }, ]) - - statisticsStore = {} as jest.Mocked - statisticsStore.incrementOutOfSyncIncidents = jest.fn() - statisticsStore.incrementMeasure = jest.fn() - - analyticsStore = {} as jest.Mocked - analyticsStore.wasActivityDone = jest.fn().mockReturnValue(false) - analyticsStore.markActivity = jest.fn() }) it('should return an empty result if there are no integrity mismatches', async () => { expect( await createUseCase().execute({ userUuid: '1-2-3', - analyticsId: 1, freeUser: false, integrityPayloads: [ { @@ -87,7 +74,6 @@ describe('CheckIntegrity', () => { expect( await createUseCase().execute({ userUuid: '1-2-3', - analyticsId: 1, freeUser: false, integrityPayloads: [ { @@ -116,15 +102,12 @@ describe('CheckIntegrity', () => { }, ], }) - - expect(statisticsStore.incrementOutOfSyncIncidents).toHaveBeenCalled() }) it('should return a mismatch item that is missing on the client side', async () => { expect( await createUseCase().execute({ userUuid: '1-2-3', - analyticsId: 1, freeUser: false, integrityPayloads: [ { @@ -150,87 +133,4 @@ describe('CheckIntegrity', () => { ], }) }) - - it('should count notes for statistics of free users', async () => { - await createUseCase().execute({ - userUuid: '1-2-3', - analyticsId: 1, - freeUser: true, - integrityPayloads: [ - { - uuid: '1-2-3', - updated_at_timestamp: 1, - }, - { - uuid: '2-3-4', - updated_at_timestamp: 1, - }, - { - uuid: '3-4-5', - updated_at_timestamp: 3, - }, - ], - }) - - expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith('notes-count-free-users', 3, [ - Period.Today, - Period.ThisMonth, - ]) - expect(analyticsStore.markActivity).toHaveBeenCalledWith(['checking-integrity'], 1, [Period.Today]) - }) - - it('should count notes for statistics of paid users', async () => { - await createUseCase().execute({ - userUuid: '1-2-3', - analyticsId: 1, - freeUser: false, - integrityPayloads: [ - { - uuid: '1-2-3', - updated_at_timestamp: 1, - }, - { - uuid: '2-3-4', - updated_at_timestamp: 1, - }, - { - uuid: '3-4-5', - updated_at_timestamp: 3, - }, - ], - }) - - expect(statisticsStore.incrementMeasure).toHaveBeenCalledWith('notes-count-paid-users', 3, [ - Period.Today, - Period.ThisMonth, - ]) - expect(analyticsStore.markActivity).toHaveBeenCalledWith(['checking-integrity'], 1, [Period.Today]) - }) - - it('should not count notes for statistics if they were already counted today', async () => { - analyticsStore.wasActivityDone = jest.fn().mockReturnValue(true) - - await createUseCase().execute({ - userUuid: '1-2-3', - analyticsId: 1, - freeUser: false, - integrityPayloads: [ - { - uuid: '1-2-3', - updated_at_timestamp: 1, - }, - { - uuid: '2-3-4', - updated_at_timestamp: 1, - }, - { - uuid: '3-4-5', - updated_at_timestamp: 3, - }, - ], - }) - - expect(statisticsStore.incrementMeasure).not.toHaveBeenCalled() - expect(analyticsStore.markActivity).not.toHaveBeenCalled() - }) }) diff --git a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.ts b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.ts index 14d5d1c94..bbc524a44 100644 --- a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.ts +++ b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrity.ts @@ -1,12 +1,6 @@ import { inject, injectable } from 'inversify' import { IntegrityPayload } from '@standardnotes/payloads' -import { - AnalyticsActivity, - AnalyticsStoreInterface, - Period, - StatisticsMeasure, - StatisticsStoreInterface, -} from '@standardnotes/analytics' +import { ContentType } from '@standardnotes/common' import TYPES from '../../../Bootstrap/Types' import { ItemRepositoryInterface } from '../../Item/ItemRepositoryInterface' @@ -14,34 +8,19 @@ import { UseCaseInterface } from '../UseCaseInterface' import { CheckIntegrityDTO } from './CheckIntegrityDTO' import { CheckIntegrityResponse } from './CheckIntegrityResponse' import { ExtendedIntegrityPayload } from '../../Item/ExtendedIntegrityPayload' -import { ContentType } from '@standardnotes/common' @injectable() export class CheckIntegrity implements UseCaseInterface { - constructor( - @inject(TYPES.ItemRepository) private itemRepository: ItemRepositoryInterface, - @inject(TYPES.StatisticsStore) private statisticsStore: StatisticsStoreInterface, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - ) {} + constructor(@inject(TYPES.ItemRepository) private itemRepository: ItemRepositoryInterface) {} async execute(dto: CheckIntegrityDTO): Promise { const serverItemIntegrityPayloads = await this.itemRepository.findItemsForComputingIntegrityPayloads(dto.userUuid) - let notesCount = 0 - let filesCount = 0 const serverItemIntegrityPayloadsMap = new Map() for (const serverItemIntegrityPayload of serverItemIntegrityPayloads) { serverItemIntegrityPayloadsMap.set(serverItemIntegrityPayload.uuid, serverItemIntegrityPayload) - if (serverItemIntegrityPayload.content_type === ContentType.Note) { - notesCount++ - } - if (serverItemIntegrityPayload.content_type === ContentType.File) { - filesCount++ - } } - await this.saveNotesCountStatistics(dto.freeUser, dto.analyticsId, { notes: notesCount, files: filesCount }) - const clientItemIntegrityPayloadsMap = new Map() for (const clientItemIntegrityPayload of dto.integrityPayloads) { clientItemIntegrityPayloadsMap.set( @@ -83,41 +62,8 @@ export class CheckIntegrity implements UseCaseInterface { } } - if (mismatches.length > 0) { - await this.statisticsStore.incrementOutOfSyncIncidents() - } - return { mismatches, } } - - private async saveNotesCountStatistics( - freeUser: boolean, - analyticsId: number, - counts: { notes: number; files: number }, - ) { - const integrityWasCheckedToday = await this.analyticsStore.wasActivityDone( - AnalyticsActivity.CheckingIntegrity, - analyticsId, - Period.Today, - ) - - if (!integrityWasCheckedToday) { - await this.analyticsStore.markActivity([AnalyticsActivity.CheckingIntegrity], analyticsId, [Period.Today]) - - await this.statisticsStore.incrementMeasure( - freeUser ? StatisticsMeasure.NotesCountFreeUsers : StatisticsMeasure.NotesCountPaidUsers, - counts.notes, - [Period.Today, Period.ThisMonth], - ) - - if (!freeUser) { - await this.statisticsStore.incrementMeasure(StatisticsMeasure.FilesCount, counts.files, [ - Period.Today, - Period.ThisMonth, - ]) - } - } - } } diff --git a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrityDTO.ts b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrityDTO.ts index 55b6de799..bf3f5f48c 100644 --- a/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrityDTO.ts +++ b/packages/syncing-server/src/Domain/UseCase/CheckIntegrity/CheckIntegrityDTO.ts @@ -5,5 +5,4 @@ export type CheckIntegrityDTO = { userUuid: Uuid integrityPayloads: IntegrityPayload[] freeUser: boolean - analyticsId: number } diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItems.spec.ts b/packages/syncing-server/src/Domain/UseCase/SyncItems.spec.ts index f26d2fe1e..1857fe253 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItems.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItems.spec.ts @@ -1,7 +1,6 @@ import 'reflect-metadata' import { ContentType } from '@standardnotes/common' -import { AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { ApiVersion } from '../Api/ApiVersion' import { Item } from '../Item/Item' @@ -16,9 +15,8 @@ describe('SyncItems', () => { let item2: Item let item3: Item let itemHash: ItemHash - let analyticsStore: AnalyticsStoreInterface - const createUseCase = () => new SyncItems(itemService, analyticsStore) + const createUseCase = () => new SyncItems(itemService) beforeEach(() => { item1 = { @@ -53,61 +51,9 @@ describe('SyncItems', () => { syncToken: 'qwerty', }) itemService.frontLoadKeysItemsToTop = jest.fn().mockReturnValue([item3, item1]) - - analyticsStore = {} as jest.Mocked - analyticsStore.markActivity = jest.fn() }) it('should sync items', async () => { - expect( - await createUseCase().execute({ - userUuid: '1-2-3', - itemHashes: [itemHash], - computeIntegrityHash: false, - syncToken: 'foo', - cursorToken: 'bar', - limit: 10, - readOnlyAccess: false, - contentType: 'Note', - apiVersion: ApiVersion.v20200115, - analyticsId: 123, - sessionUuid: '2-3-4', - }), - ).toEqual({ - conflicts: [], - cursorToken: 'asdzxc', - retrievedItems: [item1], - savedItems: [item2], - syncToken: 'qwerty', - }) - - expect(itemService.frontLoadKeysItemsToTop).not.toHaveBeenCalled() - expect(itemService.getItems).toHaveBeenCalledWith({ - contentType: 'Note', - cursorToken: 'bar', - limit: 10, - syncToken: 'foo', - userUuid: '1-2-3', - }) - expect(itemService.saveItems).toHaveBeenCalledWith({ - itemHashes: [itemHash], - userUuid: '1-2-3', - apiVersion: '20200115', - readOnlyAccess: false, - sessionUuid: '2-3-4', - }) - expect(analyticsStore.markActivity).toHaveBeenNthCalledWith(1, ['editing-items'], 123, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - expect(analyticsStore.markActivity).toHaveBeenNthCalledWith(2, ['email-unbacked-up-data'], 123, [ - Period.Today, - Period.ThisWeek, - ]) - }) - - it('should sync items - no analytics', async () => { expect( await createUseCase().execute({ userUuid: '1-2-3', @@ -144,7 +90,6 @@ describe('SyncItems', () => { readOnlyAccess: false, sessionUuid: null, }) - expect(analyticsStore.markActivity).not.toHaveBeenCalled() }) it('should sync items and return items keys on top for first sync', async () => { @@ -158,7 +103,6 @@ describe('SyncItems', () => { sessionUuid: '2-3-4', contentType: 'Note', apiVersion: ApiVersion.v20200115, - analyticsId: 123, }), ).toEqual({ conflicts: [], @@ -202,7 +146,6 @@ describe('SyncItems', () => { limit: 10, contentType: 'Note', apiVersion: ApiVersion.v20200115, - analyticsId: 123, }), ).toEqual({ conflicts: [ diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts index 7aea2a741..460ffd3a1 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts @@ -1,4 +1,3 @@ -import { AnalyticsActivity, AnalyticsStoreInterface, Period } from '@standardnotes/analytics' import { inject, injectable } from 'inversify' import TYPES from '../../Bootstrap/Types' import { Item } from '../Item/Item' @@ -10,10 +9,7 @@ import { UseCaseInterface } from './UseCaseInterface' @injectable() export class SyncItems implements UseCaseInterface { - constructor( - @inject(TYPES.ItemService) private itemService: ItemServiceInterface, - @inject(TYPES.AnalyticsStore) private analyticsStore: AnalyticsStoreInterface, - ) {} + constructor(@inject(TYPES.ItemService) private itemService: ItemServiceInterface) {} async execute(dto: SyncItemsDTO): Promise { const getItemsResult = await this.itemService.getItems({ @@ -37,19 +33,6 @@ export class SyncItems implements UseCaseInterface { retrievedItems = await this.itemService.frontLoadKeysItemsToTop(dto.userUuid, retrievedItems) } - if (dto.analyticsId && saveItemsResult.savedItems.length > 0) { - await this.analyticsStore.markActivity([AnalyticsActivity.EditingItems], dto.analyticsId, [ - Period.Today, - Period.ThisWeek, - Period.ThisMonth, - ]) - - await this.analyticsStore.markActivity([AnalyticsActivity.EmailUnbackedUpData], dto.analyticsId, [ - Period.Today, - Period.ThisWeek, - ]) - } - const syncResponse: SyncItemsResponse = { retrievedItems, syncToken: saveItemsResult.syncToken, diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts b/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts index af595e494..1def1d589 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts @@ -9,7 +9,6 @@ export type SyncItemsDTO = { syncToken?: string | null cursorToken?: string | null contentType?: string - analyticsId?: number apiVersion: string readOnlyAccess: boolean sessionUuid: Uuid | null diff --git a/yarn.lock b/yarn.lock index e17946e4c..c1ca02358 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1899,7 +1899,6 @@ __metadata: dependencies: "@newrelic/winston-enricher": "npm:^4.0.0" "@sentry/node": "npm:^7.3.0" - "@standardnotes/analytics": "workspace:*" "@standardnotes/api": "npm:^1.19.0" "@standardnotes/common": "workspace:*" "@standardnotes/domain-events": "workspace:*"