mirror of
https://github.com/standardnotes/server
synced 2026-04-04 03:02:10 -04:00
Compare commits
1 Commits
@standardn
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0055edc3e4 |
2
.github/ci.env
vendored
2
.github/ci.env
vendored
@@ -26,5 +26,3 @@ MYSQL_ROOT_PASSWORD=changeme123
|
||||
AUTH_JWT_SECRET=f95259c5e441f5a4646d76422cfb3df4c4488842901aa50b6c51b8be2e0040e9
|
||||
AUTH_SERVER_ENCRYPTION_SERVER_KEY=1087415dfde3093797f9a7ca93a49e7d7aa1861735eb0d32aae9c303b8c3d060
|
||||
VALET_TOKEN_SECRET=4b886819ebe1e908077c6cae96311b48a8416bd60cc91c03060e15bdf6b30d1f
|
||||
|
||||
SYNCING_SERVER_CONTENT_SIZE_TRANSFER_LIMIT=1000000
|
||||
|
||||
2
.github/workflows/common-docker-image.yml
vendored
2
.github/workflows/common-docker-image.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
key: ${{ runner.os }}-${{ inputs.service_name }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
3
.github/workflows/e2e-home-server.yml
vendored
3
.github/workflows/e2e-home-server.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -70,7 +70,6 @@ jobs:
|
||||
echo "ACCESS_TOKEN_AGE=4" >> packages/home-server/.env
|
||||
echo "REFRESH_TOKEN_AGE=10" >> packages/home-server/.env
|
||||
echo "REVISIONS_FREQUENCY=2" >> packages/home-server/.env
|
||||
echo "CONTENT_SIZE_TRANSFER_LIMIT=1000000" >> packages/home-server/.env
|
||||
echo "DB_HOST=localhost" >> packages/home-server/.env
|
||||
echo "DB_PORT=3306" >> packages/home-server/.env
|
||||
echo "DB_DATABASE=standardnotes" >> packages/home-server/.env
|
||||
|
||||
2
.github/workflows/e2e-self-hosted.yml
vendored
2
.github/workflows/e2e-self-hosted.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
6
.github/workflows/pr.yml
vendored
6
.github/workflows/pr.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
8
.github/workflows/publish.yml
vendored
8
.github/workflows/publish.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -80,7 +80,7 @@ jobs:
|
||||
key: ${{ runner.os }}-build-${{ github.sha }}
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
git_commit_gpgsign: true
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
@@ -3,16 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.32.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.32.5...@standardnotes/analytics@2.32.6) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.32.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.32.4...@standardnotes/analytics@2.32.5) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/server/issues/903)) ([751f3b2](https://github.com/standardnotes/server/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [2.32.4](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.32.3...@standardnotes/analytics@2.32.4) (2023-10-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { EmailLevel, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AnalyticsScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
@@ -16,7 +22,6 @@ import { CalculateMonthlyRecurringRevenue } from '../src/Domain/UseCase/Calculat
|
||||
import { getBody, getSubject } from '../src/Domain/Email/DailyAnalyticsReport'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { StatisticMeasureName } from '../src/Domain/Statistics/StatisticMeasureName'
|
||||
import { EmailLevel } from '@standardnotes/domain-core'
|
||||
|
||||
const requestReport = async (
|
||||
analyticsStore: AnalyticsStoreInterface,
|
||||
@@ -270,6 +275,9 @@ void container.load().then((container) => {
|
||||
|
||||
logger.info(`Sending report to following admins: ${adminEmails}`)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AnalyticsScheduledTask, 'report')
|
||||
|
||||
Promise.resolve(
|
||||
requestReport(
|
||||
analyticsStore,
|
||||
@@ -285,11 +293,15 @@ void container.load().then((container) => {
|
||||
.then(() => {
|
||||
logger.info('Usage report generation complete')
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not finish usage report generation: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AnalyticsWorker })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import { DomainEventSubscriberInterface } from '@standardnotes/domain-events'
|
||||
import * as dayjs from 'dayjs'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.32.6",
|
||||
"version": "2.32.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
DomainEventPublisherInterface,
|
||||
DomainEventSubscriberInterface,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { MapperInterface } from '@standardnotes/domain-core'
|
||||
import { MapperInterface, ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const Mixpanel = require('mixpanel')
|
||||
|
||||
@@ -16,9 +16,9 @@ import TYPES from './Types'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import {
|
||||
SNSDomainEventPublisher,
|
||||
SQSDomainEventSubscriber,
|
||||
SNSOpenTelemetryDomainEventPublisher,
|
||||
SQSEventMessageHandler,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { PeriodKeyGeneratorInterface } from '../Domain/Time/PeriodKeyGeneratorInterface'
|
||||
@@ -139,7 +139,9 @@ export class ContainerConfigLoader {
|
||||
|
||||
container
|
||||
.bind<DomainEventPublisherInterface>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
|
||||
.toConstantValue(
|
||||
new SNSOpenTelemetryDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)),
|
||||
)
|
||||
if (env.get('MIXPANEL_TOKEN', true)) {
|
||||
container.bind<Mixpanel>(TYPES.MixpanelClient).toConstantValue(Mixpanel.init(env.get('MIXPANEL_TOKEN', true)))
|
||||
}
|
||||
@@ -240,7 +242,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventSubscriberInterface>(TYPES.DomainEventSubscriber)
|
||||
.toConstantValue(
|
||||
new SQSDomainEventSubscriber(
|
||||
new SQSOpenTelemetryDomainEventSubscriber(
|
||||
ServiceIdentifier.NAMES.AnalyticsWorker,
|
||||
container.get<SQSClient>(TYPES.SQS),
|
||||
container.get<string>(TYPES.SQS_QUEUE_URL),
|
||||
container.get<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler),
|
||||
|
||||
@@ -3,22 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.81.11](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.10...@standardnotes/api-gateway@1.81.11) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api-gateway:** remove calling both auth and payments on account deletion request ([6b554c2](https://github.com/standardnotes/api-gateway/commit/6b554c28b731a9080d7ad2942d3fa05c01dcabf2))
|
||||
|
||||
## [1.81.10](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.9...@standardnotes/api-gateway@1.81.10) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.81.9](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.8...@standardnotes/api-gateway@1.81.9) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/api-gateway/issues/903)) ([751f3b2](https://github.com/standardnotes/api-gateway/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.81.8](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.81.7...@standardnotes/api-gateway@1.81.8) (2023-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.ApiGateway })
|
||||
sdk.start()
|
||||
|
||||
import '../src/Controller/LegacyController'
|
||||
import '../src/Controller/HealthCheckController'
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.81.11",
|
||||
"version": "1.81.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@ export class UsersController extends BaseHttpController {
|
||||
@inject(TYPES.ApiGateway_ServiceProxy) private httpService: ServiceProxyInterface,
|
||||
@inject(TYPES.ApiGateway_EndpointResolver) private endpointResolver: EndpointResolverInterface,
|
||||
@inject(TYPES.ApiGateway_Logger) private logger: Logger,
|
||||
@inject(TYPES.ApiGateway_IS_CONFIGURED_FOR_HOME_SERVER) private isConfiguredForHomeServer: boolean,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
@@ -237,6 +238,10 @@ export class UsersController extends BaseHttpController {
|
||||
|
||||
@httpDelete('/:userUuid', TYPES.ApiGateway_RequiredCrossServiceTokenMiddleware)
|
||||
async deleteUser(request: Request, response: Response): Promise<void> {
|
||||
if (!this.isConfiguredForHomeServer) {
|
||||
await this.httpService.callPaymentsServer(request, response, 'api/account', request.body, true)
|
||||
}
|
||||
|
||||
await this.httpService.callAuthServer(
|
||||
request,
|
||||
response,
|
||||
|
||||
@@ -151,6 +151,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||
if (!this.paymentsServerUrl) {
|
||||
this.logger.debug('Payments Server URL not defined. Skipped request to Payments API.')
|
||||
@@ -158,13 +159,18 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
return
|
||||
}
|
||||
|
||||
await this.callServerWithLegacyFormat(
|
||||
const rawResponse = await this.callServerWithLegacyFormat(
|
||||
this.paymentsServerUrl,
|
||||
request,
|
||||
response,
|
||||
endpointOrMethodIdentifier,
|
||||
payload,
|
||||
returnRawResponse,
|
||||
)
|
||||
|
||||
if (returnRawResponse) {
|
||||
return rawResponse
|
||||
}
|
||||
}
|
||||
|
||||
async callAuthServerWithLegacyFormat(
|
||||
@@ -339,6 +345,7 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
response: Response,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>> {
|
||||
const serviceResponse = await this.getServerResponse(
|
||||
serverUrl,
|
||||
@@ -357,10 +364,18 @@ export class HttpServiceProxy implements ServiceProxyInterface {
|
||||
if (serviceResponse.request._redirectable._redirectCount > 0) {
|
||||
response.status(302)
|
||||
|
||||
if (returnRawResponse) {
|
||||
return response
|
||||
}
|
||||
|
||||
response.redirect(serviceResponse.request.res.responseUrl)
|
||||
} else {
|
||||
response.status(serviceResponse.status)
|
||||
|
||||
if (returnRawResponse) {
|
||||
return response
|
||||
}
|
||||
|
||||
response.send(serviceResponse.data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface ServiceProxyInterface {
|
||||
response: Response,
|
||||
endpointOrMethodIdentifier: string,
|
||||
payload?: Record<string, unknown> | string,
|
||||
returnRawResponse?: boolean,
|
||||
): Promise<void | Response<unknown, Record<string, unknown>>>
|
||||
callWebSocketServer(
|
||||
request: Request,
|
||||
|
||||
@@ -3,24 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.166.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.165.4...@standardnotes/auth-server@1.166.0) (2023-11-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add triggering post setting update actions ([#905](https://github.com/standardnotes/server/issues/905)) ([d228a86](https://github.com/standardnotes/server/commit/d228a86f48c9ff62b7810244c347abf7770e2b9f))
|
||||
|
||||
## [1.165.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.165.3...@standardnotes/auth-server@1.165.4) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* account deletion event ([#904](https://github.com/standardnotes/server/issues/904)) ([d66ae62](https://github.com/standardnotes/server/commit/d66ae62cf4f413cac5f6f4eac45dc0f1ddbc9e32))
|
||||
|
||||
## [1.165.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.165.2...@standardnotes/auth-server@1.165.3) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/server/issues/903)) ([751f3b2](https://github.com/standardnotes/server/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.165.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.165.1...@standardnotes/auth-server@1.165.2) (2023-11-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier, SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Stream } from 'stream'
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
@@ -7,13 +15,78 @@ import * as utc from 'dayjs/plugin/utc'
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { TriggerEmailBackupForAllUsers } from '../src/Domain/UseCase/TriggerEmailBackupForAllUsers/TriggerEmailBackupForAllUsers'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingRepositoryInterface'
|
||||
import { MuteFailedBackupsEmailsOption } from '@standardnotes/settings'
|
||||
import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { GetUserKeyParams } from '../src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const backupFrequency = inputArgs[0]
|
||||
const backupProvider = inputArgs[0]
|
||||
const backupFrequency = inputArgs[1]
|
||||
|
||||
const requestBackups = async (triggerEmailBackupForAllUsers: TriggerEmailBackupForAllUsers): Promise<void> => {
|
||||
await triggerEmailBackupForAllUsers.execute({ backupFrequency })
|
||||
const requestBackups = async (
|
||||
settingRepository: SettingRepositoryInterface,
|
||||
roleService: RoleServiceInterface,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
getUserKeyParamsUseCase: GetUserKeyParams,
|
||||
): Promise<void> => {
|
||||
const settingName = SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue()
|
||||
const permissionName = PermissionName.DailyEmailBackup
|
||||
const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails
|
||||
const muteEmailsSettingValue = MuteFailedBackupsEmailsOption.Muted
|
||||
|
||||
const stream = await settingRepository.streamAllByNameAndValue(settingName, backupFrequency)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
stream
|
||||
.pipe(
|
||||
new Stream.Transform({
|
||||
objectMode: true,
|
||||
transform: async (setting, _encoding, callback) => {
|
||||
const userIsPermittedForEmailBackups = await roleService.userHasPermission(
|
||||
setting.setting_user_uuid,
|
||||
permissionName,
|
||||
)
|
||||
if (!userIsPermittedForEmailBackups) {
|
||||
callback()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let userHasEmailsMuted = false
|
||||
const emailsMutedSetting = await settingRepository.findOneByNameAndUserUuid(
|
||||
muteEmailsSettingName,
|
||||
setting.setting_user_uuid,
|
||||
)
|
||||
if (emailsMutedSetting !== null && emailsMutedSetting.props.value !== null) {
|
||||
userHasEmailsMuted = emailsMutedSetting.props.value === muteEmailsSettingValue
|
||||
}
|
||||
|
||||
const keyParamsResponse = await getUserKeyParamsUseCase.execute({
|
||||
userUuid: setting.setting_user_uuid,
|
||||
authenticated: false,
|
||||
})
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createEmailBackupRequestedEvent(
|
||||
setting.setting_user_uuid,
|
||||
emailsMutedSetting?.id.toString() as string,
|
||||
userHasEmailsMuted,
|
||||
keyParamsResponse.keyParams,
|
||||
),
|
||||
)
|
||||
|
||||
callback()
|
||||
},
|
||||
}),
|
||||
)
|
||||
.on('finish', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader('worker')
|
||||
@@ -25,20 +98,31 @@ void container.load().then((container) => {
|
||||
|
||||
const logger: Logger = container.get(TYPES.Auth_Logger)
|
||||
|
||||
logger.info(`Starting ${backupFrequency} email backup requesting...`)
|
||||
logger.info(`Starting ${backupFrequency} ${backupProvider} backup requesting...`)
|
||||
|
||||
const triggerEmailBackupForAllUsers: TriggerEmailBackupForAllUsers = container.get(
|
||||
TYPES.Auth_TriggerEmailBackupForAllUsers,
|
||||
const settingRepository: SettingRepositoryInterface = container.get(TYPES.Auth_SettingRepository)
|
||||
const roleService: RoleServiceInterface = container.get(TYPES.Auth_RoleService)
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory)
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher)
|
||||
const getUserKeyParamsUseCase: GetUserKeyParams = container.get(TYPES.Auth_GetUserKeyParams)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'backup')
|
||||
|
||||
Promise.resolve(
|
||||
requestBackups(settingRepository, roleService, domainEventFactory, domainEventPublisher, getUserKeyParamsUseCase),
|
||||
)
|
||||
|
||||
Promise.resolve(requestBackups(triggerEmailBackupForAllUsers))
|
||||
.then(() => {
|
||||
logger.info(`${backupFrequency} email backup requesting complete`)
|
||||
logger.info(`${backupFrequency} ${backupProvider} backup requesting complete`)
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not finish ${backupFrequency} email backup requesting: ${error.message}`)
|
||||
logger.error(`Could not finish ${backupFrequency} ${backupProvider} backup requesting: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
@@ -30,15 +36,22 @@ void container.load().then((container) => {
|
||||
const cleanupSessionTraces: CleanupSessionTraces = container.get(TYPES.Auth_CleanupSessionTraces)
|
||||
const cleanupExpiredSessions: CleanupExpiredSessions = container.get(TYPES.Auth_CleanupExpiredSessions)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'cleanup')
|
||||
|
||||
Promise.resolve(cleanup(cleanupSessionTraces, cleanupExpiredSessions))
|
||||
.then(() => {
|
||||
logger.info('Expired sessions and session traces cleaned.')
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not clean sessions and session traces: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.Auth })
|
||||
sdk.start()
|
||||
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedAuthenticatorsController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedSessionsController'
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@@ -20,6 +26,9 @@ void container.load().then((container) => {
|
||||
const persistStats: PersistStatistics = container.get(TYPES.Auth_PersistStatistics)
|
||||
const timer: TimerInterface = container.get(TYPES.Auth_Timer)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'stats')
|
||||
|
||||
Promise.resolve(
|
||||
persistStats.execute({
|
||||
sessionsInADay: timer.getUTCDateNDaysAgo(1),
|
||||
@@ -28,11 +37,15 @@ void container.load().then((container) => {
|
||||
.then(() => {
|
||||
logger.info('Stats persisted.')
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not persist stats: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { Email, ServiceIdentifier, SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
@@ -15,7 +21,6 @@ import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { GetUserKeyParams } from '../src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
import { Email, SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const backupEmail = inputArgs[0]
|
||||
@@ -89,6 +94,9 @@ void container.load().then((container) => {
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher)
|
||||
const getUserKeyParamsUseCase: GetUserKeyParams = container.get(TYPES.Auth_GetUserKeyParams)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'user_email_backup')
|
||||
|
||||
Promise.resolve(
|
||||
requestBackups(
|
||||
userRepository,
|
||||
@@ -102,11 +110,15 @@ void container.load().then((container) => {
|
||||
.then(() => {
|
||||
logger.info(`Email backup requesting complete for ${backupEmail}`)
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not finish email backup requesting for ${backupEmail}: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthWorker })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
|
||||
@@ -26,12 +26,12 @@ case "$COMMAND" in
|
||||
|
||||
'email-daily-backup' )
|
||||
echo "[Docker] Starting Email Daily Backup..."
|
||||
node docker/entrypoint-backup.js daily
|
||||
node docker/entrypoint-backup.js email daily
|
||||
;;
|
||||
|
||||
'email-weekly-backup' )
|
||||
echo "[Docker] Starting Email Weekly Backup..."
|
||||
node docker/entrypoint-backup.js weekly
|
||||
node docker/entrypoint-backup.js email weekly
|
||||
;;
|
||||
|
||||
'email-backup' )
|
||||
@@ -40,6 +40,21 @@ case "$COMMAND" in
|
||||
node docker/entrypoint-user-email-backup.js $EMAIL
|
||||
;;
|
||||
|
||||
'dropbox-daily-backup' )
|
||||
echo "[Docker] Starting Dropbox Daily Backup..."
|
||||
node docker/entrypoint-backup.js dropbox daily
|
||||
;;
|
||||
|
||||
'google-drive-daily-backup' )
|
||||
echo "[Docker] Starting Google Drive Daily Backup..."
|
||||
node docker/entrypoint-backup.js google_drive daily
|
||||
;;
|
||||
|
||||
'one-drive-daily-backup' )
|
||||
echo "[Docker] Starting One Drive Daily Backup..."
|
||||
node docker/entrypoint-backup.js one_drive daily
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "[Docker] Unknown command"
|
||||
;;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.166.0",
|
||||
"version": "1.165.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -24,9 +24,12 @@
|
||||
"worker": "yarn node dist/bin/worker.js",
|
||||
"cleanup": "yarn node dist/bin/cleanup.js",
|
||||
"stats": "yarn node dist/bin/stats.js",
|
||||
"daily-backup:email": "yarn node dist/bin/backup.js daily",
|
||||
"daily-backup:email": "yarn node dist/bin/backup.js email daily",
|
||||
"user-email-backup": "yarn node dist/bin/user_email_backup.js",
|
||||
"weekly-backup:email": "yarn node dist/bin/backup.js weekly",
|
||||
"daily-backup:dropbox": "yarn node dist/bin/backup.js dropbox daily",
|
||||
"daily-backup:google_drive": "yarn node dist/bin/backup.js google_drive daily",
|
||||
"daily-backup:one_drive": "yarn node dist/bin/backup.js one_drive daily",
|
||||
"weekly-backup:email": "yarn node dist/bin/backup.js email weekly",
|
||||
"content-recalculation": "yarn node dist/bin/content.js",
|
||||
"typeorm": "typeorm-ts-node-commonjs",
|
||||
"migrate": "yarn build && yarn typeorm migration:run -d dist/src/Bootstrap/DataSource.js"
|
||||
|
||||
@@ -79,9 +79,9 @@ import { ExtensionKeyGrantedEventHandler } from '../Domain/Handler/ExtensionKeyG
|
||||
import {
|
||||
DirectCallDomainEventPublisher,
|
||||
DirectCallEventMessageHandler,
|
||||
SNSDomainEventPublisher,
|
||||
SQSDomainEventSubscriber,
|
||||
SNSOpenTelemetryDomainEventPublisher,
|
||||
SQSEventMessageHandler,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { GetUserSubscription } from '../Domain/UseCase/GetUserSubscription/GetUserSubscription'
|
||||
import { ChangeCredentials } from '../Domain/UseCase/ChangeCredentials/ChangeCredentials'
|
||||
@@ -130,6 +130,8 @@ import { ListedAccountCreatedEventHandler } from '../Domain/Handler/ListedAccoun
|
||||
import { ListedAccountDeletedEventHandler } from '../Domain/Handler/ListedAccountDeletedEventHandler'
|
||||
import { FileRemovedEventHandler } from '../Domain/Handler/FileRemovedEventHandler'
|
||||
import { UserDisabledSessionUserAgentLoggingEventHandler } from '../Domain/Handler/UserDisabledSessionUserAgentLoggingEventHandler'
|
||||
import { SettingInterpreterInterface } from '../Domain/Setting/SettingInterpreterInterface'
|
||||
import { SettingInterpreter } from '../Domain/Setting/SettingInterpreter'
|
||||
import { SettingCrypterInterface } from '../Domain/Setting/SettingCrypterInterface'
|
||||
import { SettingCrypter } from '../Domain/Setting/SettingCrypter'
|
||||
import { SharedSubscriptionInvitationRepositoryInterface } from '../Domain/SharedSubscription/SharedSubscriptionInvitationRepositoryInterface'
|
||||
@@ -168,6 +170,7 @@ import {
|
||||
ControllerContainer,
|
||||
ControllerContainerInterface,
|
||||
MapperInterface,
|
||||
ServiceIdentifier,
|
||||
SharedVaultUser,
|
||||
} from '@standardnotes/domain-core'
|
||||
import { SessionTracePersistenceMapper } from '../Mapping/SessionTracePersistenceMapper'
|
||||
@@ -273,9 +276,6 @@ import { SubscriptionSettingPersistenceMapper } from '../Mapping/Persistence/Sub
|
||||
import { ApplyDefaultSettings } from '../Domain/UseCase/ApplyDefaultSettings/ApplyDefaultSettings'
|
||||
import { AuthResponseFactoryResolverInterface } from '../Domain/Auth/AuthResponseFactoryResolverInterface'
|
||||
import { UserInvitedToSharedVaultEventHandler } from '../Domain/Handler/UserInvitedToSharedVaultEventHandler'
|
||||
import { TriggerPostSettingUpdateActions } from '../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
||||
import { TriggerEmailBackupForUser } from '../Domain/UseCase/TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
import { TriggerEmailBackupForAllUsers } from '../Domain/UseCase/TriggerEmailBackupForAllUsers/TriggerEmailBackupForAllUsers'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
constructor(private mode: 'server' | 'worker' = 'server') {}
|
||||
@@ -379,7 +379,10 @@ export class ContainerConfigLoader {
|
||||
.toConstantValue(
|
||||
isConfiguredForHomeServer
|
||||
? directCallDomainEventPublisher
|
||||
: new SNSDomainEventPublisher(container.get(TYPES.Auth_SNS), container.get(TYPES.Auth_SNS_TOPIC_ARN)),
|
||||
: new SNSOpenTelemetryDomainEventPublisher(
|
||||
container.get(TYPES.Auth_SNS),
|
||||
container.get(TYPES.Auth_SNS_TOPIC_ARN),
|
||||
),
|
||||
)
|
||||
|
||||
// Mapping
|
||||
@@ -773,6 +776,16 @@ export class ContainerConfigLoader {
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SettingInterpreterInterface>(TYPES.Auth_SettingInterpreter)
|
||||
.toConstantValue(
|
||||
new SettingInterpreter(
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<SettingRepositoryInterface>(TYPES.Auth_SettingRepository),
|
||||
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
|
||||
),
|
||||
)
|
||||
|
||||
container.bind<OfflineSettingServiceInterface>(TYPES.Auth_OfflineSettingService).to(OfflineSettingService)
|
||||
container.bind<ContentDecoderInterface>(TYPES.Auth_ContenDecoder).toConstantValue(new ContentDecoder())
|
||||
@@ -782,16 +795,7 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<SubscriptionSettingsAssociationServiceInterface>(TYPES.Auth_SubscriptionSettingsAssociationService)
|
||||
.to(SubscriptionSettingsAssociationService)
|
||||
container
|
||||
.bind<FeatureServiceInterface>(TYPES.Auth_FeatureService)
|
||||
.toConstantValue(
|
||||
new FeatureService(
|
||||
container.get<RoleToSubscriptionMapInterface>(TYPES.Auth_RoleToSubscriptionMap),
|
||||
container.get<OfflineUserSubscriptionRepositoryInterface>(TYPES.Auth_OfflineUserSubscriptionRepository),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
container.get<UserSubscriptionRepositoryInterface>(TYPES.Auth_UserSubscriptionRepository),
|
||||
),
|
||||
)
|
||||
container.bind<FeatureServiceInterface>(TYPES.Auth_FeatureService).to(FeatureService)
|
||||
container
|
||||
.bind<SelectorInterface<boolean>>(TYPES.Auth_BooleanSelector)
|
||||
.toConstantValue(new DeterministicSelector<boolean>())
|
||||
@@ -1104,7 +1108,6 @@ export class ContainerConfigLoader {
|
||||
new DeleteAccount(
|
||||
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
|
||||
container.get<GetRegularSubscriptionForUser>(TYPES.Auth_GetRegularSubscriptionForUser),
|
||||
container.get<GetSharedSubscriptionForUser>(TYPES.Auth_GetSharedSubscriptionForUser),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<TimerInterface>(TYPES.Auth_Timer),
|
||||
@@ -1222,35 +1225,6 @@ export class ContainerConfigLoader {
|
||||
container.get<GetSharedOrRegularSubscriptionForUser>(TYPES.Auth_GetSharedOrRegularSubscriptionForUser),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TriggerEmailBackupForUser>(TYPES.Auth_TriggerEmailBackupForUser)
|
||||
.toConstantValue(
|
||||
new TriggerEmailBackupForUser(
|
||||
container.get<RoleServiceInterface>(TYPES.Auth_RoleService),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TriggerEmailBackupForAllUsers>(TYPES.Auth_TriggerEmailBackupForAllUsers)
|
||||
.toConstantValue(
|
||||
new TriggerEmailBackupForAllUsers(
|
||||
container.get<SettingRepositoryInterface>(TYPES.Auth_SettingRepository),
|
||||
container.get<TriggerEmailBackupForUser>(TYPES.Auth_TriggerEmailBackupForUser),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TriggerPostSettingUpdateActions>(TYPES.Auth_TriggerPostSettingUpdateActions)
|
||||
.toConstantValue(
|
||||
new TriggerPostSettingUpdateActions(
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
|
||||
container.get<TriggerEmailBackupForUser>(TYPES.Auth_TriggerEmailBackupForUser),
|
||||
),
|
||||
)
|
||||
|
||||
// Controller
|
||||
container
|
||||
@@ -1540,7 +1514,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventSubscriberInterface>(TYPES.Auth_DomainEventSubscriber)
|
||||
.toConstantValue(
|
||||
new SQSDomainEventSubscriber(
|
||||
new SQSOpenTelemetryDomainEventSubscriber(
|
||||
ServiceIdentifier.NAMES.AuthWorker,
|
||||
container.get<SQSClient>(TYPES.Auth_SQS),
|
||||
container.get<string>(TYPES.Auth_SQS_QUEUE_URL),
|
||||
container.get<DomainEventMessageHandlerInterface>(TYPES.Auth_DomainEventMessageHandler),
|
||||
@@ -1675,13 +1650,11 @@ export class ContainerConfigLoader {
|
||||
container.get<GetAllSettingsForUser>(TYPES.Auth_GetAllSettingsForUser),
|
||||
container.get<GetSetting>(TYPES.Auth_GetSetting),
|
||||
container.get<SetSettingValue>(TYPES.Auth_SetSettingValue),
|
||||
container.get<TriggerPostSettingUpdateActions>(TYPES.Auth_TriggerPostSettingUpdateActions),
|
||||
container.get<DeleteSetting>(TYPES.Auth_DeleteSetting),
|
||||
container.get<MapperInterface<Setting, SettingHttpRepresentation>>(TYPES.Auth_SettingHttpMapper),
|
||||
container.get<MapperInterface<SubscriptionSetting, SubscriptionSettingHttpRepresentation>>(
|
||||
TYPES.Auth_SubscriptionSettingHttpMapper,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
container.get<ControllerContainerInterface>(TYPES.Auth_ControllerContainer),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -164,9 +164,6 @@ const TYPES = {
|
||||
Auth_DesignateSurvivor: Symbol.for('Auth_DesignateSurvivor'),
|
||||
Auth_GetSharedOrRegularSubscriptionForUser: Symbol.for('Auth_GetSharedOrRegularSubscriptionForUser'),
|
||||
Auth_DisableEmailSettingBasedOnEmailSubscription: Symbol.for('Auth_DisableEmailSettingBasedOnEmailSubscription'),
|
||||
Auth_TriggerPostSettingUpdateActions: Symbol.for('Auth_TriggerPostSettingUpdateActions'),
|
||||
Auth_TriggerEmailBackupForUser: Symbol.for('Auth_TriggerEmailBackupForUser'),
|
||||
Auth_TriggerEmailBackupForAllUsers: Symbol.for('Auth_TriggerEmailBackupForAllUsers'),
|
||||
// Handlers
|
||||
Auth_AccountDeletionRequestedEventHandler: Symbol.for('Auth_AccountDeletionRequestedEventHandler'),
|
||||
Auth_SubscriptionPurchasedEventHandler: Symbol.for('Auth_SubscriptionPurchasedEventHandler'),
|
||||
@@ -233,6 +230,7 @@ const TYPES = {
|
||||
Auth_SubscriptionSettingsAssociationService: Symbol.for('Auth_SubscriptionSettingsAssociationService'),
|
||||
Auth_FeatureService: Symbol.for('Auth_FeatureService'),
|
||||
Auth_SettingCrypter: Symbol.for('Auth_SettingCrypter'),
|
||||
Auth_SettingInterpreter: Symbol.for('Auth_SettingInterpreter'),
|
||||
Auth_ProtocolVersionSelector: Symbol.for('Auth_ProtocolVersionSelector'),
|
||||
Auth_BooleanSelector: Symbol.for('Auth_BooleanSelector'),
|
||||
Auth_BaseAuthController: Symbol.for('Auth_BaseAuthController'),
|
||||
|
||||
@@ -281,16 +281,9 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
|
||||
createAccountDeletionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
userCreatedAtTimestamp: number
|
||||
regularSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
sharedSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
regularSubscriptionUuid: string | undefined
|
||||
roleNames: string[]
|
||||
}): AccountDeletionRequestedEvent {
|
||||
return {
|
||||
type: 'ACCOUNT_DELETION_REQUESTED',
|
||||
|
||||
@@ -45,16 +45,9 @@ export interface DomainEventFactoryInterface {
|
||||
): EmailBackupRequestedEvent
|
||||
createAccountDeletionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
email: string
|
||||
userCreatedAtTimestamp: number
|
||||
regularSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
sharedSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
regularSubscriptionUuid: string | undefined
|
||||
roleNames: string[]
|
||||
}): AccountDeletionRequestedEvent
|
||||
createUserRolesChangedEvent(userUuid: string, email: string, currentRoles: string[]): UserRolesChangedEvent
|
||||
createUserEmailChangedEvent(userUuid: string, fromEmail: string, toEmail: string): UserEmailChangedEvent
|
||||
|
||||
@@ -35,7 +35,6 @@ import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/Offl
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||
import { UserSubscriptionType } from '../Subscription/UserSubscriptionType'
|
||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||
|
||||
describe('FeatureService', () => {
|
||||
let roleToSubscriptionMap: RoleToSubscriptionMapInterface
|
||||
@@ -53,10 +52,8 @@ describe('FeatureService', () => {
|
||||
let offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let offlineUserSubscription: OfflineUserSubscription
|
||||
let userSubscriptionRepository: UserSubscriptionRepositoryInterface
|
||||
|
||||
const createService = () =>
|
||||
new FeatureService(roleToSubscriptionMap, offlineUserSubscriptionRepository, timer, userSubscriptionRepository)
|
||||
const createService = () => new FeatureService(roleToSubscriptionMap, offlineUserSubscriptionRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
roleToSubscriptionMap = {} as jest.Mocked<RoleToSubscriptionMapInterface>
|
||||
@@ -110,7 +107,7 @@ describe('FeatureService', () => {
|
||||
renewedAt: null,
|
||||
planName: SubscriptionName.PlusPlan,
|
||||
endsAt: 555,
|
||||
userUuid: 'user-1-1-1',
|
||||
user: Promise.resolve(user),
|
||||
cancelled: false,
|
||||
subscriptionId: 1,
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
@@ -123,7 +120,7 @@ describe('FeatureService', () => {
|
||||
renewedAt: null,
|
||||
planName: SubscriptionName.ProPlan,
|
||||
endsAt: 777,
|
||||
userUuid: 'user-1-1-1',
|
||||
user: Promise.resolve(user),
|
||||
cancelled: false,
|
||||
subscriptionId: 2,
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
@@ -136,7 +133,7 @@ describe('FeatureService', () => {
|
||||
renewedAt: null,
|
||||
planName: SubscriptionName.PlusPlan,
|
||||
endsAt: 333,
|
||||
userUuid: 'user-1-1-1',
|
||||
user: Promise.resolve(user),
|
||||
cancelled: true,
|
||||
subscriptionId: 3,
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
@@ -149,7 +146,7 @@ describe('FeatureService', () => {
|
||||
renewedAt: null,
|
||||
planName: SubscriptionName.PlusPlan,
|
||||
endsAt: 333,
|
||||
userUuid: 'user-1-1-1',
|
||||
user: Promise.resolve(user),
|
||||
cancelled: true,
|
||||
subscriptionId: 4,
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
@@ -158,11 +155,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1]),
|
||||
subscriptions: Promise.resolve([subscription1]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository = {} as jest.Mocked<UserSubscriptionRepositoryInterface>
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1])
|
||||
|
||||
offlineUserSubscription = {
|
||||
roles: Promise.resolve([role1]),
|
||||
uuid: 'subscription-1-1-1',
|
||||
@@ -252,10 +247,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1, role2, nonSubscriptionRole]),
|
||||
subscriptions: Promise.resolve([subscription1, subscription2]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1, subscription2])
|
||||
|
||||
expect(await createService().userIsEntitledToFeature(user, 'files-beta')).toBe(true)
|
||||
})
|
||||
|
||||
@@ -275,12 +269,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1]),
|
||||
subscriptions: Promise.resolve([subscription3, subscription1, subscription4]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue([subscription3, subscription1, subscription4])
|
||||
|
||||
const features = await createService().getFeaturesForUser(user)
|
||||
expect(features).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -293,13 +284,14 @@ describe('FeatureService', () => {
|
||||
})
|
||||
|
||||
it('should not return user features if a subscription could not be found', async () => {
|
||||
const subscriptions: Array<UserSubscription> = []
|
||||
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1]),
|
||||
subscriptions: Promise.resolve(subscriptions),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([])
|
||||
|
||||
expect(await createService().getFeaturesForUser(user)).toEqual([])
|
||||
})
|
||||
|
||||
@@ -315,12 +307,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1]),
|
||||
subscriptions: Promise.resolve([subscription3, subscription1, subscription4]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest
|
||||
.fn()
|
||||
.mockReturnValue([subscription3, subscription1, subscription4])
|
||||
|
||||
expect(await createService().getFeaturesForUser(user)).toEqual([])
|
||||
})
|
||||
|
||||
@@ -332,7 +321,7 @@ describe('FeatureService', () => {
|
||||
renewedAt: null,
|
||||
planName: 'non existing plan name' as SubscriptionName,
|
||||
endsAt: 555,
|
||||
userUuid: 'user-1-1-1',
|
||||
user: Promise.resolve(user),
|
||||
cancelled: false,
|
||||
subscriptionId: 1,
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
@@ -341,10 +330,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1]),
|
||||
subscriptions: Promise.resolve([subscription1]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1])
|
||||
|
||||
expect(await createService().getFeaturesForUser(user)).toEqual([])
|
||||
})
|
||||
|
||||
@@ -363,10 +351,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1, role2]),
|
||||
subscriptions: Promise.resolve([subscription1, subscription2]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1, subscription2])
|
||||
|
||||
const features = await createService().getFeaturesForUser(user)
|
||||
expect(features).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -422,10 +409,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1, role2, nonSubscriptionRole]),
|
||||
subscriptions: Promise.resolve([subscription1, subscription2]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1, subscription2])
|
||||
|
||||
const features = await createService().getFeaturesForUser(user)
|
||||
expect(features).toEqual(
|
||||
expect.arrayContaining([
|
||||
@@ -459,10 +445,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1, role2]),
|
||||
subscriptions: Promise.resolve([subscription1, subscription2]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1, subscription2])
|
||||
|
||||
const longestExpireAt = 777
|
||||
|
||||
const features = await createService().getFeaturesForUser(user)
|
||||
@@ -497,10 +482,9 @@ describe('FeatureService', () => {
|
||||
user = {
|
||||
uuid: 'user-1-1-1',
|
||||
roles: Promise.resolve([role1, role2]),
|
||||
subscriptions: Promise.resolve([subscription1, subscription2]),
|
||||
} as jest.Mocked<User>
|
||||
|
||||
userSubscriptionRepository.findByUserUuid = jest.fn().mockReturnValue([subscription1, subscription2])
|
||||
|
||||
const features = await createService().getFeaturesForUser(user)
|
||||
expect(features).toEqual(
|
||||
expect.arrayContaining([
|
||||
|
||||
@@ -1,22 +1,24 @@
|
||||
import { SubscriptionName } from '@standardnotes/common'
|
||||
import { FeatureDescription, GetFeatures } from '@standardnotes/features'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { RoleToSubscriptionMapInterface } from '../Role/RoleToSubscriptionMapInterface'
|
||||
|
||||
import { User } from '../User/User'
|
||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||
import { FeatureServiceInterface } from './FeatureServiceInterface'
|
||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||
import { Role } from '../Role/Role'
|
||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
@injectable()
|
||||
export class FeatureService implements FeatureServiceInterface {
|
||||
constructor(
|
||||
private roleToSubscriptionMap: RoleToSubscriptionMapInterface,
|
||||
@inject(TYPES.Auth_RoleToSubscriptionMap) private roleToSubscriptionMap: RoleToSubscriptionMapInterface,
|
||||
@inject(TYPES.Auth_OfflineUserSubscriptionRepository)
|
||||
private offlineUserSubscriptionRepository: OfflineUserSubscriptionRepositoryInterface,
|
||||
private timer: TimerInterface,
|
||||
private userSubscriptionRepository: UserSubscriptionRepositoryInterface,
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async userIsEntitledToFeature(user: User, featureIdentifier: string): Promise<boolean> {
|
||||
@@ -59,7 +61,7 @@ export class FeatureService implements FeatureServiceInterface {
|
||||
}
|
||||
|
||||
async getFeaturesForUser(user: User): Promise<Array<FeatureDescription>> {
|
||||
const userSubscriptions = await this.userSubscriptionRepository.findByUserUuid(user.uuid)
|
||||
const userSubscriptions = await user.subscriptions
|
||||
|
||||
return this.getFeaturesForSubscriptions(userSubscriptions, await user.roles)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||
import { Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { Username } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterface {
|
||||
@@ -48,22 +48,7 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf
|
||||
private async removeRoleFromSubscriptionUsers(subscriptionId: number, subscriptionName: string): Promise<void> {
|
||||
const userSubscriptions = await this.userSubscriptionRepository.findBySubscriptionId(subscriptionId)
|
||||
for (const userSubscription of userSubscriptions) {
|
||||
const userUuidOrError = Uuid.create(userSubscription.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
this.logger.warn(`Could not remove role from user with uuid: ${userUuidOrError.getError()}`)
|
||||
|
||||
continue
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||
if (user === null) {
|
||||
this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
await this.roleService.removeUserRoleBasedOnSubscription(user, subscriptionName)
|
||||
await this.roleService.removeUserRoleBasedOnSubscription(await userSubscription.user, subscriptionName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
|
||||
): Promise<UserSubscription> {
|
||||
const subscription = new UserSubscription()
|
||||
subscription.planName = subscriptionName
|
||||
subscription.userUuid = user.uuid
|
||||
subscription.user = Promise.resolve(user)
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
subscription.endsAt = subscriptionExpiresAt
|
||||
|
||||
@@ -80,7 +80,7 @@ export class SubscriptionReassignedEventHandler implements DomainEventHandlerInt
|
||||
): Promise<UserSubscription> {
|
||||
const subscription = new UserSubscription()
|
||||
subscription.planName = subscriptionName
|
||||
subscription.userUuid = user.uuid
|
||||
subscription.user = Promise.resolve(user)
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
subscription.endsAt = subscriptionExpiresAt
|
||||
|
||||
@@ -7,7 +7,7 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { UserSubscriptionRepositoryInterface } from '../Subscription/UserSubscriptionRepositoryInterface'
|
||||
import { OfflineUserSubscriptionRepositoryInterface } from '../Subscription/OfflineUserSubscriptionRepositoryInterface'
|
||||
import { Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { Username } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionRefundedEventHandler implements DomainEventHandlerInterface {
|
||||
@@ -48,22 +48,7 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter
|
||||
private async removeRoleFromSubscriptionUsers(subscriptionId: number, subscriptionName: string): Promise<void> {
|
||||
const userSubscriptions = await this.userSubscriptionRepository.findBySubscriptionId(subscriptionId)
|
||||
for (const userSubscription of userSubscriptions) {
|
||||
const userUuidOrError = Uuid.create(userSubscription.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
this.logger.warn(`Could not remove role from user with uuid: ${userUuidOrError.getError()}`)
|
||||
|
||||
continue
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||
if (user === null) {
|
||||
this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
await this.roleService.removeUserRoleBasedOnSubscription(user, subscriptionName)
|
||||
await this.roleService.removeUserRoleBasedOnSubscription(await userSubscription.user, subscriptionName)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { RoleServiceInterface } from '../Role/RoleServiceInterface'
|
||||
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
|
||||
import { Logger } from 'winston'
|
||||
import { OfflineUserSubscription } from '../Subscription/OfflineUserSubscription'
|
||||
import { Username, Uuid } from '@standardnotes/domain-core'
|
||||
import { Username } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterface {
|
||||
@@ -71,20 +71,7 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf
|
||||
private async addRoleToSubscriptionUsers(subscriptionId: number, subscriptionName: string): Promise<void> {
|
||||
const userSubscriptions = await this.userSubscriptionRepository.findBySubscriptionId(subscriptionId)
|
||||
for (const userSubscription of userSubscriptions) {
|
||||
const userUuidOrError = Uuid.create(userSubscription.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
this.logger.warn(`Could not add role to user with uuid: ${userUuidOrError.getError()}`)
|
||||
|
||||
continue
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const user = await this.userRepository.findOneByUuid(userUuid)
|
||||
if (user === null) {
|
||||
this.logger.warn(`Could not find user with uuid: ${userUuid.value}`)
|
||||
|
||||
continue
|
||||
}
|
||||
const user = await userSubscription.user
|
||||
|
||||
await this.roleService.addUserRoleBasedOnSubscription(user, subscriptionName)
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ export class SubscriptionSyncRequestedEventHandler implements DomainEventHandler
|
||||
}
|
||||
|
||||
subscription.planName = subscriptionName
|
||||
subscription.userUuid = user.uuid
|
||||
subscription.user = Promise.resolve(user)
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
subscription.endsAt = subscriptionExpiresAt
|
||||
|
||||
153
packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts
Normal file
153
packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
import {
|
||||
DomainEventPublisherInterface,
|
||||
EmailBackupRequestedEvent,
|
||||
MuteEmailsSettingChangedEvent,
|
||||
UserDisabledSessionUserAgentLoggingEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { EmailBackupFrequency, LogSessionUserAgentOption, MuteMarketingEmailsOption } from '@standardnotes/settings'
|
||||
import 'reflect-metadata'
|
||||
import { Logger } from 'winston'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { Setting } from './Setting'
|
||||
import { SettingCrypterInterface } from './SettingCrypterInterface'
|
||||
|
||||
import { SettingInterpreter } from './SettingInterpreter'
|
||||
import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
||||
import { GetUserKeyParams } from '../UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
import { Uuid, Timestamps, UniqueEntityId, SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
describe('SettingInterpreter', () => {
|
||||
let user: User
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let settingRepository: SettingRepositoryInterface
|
||||
let settingCrypter: SettingCrypterInterface
|
||||
let logger: Logger
|
||||
let getUserKeyParams: GetUserKeyParams
|
||||
|
||||
const createInterpreter = () =>
|
||||
new SettingInterpreter(domainEventPublisher, domainEventFactory, settingRepository, getUserKeyParams)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {
|
||||
uuid: '4-5-6',
|
||||
email: 'test@test.te',
|
||||
} as jest.Mocked<User>
|
||||
|
||||
settingRepository = {} as jest.Mocked<SettingRepositoryInterface>
|
||||
settingRepository.findLastByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
settingCrypter = {} as jest.Mocked<SettingCrypterInterface>
|
||||
settingCrypter.decryptSettingValue = jest.fn().mockReturnValue('decrypted')
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createEmailBackupRequestedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<EmailBackupRequestedEvent>)
|
||||
domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<UserDisabledSessionUserAgentLoggingEvent>)
|
||||
domainEventFactory.createMuteEmailsSettingChangedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<MuteEmailsSettingChangedEvent>)
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.debug = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
logger.error = jest.fn()
|
||||
|
||||
getUserKeyParams = {} as jest.Mocked<GetUserKeyParams>
|
||||
getUserKeyParams.execute = jest.fn().mockReturnValue({ keyParams: {} as jest.Mocked<KeyParamsData> })
|
||||
})
|
||||
|
||||
it('should trigger session cleanup if user is disabling session user agent logging', async () => {
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
user,
|
||||
LogSessionUserAgentOption.Disabled,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
||||
userUuid: '4-5-6',
|
||||
email: 'test@test.te',
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Daily,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false, {})
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails muted', async () => {
|
||||
const setting = Setting.create(
|
||||
{
|
||||
name: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
value: 'muted',
|
||||
serverEncryptionVersion: 0,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
sensitive: false,
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
},
|
||||
new UniqueEntityId('7fb54003-1dd2-40bd-8900-2bacd6cf629c'),
|
||||
).getValue()
|
||||
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(setting)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Daily,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith(
|
||||
'4-5-6',
|
||||
'7fb54003-1dd2-40bd-8900-2bacd6cf629c',
|
||||
true,
|
||||
{},
|
||||
)
|
||||
})
|
||||
|
||||
it('should not trigger backup if email backup setting is disabled', async () => {
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.EmailBackupFrequency,
|
||||
user,
|
||||
EmailBackupFrequency.Disabled,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
expect(domainEventFactory.createEmailBackupRequestedEvent).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should trigger mute subscription emails rejection if mute setting changed', async () => {
|
||||
settingRepository.findOneByNameAndUserUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createInterpreter().interpretSettingUpdated(
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
user,
|
||||
MuteMarketingEmailsOption.Muted,
|
||||
)
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createMuteEmailsSettingChangedEvent).toHaveBeenCalledWith({
|
||||
emailSubscriptionRejectionLevel: 'MARKETING',
|
||||
mute: true,
|
||||
username: 'test@test.te',
|
||||
})
|
||||
})
|
||||
})
|
||||
113
packages/auth/src/Domain/Setting/SettingInterpreter.ts
Normal file
113
packages/auth/src/Domain/Setting/SettingInterpreter.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { EmailLevel, SettingName } from '@standardnotes/domain-core'
|
||||
import { EmailBackupFrequency, LogSessionUserAgentOption, MuteFailedBackupsEmailsOption } from '@standardnotes/settings'
|
||||
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { User } from '../User/User'
|
||||
import { SettingInterpreterInterface } from './SettingInterpreterInterface'
|
||||
import { SettingRepositoryInterface } from './SettingRepositoryInterface'
|
||||
import { GetUserKeyParams } from '../UseCase/GetUserKeyParams/GetUserKeyParams'
|
||||
|
||||
export class SettingInterpreter implements SettingInterpreterInterface {
|
||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<string, string> = new Map([
|
||||
[SettingName.NAMES.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||
[SettingName.NAMES.MuteFailedCloudBackupsEmails, EmailLevel.LEVELS.FailedCloudBackup],
|
||||
[SettingName.NAMES.MuteMarketingEmails, EmailLevel.LEVELS.Marketing],
|
||||
[SettingName.NAMES.MuteSignInEmails, EmailLevel.LEVELS.SignIn],
|
||||
])
|
||||
|
||||
constructor(
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private settingRepository: SettingRepositoryInterface,
|
||||
private getUserKeyParams: GetUserKeyParams,
|
||||
) {}
|
||||
|
||||
async interpretSettingUpdated(
|
||||
updatedSettingName: string,
|
||||
user: User,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
if (this.isChangingMuteEmailsSetting(updatedSettingName)) {
|
||||
await this.triggerEmailSubscriptionChange(user, updatedSettingName, unencryptedValue)
|
||||
}
|
||||
|
||||
if (this.isEnablingEmailBackupSetting(updatedSettingName, unencryptedValue)) {
|
||||
await this.triggerEmailBackup(user.uuid)
|
||||
}
|
||||
|
||||
if (this.isDisablingSessionUserAgentLogging(updatedSettingName, unencryptedValue)) {
|
||||
await this.triggerSessionUserAgentCleanup(user)
|
||||
}
|
||||
}
|
||||
|
||||
private async triggerEmailBackup(userUuid: string): Promise<void> {
|
||||
let userHasEmailsMuted = false
|
||||
let muteEmailsSettingUuid = ''
|
||||
const muteFailedEmailsBackupSetting = await this.settingRepository.findOneByNameAndUserUuid(
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
userUuid,
|
||||
)
|
||||
if (muteFailedEmailsBackupSetting !== null) {
|
||||
userHasEmailsMuted = muteFailedEmailsBackupSetting.props.value === MuteFailedBackupsEmailsOption.Muted
|
||||
muteEmailsSettingUuid = muteFailedEmailsBackupSetting.id.toString()
|
||||
}
|
||||
|
||||
const keyParamsResponse = await this.getUserKeyParams.execute({
|
||||
authenticated: false,
|
||||
userUuid,
|
||||
})
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailBackupRequestedEvent(
|
||||
userUuid,
|
||||
muteEmailsSettingUuid,
|
||||
userHasEmailsMuted,
|
||||
keyParamsResponse.keyParams,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private isChangingMuteEmailsSetting(settingName: string): boolean {
|
||||
return [
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
].includes(settingName)
|
||||
}
|
||||
|
||||
private isEnablingEmailBackupSetting(settingName: string, newValue: string | null): boolean {
|
||||
return (
|
||||
settingName === SettingName.NAMES.EmailBackupFrequency &&
|
||||
[EmailBackupFrequency.Daily, EmailBackupFrequency.Weekly].includes(newValue as EmailBackupFrequency)
|
||||
)
|
||||
}
|
||||
|
||||
private isDisablingSessionUserAgentLogging(settingName: string, newValue: string | null): boolean {
|
||||
return SettingName.NAMES.LogSessionUserAgent === settingName && LogSessionUserAgentOption.Disabled === newValue
|
||||
}
|
||||
|
||||
private async triggerEmailSubscriptionChange(
|
||||
user: User,
|
||||
settingName: string,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createMuteEmailsSettingChangedEvent({
|
||||
username: user.email,
|
||||
mute: unencryptedValue === 'muted',
|
||||
emailSubscriptionRejectionLevel: this.emailSettingToSubscriptionRejectionLevelMap.get(settingName) as string,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async triggerSessionUserAgentCleanup(user: User) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent({
|
||||
userUuid: user.uuid,
|
||||
email: user.email,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { User } from '../User/User'
|
||||
|
||||
export interface SettingInterpreterInterface {
|
||||
interpretSettingUpdated(updatedSettingName: string, user: User, newUnencryptedValue: string | null): Promise<void>
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ReadStream } from 'fs'
|
||||
import { SettingName } from '@standardnotes/domain-core'
|
||||
|
||||
import { DeleteSettingDto } from '../UseCase/DeleteSetting/DeleteSettingDto'
|
||||
@@ -9,8 +10,8 @@ export interface SettingRepositoryInterface {
|
||||
findOneByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findLastByNameAndUserUuid(name: string, userUuid: string): Promise<Setting | null>
|
||||
findAllByUserUuid(userUuid: string): Promise<Setting[]>
|
||||
countAllByNameAndValue(dto: { name: SettingName; value: string }): Promise<number>
|
||||
findAllByNameAndValue(dto: { name: SettingName; value: string; offset: number; limit: number }): Promise<Setting[]>
|
||||
streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream>
|
||||
streamAllByName(name: SettingName): Promise<ReadStream>
|
||||
deleteByUserUuid(dto: DeleteSettingDto): Promise<void>
|
||||
insert(setting: Setting): Promise<void>
|
||||
update(setting: Setting): Promise<void>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { User } from '../User/User'
|
||||
import { UserSubscriptionType } from './UserSubscriptionType'
|
||||
|
||||
@Entity({ name: 'user_subscriptions' })
|
||||
@@ -62,9 +63,14 @@ export class UserSubscription {
|
||||
})
|
||||
declare subscriptionType: UserSubscriptionType
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
declare userUuid: string
|
||||
@ManyToOne(
|
||||
/* istanbul ignore next */
|
||||
() => User,
|
||||
/* istanbul ignore next */
|
||||
(user) => user.subscriptions,
|
||||
/* istanbul ignore next */
|
||||
{ onDelete: 'CASCADE', nullable: false, lazy: true, eager: false },
|
||||
)
|
||||
@JoinColumn({ name: 'user_uuid', referencedColumnName: 'uuid' })
|
||||
declare user: Promise<User>
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ describe('AcceptSharedSubscriptionInvitation', () => {
|
||||
subscriptionId: 3,
|
||||
subscriptionType: 'shared',
|
||||
updatedAt: 1,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(invitee),
|
||||
})
|
||||
expect(roleService.addUserRoleBasedOnSubscription).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
||||
expect(applyDefaultSubscriptionSettings.execute).toHaveBeenCalled()
|
||||
@@ -145,7 +145,7 @@ describe('AcceptSharedSubscriptionInvitation', () => {
|
||||
subscriptionId: 3,
|
||||
subscriptionType: 'shared',
|
||||
updatedAt: 3,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(invitee),
|
||||
})
|
||||
expect(roleService.addUserRoleBasedOnSubscription).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
||||
expect(applyDefaultSubscriptionSettings.execute).toHaveBeenCalled()
|
||||
|
||||
@@ -115,7 +115,7 @@ export class AcceptSharedSubscriptionInvitation implements UseCaseInterface {
|
||||
): Promise<UserSubscription> {
|
||||
const subscription = new UserSubscription()
|
||||
subscription.planName = subscriptionName
|
||||
subscription.userUuid = user.uuid
|
||||
subscription.user = Promise.resolve(user)
|
||||
const timestamp = this.timer.getTimestampInMicroseconds()
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
|
||||
@@ -53,7 +53,7 @@ export class ActivatePremiumFeatures implements UseCaseInterface<string> {
|
||||
|
||||
const subscription = new UserSubscription()
|
||||
subscription.planName = subscriptionPlanName.value
|
||||
subscription.userUuid = user.uuid
|
||||
subscription.user = Promise.resolve(user)
|
||||
subscription.createdAt = timestamp
|
||||
subscription.updatedAt = timestamp
|
||||
subscription.endsAt = this.timer.convertDateToMicroseconds(endsAt)
|
||||
|
||||
@@ -80,7 +80,7 @@ describe('CancelSharedSubscriptionInvitation', () => {
|
||||
userSubscriptionRepository.findBySubscriptionIdAndType = jest.fn().mockReturnValue([inviterSubscription])
|
||||
userSubscriptionRepository.findOneByUserUuidAndSubscriptionId = jest
|
||||
.fn()
|
||||
.mockReturnValue({ userUuid: '123' } as jest.Mocked<UserSubscription>)
|
||||
.mockReturnValue({ user: Promise.resolve(invitee) } as jest.Mocked<UserSubscription>)
|
||||
userSubscriptionRepository.save = jest.fn()
|
||||
|
||||
roleService = {} as jest.Mocked<RoleServiceInterface>
|
||||
@@ -120,7 +120,7 @@ describe('CancelSharedSubscriptionInvitation', () => {
|
||||
})
|
||||
expect(userSubscriptionRepository.save).toHaveBeenCalledWith({
|
||||
endsAt: 1,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(invitee),
|
||||
})
|
||||
expect(roleService.removeUserRoleBasedOnSubscription).toHaveBeenCalledWith(invitee, 'PLUS_PLAN')
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
|
||||
@@ -5,6 +5,7 @@ import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@sta
|
||||
|
||||
import { CreateValetToken } from './CreateValetToken'
|
||||
import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
import { User } from '../../User/User'
|
||||
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { GetRegularSubscriptionForUser } from '../GetRegularSubscriptionForUser/GetRegularSubscriptionForUser'
|
||||
@@ -24,6 +25,7 @@ describe('CreateValetToken', () => {
|
||||
const valetTokenTTL = 123
|
||||
let regularSubscription: UserSubscription
|
||||
let sharedSubscription: UserSubscription
|
||||
let user: User
|
||||
|
||||
const createUseCase = () =>
|
||||
new CreateValetToken(
|
||||
@@ -57,16 +59,20 @@ describe('CreateValetToken', () => {
|
||||
subscriptionSettingsAssociationService = {} as jest.Mocked<SubscriptionSettingsAssociationServiceInterface>
|
||||
subscriptionSettingsAssociationService.getFileUploadLimit = jest.fn().mockReturnValue(5_368_709_120)
|
||||
|
||||
user = {
|
||||
uuid: '123',
|
||||
} as jest.Mocked<User>
|
||||
|
||||
regularSubscription = {
|
||||
uuid: '1-2-3',
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
sharedSubscription = {
|
||||
uuid: '2-3-4',
|
||||
subscriptionType: UserSubscriptionType.Shared,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
getRegularSubscription = {} as jest.Mocked<GetRegularSubscriptionForUser>
|
||||
|
||||
@@ -11,46 +11,29 @@ import { TimerInterface } from '@standardnotes/time'
|
||||
import { Result, RoleName } from '@standardnotes/domain-core'
|
||||
import { Role } from '../../Role/Role'
|
||||
import { GetRegularSubscriptionForUser } from '../GetRegularSubscriptionForUser/GetRegularSubscriptionForUser'
|
||||
import { GetSharedSubscriptionForUser } from '../GetSharedSubscriptionForUser/GetSharedSubscriptionForUser'
|
||||
|
||||
describe('DeleteAccount', () => {
|
||||
let userRepository: UserRepositoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let getRegularSubscription: GetRegularSubscriptionForUser
|
||||
let getSharedSubscription: GetSharedSubscriptionForUser
|
||||
let user: User
|
||||
let regularSubscription: UserSubscription
|
||||
let sharedSubscription: UserSubscription
|
||||
let timer: TimerInterface
|
||||
|
||||
const createUseCase = () =>
|
||||
new DeleteAccount(
|
||||
userRepository,
|
||||
getRegularSubscription,
|
||||
getSharedSubscription,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
timer,
|
||||
)
|
||||
new DeleteAccount(userRepository, getRegularSubscription, domainEventPublisher, domainEventFactory, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
user = {
|
||||
uuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
} as jest.Mocked<User>
|
||||
user.roles = Promise.resolve([{ name: RoleName.NAMES.CoreUser } as jest.Mocked<Role>])
|
||||
|
||||
regularSubscription = {
|
||||
uuid: '1-2-3',
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
userUuid: 'u-1-2-3',
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
sharedSubscription = {
|
||||
uuid: '1-2-3',
|
||||
subscriptionType: UserSubscriptionType.Shared,
|
||||
userUuid: 'u-1-2-3',
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
userRepository = {} as jest.Mocked<UserRepositoryInterface>
|
||||
@@ -60,9 +43,6 @@ describe('DeleteAccount', () => {
|
||||
getRegularSubscription = {} as jest.Mocked<GetRegularSubscriptionForUser>
|
||||
getRegularSubscription.execute = jest.fn().mockReturnValue(Result.ok(regularSubscription))
|
||||
|
||||
getSharedSubscription = {} as jest.Mocked<GetSharedSubscriptionForUser>
|
||||
getSharedSubscription.execute = jest.fn().mockReturnValue(Result.ok(sharedSubscription))
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
@@ -78,7 +58,6 @@ describe('DeleteAccount', () => {
|
||||
describe('when user uuid is provided', () => {
|
||||
it('should trigger account deletion - no subscription', async () => {
|
||||
getRegularSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
getSharedSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
|
||||
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||
|
||||
@@ -87,11 +66,12 @@ describe('DeleteAccount', () => {
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
userCreatedAtTimestamp: 1,
|
||||
email: 'test@test.te',
|
||||
regularSubscriptionUuid: undefined,
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger account deletion - shared subscription present', async () => {
|
||||
it('should trigger account deletion - subscription present', async () => {
|
||||
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
@@ -99,35 +79,9 @@ describe('DeleteAccount', () => {
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
sharedSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger account deletion - regular subscription present', async () => {
|
||||
getSharedSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
|
||||
const result = await createUseCase().execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
})
|
||||
|
||||
@@ -155,7 +109,6 @@ describe('DeleteAccount', () => {
|
||||
describe('when username is provided', () => {
|
||||
it('should trigger account deletion - no subscription', async () => {
|
||||
getRegularSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
getSharedSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
|
||||
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||
|
||||
@@ -163,12 +116,13 @@ describe('DeleteAccount', () => {
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: undefined,
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger account deletion - shared subscription present', async () => {
|
||||
it('should trigger account deletion - subscription present', async () => {
|
||||
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
@@ -176,35 +130,9 @@ describe('DeleteAccount', () => {
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
sharedSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger account deletion - regular subscription present', async () => {
|
||||
getSharedSubscription.execute = jest.fn().mockReturnValue(Result.fail('not found'))
|
||||
|
||||
const result = await createUseCase().execute({ username: 'test@test.te' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createAccountDeletionRequestedEvent).toHaveBeenLastCalledWith({
|
||||
userUuid: '1-2-3',
|
||||
email: 'test@test.te',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscription: {
|
||||
ownerUuid: 'u-1-2-3',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -8,14 +8,11 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
||||
import { DeleteAccountDTO } from './DeleteAccountDTO'
|
||||
import { User } from '../../User/User'
|
||||
import { GetRegularSubscriptionForUser } from '../GetRegularSubscriptionForUser/GetRegularSubscriptionForUser'
|
||||
import { UserSubscription } from '../../Subscription/UserSubscription'
|
||||
import { GetSharedSubscriptionForUser } from '../GetSharedSubscriptionForUser/GetSharedSubscriptionForUser'
|
||||
|
||||
export class DeleteAccount implements UseCaseInterface<string> {
|
||||
constructor(
|
||||
private userRepository: UserRepositoryInterface,
|
||||
private getRegularSubscription: GetRegularSubscriptionForUser,
|
||||
private getSharedSubscription: GetSharedSubscriptionForUser,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private timer: TimerInterface,
|
||||
@@ -47,39 +44,23 @@ export class DeleteAccount implements UseCaseInterface<string> {
|
||||
return Result.ok('User already deleted.')
|
||||
}
|
||||
|
||||
let sharedSubscription: UserSubscription | undefined
|
||||
const sharedSubscriptionOrError = await this.getSharedSubscription.execute({
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (!sharedSubscriptionOrError.isFailed()) {
|
||||
sharedSubscription = sharedSubscriptionOrError.getValue()
|
||||
}
|
||||
const roles = await user.roles
|
||||
|
||||
let regularSubscription: UserSubscription | undefined
|
||||
const regularSubscriptionOrError = await this.getRegularSubscription.execute({
|
||||
let regularSubscriptionUuid: string | undefined
|
||||
const result = await this.getRegularSubscription.execute({
|
||||
userUuid: user.uuid,
|
||||
})
|
||||
if (!regularSubscriptionOrError.isFailed()) {
|
||||
regularSubscription = regularSubscriptionOrError.getValue()
|
||||
if (!result.isFailed()) {
|
||||
const regularSubscription = result.getValue()
|
||||
regularSubscriptionUuid = regularSubscription.uuid
|
||||
}
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createAccountDeletionRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
email: user.email,
|
||||
userCreatedAtTimestamp: this.timer.convertDateToMicroseconds(user.createdAt),
|
||||
regularSubscription: regularSubscription
|
||||
? {
|
||||
ownerUuid: regularSubscription.userUuid,
|
||||
uuid: regularSubscription.uuid,
|
||||
}
|
||||
: undefined,
|
||||
sharedSubscription: sharedSubscription
|
||||
? {
|
||||
ownerUuid: sharedSubscription.userUuid,
|
||||
uuid: sharedSubscription.uuid,
|
||||
}
|
||||
: undefined,
|
||||
regularSubscriptionUuid,
|
||||
roleNames: roles.map((role) => role.name),
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
import { Logger } from 'winston'
|
||||
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
|
||||
import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
import { TriggerEmailBackupForAllUsers } from './TriggerEmailBackupForAllUsers'
|
||||
import { EncryptionVersion } from '../../Encryption/EncryptionVersion'
|
||||
|
||||
import { Setting } from '../../Setting/Setting'
|
||||
import { Result, SettingName, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
describe('TriggerEmailBackupForAllUsers', () => {
|
||||
let settingRepository: SettingRepositoryInterface
|
||||
let triggerEmailBackupForUserUseCase: TriggerEmailBackupForUser
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () =>
|
||||
new TriggerEmailBackupForAllUsers(settingRepository, triggerEmailBackupForUserUseCase, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
const setting = Setting.create({
|
||||
name: SettingName.NAMES.EmailBackupFrequency,
|
||||
value: null,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
sensitive: false,
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
settingRepository = {} as jest.Mocked<SettingRepositoryInterface>
|
||||
settingRepository.countAllByNameAndValue = jest.fn().mockResolvedValue(1)
|
||||
settingRepository.findAllByNameAndValue = jest.fn().mockResolvedValue([setting])
|
||||
|
||||
triggerEmailBackupForUserUseCase = {} as jest.Mocked<TriggerEmailBackupForUser>
|
||||
triggerEmailBackupForUserUseCase.execute = jest.fn().mockResolvedValue(Result.ok())
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
logger.info = jest.fn()
|
||||
})
|
||||
|
||||
it('triggers email backup for all users', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ backupFrequency: 'daily' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Result, SettingName, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
import { SettingRepositoryInterface } from '../../Setting/SettingRepositoryInterface'
|
||||
import { TriggerEmailBackupForAllUsersDTO } from './TriggerEmailBackupForAllUsersDTO'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
export class TriggerEmailBackupForAllUsers implements UseCaseInterface<void> {
|
||||
private PAGING_LIMIT = 100
|
||||
|
||||
constructor(
|
||||
private settingRepository: SettingRepositoryInterface,
|
||||
private triggerEmailBackupForUserUseCase: TriggerEmailBackupForUser,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: TriggerEmailBackupForAllUsersDTO): Promise<Result<void>> {
|
||||
const emailBackupFrequencySettingName = SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue()
|
||||
|
||||
const allSettingsCount = await this.settingRepository.countAllByNameAndValue({
|
||||
name: emailBackupFrequencySettingName,
|
||||
value: dto.backupFrequency,
|
||||
})
|
||||
|
||||
this.logger.info(`Found ${allSettingsCount} users with email backup frequency set to ${dto.backupFrequency}`)
|
||||
|
||||
let failedUsers = 0
|
||||
const numberOfPages = Math.ceil(allSettingsCount / this.PAGING_LIMIT)
|
||||
for (let i = 0; i < numberOfPages; i++) {
|
||||
const settings = await this.settingRepository.findAllByNameAndValue({
|
||||
name: emailBackupFrequencySettingName,
|
||||
value: dto.backupFrequency,
|
||||
offset: i * this.PAGING_LIMIT,
|
||||
limit: this.PAGING_LIMIT,
|
||||
})
|
||||
|
||||
for (const setting of settings) {
|
||||
const result = await this.triggerEmailBackupForUserUseCase.execute({
|
||||
userUuid: setting.props.userUuid.value,
|
||||
})
|
||||
/* istanbul ignore next */
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Failed to trigger email backup for user ${setting.props.userUuid.value}`)
|
||||
failedUsers++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (failedUsers > 0) {
|
||||
this.logger.error(`Failed to trigger email backup for ${failedUsers} users`)
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface TriggerEmailBackupForAllUsersDTO {
|
||||
backupFrequency: string
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
import { DomainEventPublisherInterface, EmailBackupRequestedEvent } from '@standardnotes/domain-events'
|
||||
import { Result, SettingName, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import { GetUserKeyParams } from '../GetUserKeyParams/GetUserKeyParams'
|
||||
import { TriggerEmailBackupForUser } from './TriggerEmailBackupForUser'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { Setting } from '../../Setting/Setting'
|
||||
import { EncryptionVersion } from '../../Encryption/EncryptionVersion'
|
||||
|
||||
describe('TriggerEmailBackupForUser', () => {
|
||||
let roleService: RoleServiceInterface
|
||||
let getSetting: GetSetting
|
||||
let getUserKeyParamsUseCase: GetUserKeyParams
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
|
||||
const createUseCase = () =>
|
||||
new TriggerEmailBackupForUser(
|
||||
roleService,
|
||||
getSetting,
|
||||
getUserKeyParamsUseCase,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
roleService = {} as jest.Mocked<RoleServiceInterface>
|
||||
roleService.userHasPermission = jest.fn().mockResolvedValue(true)
|
||||
|
||||
const setting = Setting.create({
|
||||
name: SettingName.NAMES.ListedAuthorSecrets,
|
||||
value: null,
|
||||
serverEncryptionVersion: EncryptionVersion.Default,
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
sensitive: false,
|
||||
timestamps: Timestamps.create(123, 123).getValue(),
|
||||
}).getValue()
|
||||
|
||||
getSetting = {} as jest.Mocked<GetSetting>
|
||||
getSetting.execute = jest.fn().mockResolvedValue(Result.ok({ setting, decryptedValue: 'not_muted' }))
|
||||
|
||||
getUserKeyParamsUseCase = {} as jest.Mocked<GetUserKeyParams>
|
||||
getUserKeyParamsUseCase.execute = jest.fn().mockResolvedValue({ keyParams: {} })
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createEmailBackupRequestedEvent = jest.fn().mockReturnValue({} as EmailBackupRequestedEvent)
|
||||
})
|
||||
|
||||
it('publishes EmailBackupRequestedEvent', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('returns error if user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ userUuid: 'invalid-uuid' })
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
|
||||
it('returns error if user is not permitted for email backups', async () => {
|
||||
roleService.userHasPermission = jest.fn().mockResolvedValue(false)
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({ userUuid: '00000000-0000-0000-0000-000000000000' })
|
||||
|
||||
expect(result.isFailed()).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,66 +0,0 @@
|
||||
import { Result, SettingName, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { PermissionName } from '@standardnotes/features'
|
||||
|
||||
import { TriggerEmailBackupForUserDTO } from './TriggerEmailBackupForUserDTO'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import { MuteFailedBackupsEmailsOption } from '@standardnotes/settings'
|
||||
import { GetUserKeyParams } from '../GetUserKeyParams/GetUserKeyParams'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class TriggerEmailBackupForUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private roleService: RoleServiceInterface,
|
||||
private getSetting: GetSetting,
|
||||
private getUserKeyParamsUseCase: GetUserKeyParams,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: TriggerEmailBackupForUserDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const userIsPermittedForEmailBackups = await this.roleService.userHasPermission(
|
||||
userUuid.value,
|
||||
PermissionName.DailyEmailBackup,
|
||||
)
|
||||
|
||||
if (!userIsPermittedForEmailBackups) {
|
||||
return Result.fail(`User ${userUuid.value} is not permitted for email backups`)
|
||||
}
|
||||
|
||||
let userHasEmailsMuted = false
|
||||
const emailsMutedSettingOrError = await this.getSetting.execute({
|
||||
allowSensitiveRetrieval: true,
|
||||
decrypted: true,
|
||||
settingName: SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
userUuid: userUuid.value,
|
||||
})
|
||||
let emailsMutedSetting = null
|
||||
if (!emailsMutedSettingOrError.isFailed()) {
|
||||
emailsMutedSetting = emailsMutedSettingOrError.getValue()
|
||||
userHasEmailsMuted = emailsMutedSetting.decryptedValue === MuteFailedBackupsEmailsOption.Muted
|
||||
}
|
||||
|
||||
const keyParamsResponse = await this.getUserKeyParamsUseCase.execute({
|
||||
userUuid: userUuid.value,
|
||||
authenticated: false,
|
||||
})
|
||||
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createEmailBackupRequestedEvent(
|
||||
userUuid.value,
|
||||
emailsMutedSetting?.setting.id.toString() as string,
|
||||
userHasEmailsMuted,
|
||||
keyParamsResponse.keyParams,
|
||||
),
|
||||
)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface TriggerEmailBackupForUserDTO {
|
||||
userUuid: string
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
import {
|
||||
DomainEventPublisherInterface,
|
||||
EmailBackupRequestedEvent,
|
||||
MuteEmailsSettingChangedEvent,
|
||||
UserDisabledSessionUserAgentLoggingEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { EmailBackupFrequency, LogSessionUserAgentOption, MuteMarketingEmailsOption } from '@standardnotes/settings'
|
||||
import { SettingName, Result } from '@standardnotes/domain-core'
|
||||
|
||||
import { TriggerPostSettingUpdateActions } from './TriggerPostSettingUpdateActions'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
|
||||
describe('TriggerPostSettingUpdateActions', () => {
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
let triggerEmailBackupForUser: TriggerEmailBackupForUser
|
||||
|
||||
const createUseCase = () =>
|
||||
new TriggerPostSettingUpdateActions(domainEventPublisher, domainEventFactory, triggerEmailBackupForUser)
|
||||
|
||||
beforeEach(() => {
|
||||
triggerEmailBackupForUser = {} as jest.Mocked<TriggerEmailBackupForUser>
|
||||
triggerEmailBackupForUser.execute = jest.fn().mockReturnValue(Result.ok())
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createEmailBackupRequestedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<EmailBackupRequestedEvent>)
|
||||
domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<UserDisabledSessionUserAgentLoggingEvent>)
|
||||
domainEventFactory.createMuteEmailsSettingChangedEvent = jest
|
||||
.fn()
|
||||
.mockReturnValue({} as jest.Mocked<MuteEmailsSettingChangedEvent>)
|
||||
})
|
||||
|
||||
it('should trigger session cleanup if user is disabling session user agent logging', async () => {
|
||||
await createUseCase().execute({
|
||||
updatedSettingName: SettingName.NAMES.LogSessionUserAgent,
|
||||
userUuid: '4-5-6',
|
||||
userEmail: 'test@test.te',
|
||||
unencryptedValue: LogSessionUserAgentOption.Disabled,
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent).toHaveBeenCalledWith({
|
||||
userUuid: '4-5-6',
|
||||
email: 'test@test.te',
|
||||
})
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails not muted', async () => {
|
||||
await createUseCase().execute({
|
||||
updatedSettingName: SettingName.NAMES.EmailBackupFrequency,
|
||||
userUuid: '4-5-6',
|
||||
userEmail: 'test@test.te',
|
||||
unencryptedValue: EmailBackupFrequency.Daily,
|
||||
})
|
||||
|
||||
expect(triggerEmailBackupForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should trigger backup if email backup setting is created - emails muted', async () => {
|
||||
await createUseCase().execute({
|
||||
updatedSettingName: SettingName.NAMES.EmailBackupFrequency,
|
||||
userUuid: '4-5-6',
|
||||
userEmail: 'test@test.te',
|
||||
unencryptedValue: EmailBackupFrequency.Daily,
|
||||
})
|
||||
|
||||
expect(triggerEmailBackupForUser.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not trigger backup if email backup setting is disabled', async () => {
|
||||
await createUseCase().execute({
|
||||
updatedSettingName: SettingName.NAMES.EmailBackupFrequency,
|
||||
userUuid: '4-5-6',
|
||||
userEmail: 'test@test.te',
|
||||
unencryptedValue: EmailBackupFrequency.Disabled,
|
||||
})
|
||||
|
||||
expect(triggerEmailBackupForUser.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should trigger mute subscription emails rejection if mute setting changed', async () => {
|
||||
await createUseCase().execute({
|
||||
updatedSettingName: SettingName.NAMES.MuteMarketingEmails,
|
||||
userUuid: '4-5-6',
|
||||
userEmail: 'test@test.te',
|
||||
unencryptedValue: MuteMarketingEmailsOption.Muted,
|
||||
})
|
||||
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalled()
|
||||
expect(domainEventFactory.createMuteEmailsSettingChangedEvent).toHaveBeenCalledWith({
|
||||
emailSubscriptionRejectionLevel: 'MARKETING',
|
||||
mute: true,
|
||||
username: 'test@test.te',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,83 +0,0 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { EmailLevel, Result, SettingName, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
import { EmailBackupFrequency, LogSessionUserAgentOption } from '@standardnotes/settings'
|
||||
|
||||
import { TriggerPostSettingUpdateActionsDTO } from './TriggerPostSettingUpdateActionsDTO'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
|
||||
|
||||
export class TriggerPostSettingUpdateActions implements UseCaseInterface<void> {
|
||||
private readonly emailSettingToSubscriptionRejectionLevelMap: Map<string, string> = new Map([
|
||||
[SettingName.NAMES.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup],
|
||||
[SettingName.NAMES.MuteFailedCloudBackupsEmails, EmailLevel.LEVELS.FailedCloudBackup],
|
||||
[SettingName.NAMES.MuteMarketingEmails, EmailLevel.LEVELS.Marketing],
|
||||
[SettingName.NAMES.MuteSignInEmails, EmailLevel.LEVELS.SignIn],
|
||||
])
|
||||
|
||||
constructor(
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private triggerEmailBackupForUser: TriggerEmailBackupForUser,
|
||||
) {}
|
||||
|
||||
async execute(dto: TriggerPostSettingUpdateActionsDTO): Promise<Result<void>> {
|
||||
if (this.isChangingMuteEmailsSetting(dto.updatedSettingName)) {
|
||||
await this.triggerEmailSubscriptionChange(dto.userEmail, dto.updatedSettingName, dto.unencryptedValue)
|
||||
}
|
||||
|
||||
if (this.isEnablingEmailBackupSetting(dto.updatedSettingName, dto.unencryptedValue)) {
|
||||
await this.triggerEmailBackupForUser.execute({
|
||||
userUuid: dto.userUuid,
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isDisablingSessionUserAgentLogging(dto.updatedSettingName, dto.unencryptedValue)) {
|
||||
await this.triggerSessionUserAgentCleanup(dto.userEmail, dto.userUuid)
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
private isChangingMuteEmailsSetting(settingName: string): boolean {
|
||||
return [
|
||||
SettingName.NAMES.MuteFailedBackupsEmails,
|
||||
SettingName.NAMES.MuteFailedCloudBackupsEmails,
|
||||
SettingName.NAMES.MuteMarketingEmails,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
].includes(settingName)
|
||||
}
|
||||
|
||||
private isEnablingEmailBackupSetting(settingName: string, newValue: string | null): boolean {
|
||||
return (
|
||||
settingName === SettingName.NAMES.EmailBackupFrequency &&
|
||||
[EmailBackupFrequency.Daily, EmailBackupFrequency.Weekly].includes(newValue as EmailBackupFrequency)
|
||||
)
|
||||
}
|
||||
|
||||
private isDisablingSessionUserAgentLogging(settingName: string, newValue: string | null): boolean {
|
||||
return SettingName.NAMES.LogSessionUserAgent === settingName && LogSessionUserAgentOption.Disabled === newValue
|
||||
}
|
||||
|
||||
private async triggerEmailSubscriptionChange(
|
||||
userEmail: string,
|
||||
settingName: string,
|
||||
unencryptedValue: string | null,
|
||||
): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createMuteEmailsSettingChangedEvent({
|
||||
username: userEmail,
|
||||
mute: unencryptedValue === 'muted',
|
||||
emailSubscriptionRejectionLevel: this.emailSettingToSubscriptionRejectionLevelMap.get(settingName) as string,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async triggerSessionUserAgentCleanup(userEmail: string, userUuid: string) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createUserDisabledSessionUserAgentLoggingEvent({
|
||||
userUuid,
|
||||
email: userEmail,
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export interface TriggerPostSettingUpdateActionsDTO {
|
||||
updatedSettingName: string
|
||||
userUuid: string
|
||||
userEmail: string
|
||||
unencryptedValue: string | null
|
||||
}
|
||||
@@ -45,13 +45,13 @@ describe('UpdateStorageQuotaUsedForUser', () => {
|
||||
regularSubscription = {
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
subscriptionType: UserSubscriptionType.Regular,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
sharedSubscription = {
|
||||
uuid: '2-3-4',
|
||||
subscriptionType: UserSubscriptionType.Shared,
|
||||
userUuid: '123',
|
||||
user: Promise.resolve(user),
|
||||
} as jest.Mocked<UserSubscription>
|
||||
|
||||
getSharedSubscription = {} as jest.Mocked<GetSharedSubscriptionForUser>
|
||||
|
||||
@@ -56,6 +56,7 @@ export class UpdateStorageQuotaUsedForUser implements UseCaseInterface<void> {
|
||||
|
||||
private async updateUploadBytesUsedSetting(subscription: UserSubscription, bytesUsed: number): Promise<void> {
|
||||
let bytesAlreadyUsed = '0'
|
||||
const subscriptionUser = await subscription.user
|
||||
|
||||
const bytesUsedSettingExists = await this.getSubscriptionSetting.execute({
|
||||
userSubscriptionUuid: subscription.uuid,
|
||||
@@ -76,7 +77,7 @@ export class UpdateStorageQuotaUsedForUser implements UseCaseInterface<void> {
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Could not set file upload bytes used for subscription ${subscription.uuid}`)
|
||||
this.logger.error(`Could not set file upload bytes used for user ${subscriptionUser.uuid}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Column, Entity, Index, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { RevokedSession } from '../Session/RevokedSession'
|
||||
import { Role } from '../Role/Role'
|
||||
import { UserSubscription } from '../Subscription/UserSubscription'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
import { TypeORMEmergencyAccessInvitation } from '../../Infra/TypeORM/TypeORMEmergencyAccessInvitation'
|
||||
|
||||
@@ -160,6 +161,16 @@ export class User {
|
||||
})
|
||||
declare roles: Promise<Array<Role>>
|
||||
|
||||
@OneToMany(
|
||||
/* istanbul ignore next */
|
||||
() => UserSubscription,
|
||||
/* istanbul ignore next */
|
||||
(subscription) => subscription.user,
|
||||
/* istanbul ignore next */
|
||||
{ lazy: true, eager: false },
|
||||
)
|
||||
declare subscriptions: Promise<UserSubscription[]>
|
||||
|
||||
@OneToMany(
|
||||
/* istanbul ignore next */
|
||||
() => TypeORMEmergencyAccessInvitation,
|
||||
|
||||
@@ -19,8 +19,6 @@ import { SubscriptionSetting } from '../../Domain/Setting/SubscriptionSetting'
|
||||
import { SettingHttpRepresentation } from '../../Mapping/Http/SettingHttpRepresentation'
|
||||
import { SubscriptionSettingHttpRepresentation } from '../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
||||
import { GetAllSettingsForUser } from '../../Domain/UseCase/GetAllSettingsForUser/GetAllSettingsForUser'
|
||||
import { TriggerPostSettingUpdateActions } from '../../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
@controller('/users/:userUuid')
|
||||
export class AnnotatedSettingsController extends BaseSettingsController {
|
||||
@@ -28,23 +26,18 @@ export class AnnotatedSettingsController extends BaseSettingsController {
|
||||
@inject(TYPES.Auth_GetAllSettingsForUser) override doGetSettings: GetAllSettingsForUser,
|
||||
@inject(TYPES.Auth_GetSetting) override doGetSetting: GetSetting,
|
||||
@inject(TYPES.Auth_SetSettingValue) override setSettingValue: SetSettingValue,
|
||||
@inject(TYPES.Auth_TriggerPostSettingUpdateActions)
|
||||
override triggerPostSettingUpdateActions: TriggerPostSettingUpdateActions,
|
||||
@inject(TYPES.Auth_DeleteSetting) override doDeleteSetting: DeleteSetting,
|
||||
@inject(TYPES.Auth_SettingHttpMapper) settingHttMapper: MapperInterface<Setting, SettingHttpRepresentation>,
|
||||
@inject(TYPES.Auth_SubscriptionSettingHttpMapper)
|
||||
subscriptionSettingHttpMapper: MapperInterface<SubscriptionSetting, SubscriptionSettingHttpRepresentation>,
|
||||
@inject(TYPES.Auth_Logger) logger: Logger,
|
||||
) {
|
||||
super(
|
||||
doGetSettings,
|
||||
doGetSetting,
|
||||
setSettingValue,
|
||||
triggerPostSettingUpdateActions,
|
||||
doDeleteSetting,
|
||||
settingHttMapper,
|
||||
subscriptionSettingHttpMapper,
|
||||
logger,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ControllerContainerInterface, MapperInterface } from '@standardnotes/do
|
||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||
import { ErrorTag } from '@standardnotes/responses'
|
||||
import { Request, Response } from 'express'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DeleteSetting } from '../../../Domain/UseCase/DeleteSetting/DeleteSetting'
|
||||
import { GetSetting } from '../../../Domain/UseCase/GetSetting/GetSetting'
|
||||
@@ -12,21 +11,18 @@ import { Setting } from '../../../Domain/Setting/Setting'
|
||||
import { SubscriptionSetting } from '../../../Domain/Setting/SubscriptionSetting'
|
||||
import { SubscriptionSettingHttpRepresentation } from '../../../Mapping/Http/SubscriptionSettingHttpRepresentation'
|
||||
import { SettingHttpRepresentation } from '../../../Mapping/Http/SettingHttpRepresentation'
|
||||
import { TriggerPostSettingUpdateActions } from '../../../Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions'
|
||||
|
||||
export class BaseSettingsController extends BaseHttpController {
|
||||
constructor(
|
||||
protected doGetSettings: GetAllSettingsForUser,
|
||||
protected doGetSetting: GetSetting,
|
||||
protected setSettingValue: SetSettingValue,
|
||||
protected triggerPostSettingUpdateActions: TriggerPostSettingUpdateActions,
|
||||
protected doDeleteSetting: DeleteSetting,
|
||||
protected settingHttMapper: MapperInterface<Setting, SettingHttpRepresentation>,
|
||||
protected subscriptionSettingHttpMapper: MapperInterface<
|
||||
SubscriptionSetting,
|
||||
SubscriptionSettingHttpRepresentation
|
||||
>,
|
||||
protected logger: Logger,
|
||||
private controllerContainer?: ControllerContainerInterface,
|
||||
) {
|
||||
super()
|
||||
@@ -179,16 +175,6 @@ export class BaseSettingsController extends BaseHttpController {
|
||||
}
|
||||
const setting = result.getValue()
|
||||
|
||||
const triggerResult = await this.triggerPostSettingUpdateActions.execute({
|
||||
updatedSettingName: setting.props.name,
|
||||
userUuid: response.locals.user.uuid,
|
||||
userEmail: response.locals.user.email,
|
||||
unencryptedValue: value,
|
||||
})
|
||||
if (triggerResult.isFailed()) {
|
||||
this.logger.error(`Failed to trigger post setting update actions: ${triggerResult.getError()}`)
|
||||
}
|
||||
|
||||
return this.json({
|
||||
success: true,
|
||||
setting: setting.props.sensitive ? undefined : this.settingHttMapper.toProjection(setting),
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ReadStream } from 'fs'
|
||||
import { Repository } from 'typeorm'
|
||||
|
||||
import { Setting } from '../../Domain/Setting/Setting'
|
||||
@@ -12,36 +13,6 @@ export class TypeORMSettingRepository implements SettingRepositoryInterface {
|
||||
private mapper: MapperInterface<Setting, TypeORMSetting>,
|
||||
) {}
|
||||
|
||||
async countAllByNameAndValue(dto: { name: SettingName; value: string }): Promise<number> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('name = :name AND value = :value', {
|
||||
name: dto.name.value,
|
||||
value: dto.value,
|
||||
})
|
||||
.getCount()
|
||||
}
|
||||
|
||||
async findAllByNameAndValue(dto: {
|
||||
name: SettingName
|
||||
value: string
|
||||
offset: number
|
||||
limit: number
|
||||
}): Promise<Setting[]> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('name = :name AND value = :value', {
|
||||
name: dto.name.value,
|
||||
value: dto.value,
|
||||
})
|
||||
.orderBy('created_at', 'ASC')
|
||||
.take(dto.limit)
|
||||
.skip(dto.offset)
|
||||
.getMany()
|
||||
|
||||
return persistence.map((p) => this.mapper.toDomain(p))
|
||||
}
|
||||
|
||||
async insert(setting: Setting): Promise<void> {
|
||||
const persistence = this.mapper.toProjection(setting)
|
||||
|
||||
@@ -71,6 +42,27 @@ export class TypeORMSettingRepository implements SettingRepositoryInterface {
|
||||
return this.mapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async streamAllByName(name: SettingName): Promise<ReadStream> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
.where('setting.name = :name', {
|
||||
name: name.value,
|
||||
})
|
||||
.orderBy('updated_at', 'ASC')
|
||||
.stream()
|
||||
}
|
||||
|
||||
async streamAllByNameAndValue(name: SettingName, value: string): Promise<ReadStream> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
.where('setting.name = :name AND setting.value = :value', {
|
||||
name: name.value,
|
||||
value,
|
||||
})
|
||||
.orderBy('updated_at', 'ASC')
|
||||
.stream()
|
||||
}
|
||||
|
||||
async findOneByUuid(uuid: string): Promise<Setting | null> {
|
||||
const persistence = await this.ormRepository
|
||||
.createQueryBuilder('setting')
|
||||
|
||||
@@ -3,16 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.20.3...@standardnotes/domain-events-infra@1.20.4) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.20.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.20.2...@standardnotes/domain-events-infra@1.20.3) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/server/issues/903)) ([751f3b2](https://github.com/standardnotes/server/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.20.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.20.1...@standardnotes/domain-events-infra@1.20.2) (2023-10-19)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.20.4",
|
||||
"version": "1.20.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Consumer } from 'sqs-consumer'
|
||||
import { Message, SQSClient } from '@aws-sdk/client-sqs'
|
||||
import { DomainEventSubscriberInterface, DomainEventMessageHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
export class SQSDomainEventSubscriber implements DomainEventSubscriberInterface {
|
||||
constructor(
|
||||
private sqs: SQSClient,
|
||||
private queueUrl: string,
|
||||
private domainEventMessageHandler: DomainEventMessageHandlerInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
start(): void {
|
||||
const sqsConsumer = Consumer.create({
|
||||
attributeNames: ['All'],
|
||||
messageAttributeNames: ['All'],
|
||||
queueUrl: this.queueUrl,
|
||||
sqs: this.sqs,
|
||||
handleMessage: this.handleMessage.bind(this),
|
||||
})
|
||||
|
||||
sqsConsumer.on('error', this.handleError.bind(this))
|
||||
sqsConsumer.on('processing_error', this.handleError.bind(this))
|
||||
|
||||
sqsConsumer.start()
|
||||
}
|
||||
|
||||
async handleMessage(message: Message): Promise<void> {
|
||||
await this.domainEventMessageHandler.handleMessage(<string>message.Body)
|
||||
}
|
||||
|
||||
handleError(error: Error): void {
|
||||
this.logger.error('Error occured while handling SQS message: %O', error)
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ export * from './SNS/SNSDomainEventPublisher'
|
||||
export * from './SNS/SNSOpenTelemetryDomainEventPublisher'
|
||||
|
||||
export * from './SQS/SQSBounceNotificiationHandler'
|
||||
export * from './SQS/SQSDomainEventSubscriber'
|
||||
export * from './SQS/SQSDomainEventSubscriberFactory'
|
||||
export * from './SQS/SQSEventMessageHandler'
|
||||
export * from './SQS/SQSOpenTelemetryDomainEventSubscriber'
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.133.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.133.0...@standardnotes/domain-events@2.133.1) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* account deletion event ([#904](https://github.com/standardnotes/server/issues/904)) ([d66ae62](https://github.com/standardnotes/server/commit/d66ae62cf4f413cac5f6f4eac45dc0f1ddbc9e32))
|
||||
|
||||
# [2.133.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.132.3...@standardnotes/domain-events@2.133.0) (2023-10-19)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.133.1",
|
||||
"version": "2.133.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
export interface AccountDeletionRequestedEventPayload {
|
||||
userUuid: string
|
||||
email: string
|
||||
userCreatedAtTimestamp: number
|
||||
regularSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
sharedSubscription?: {
|
||||
uuid: string
|
||||
ownerUuid: string
|
||||
}
|
||||
regularSubscriptionUuid: string | undefined
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export interface FileRemovedEventPayload {
|
||||
userUuid: string
|
||||
regularSubscriptionUuid: string
|
||||
fileByteSize: number
|
||||
filePath: string
|
||||
fileName: string
|
||||
|
||||
@@ -3,14 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.13.19](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.13.18...@standardnotes/event-store@1.13.19) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.13.18](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.13.17...@standardnotes/event-store@1.13.18) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.13.17](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.13.16...@standardnotes/event-store@1.13.17) (2023-10-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.13.19",
|
||||
"version": "1.13.17",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.32.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.32.4...@standardnotes/files-server@1.32.5) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* account deletion event ([#904](https://github.com/standardnotes/files/issues/904)) ([d66ae62](https://github.com/standardnotes/files/commit/d66ae62cf4f413cac5f6f4eac45dc0f1ddbc9e32))
|
||||
|
||||
## [1.32.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.32.3...@standardnotes/files-server@1.32.4) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/files/issues/903)) ([751f3b2](https://github.com/standardnotes/files/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.32.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.32.2...@standardnotes/files-server@1.32.3) (2023-10-31)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.Files })
|
||||
sdk.start()
|
||||
|
||||
import * as busboy from 'connect-busboy'
|
||||
|
||||
import '../src/Infra/InversifyExpress/AnnotatedFallbackController'
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.FilesWorker })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.32.5",
|
||||
"version": "1.32.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -16,9 +16,9 @@ import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import {
|
||||
DirectCallDomainEventPublisher,
|
||||
DirectCallEventMessageHandler,
|
||||
SNSDomainEventPublisher,
|
||||
SQSDomainEventSubscriber,
|
||||
SNSOpenTelemetryDomainEventPublisher,
|
||||
SQSEventMessageHandler,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { StreamDownloadFile } from '../Domain/UseCase/StreamDownloadFile/StreamDownloadFile'
|
||||
import { FileDownloaderInterface } from '../Domain/Services/FileDownloaderInterface'
|
||||
@@ -52,6 +52,7 @@ import { S3FileMover } from '../Infra/S3/S3FileMover'
|
||||
import { FSFileMover } from '../Infra/FS/FSFileMover'
|
||||
import { MoveFile } from '../Domain/UseCase/MoveFile/MoveFile'
|
||||
import { SharedVaultValetTokenAuthMiddleware } from '../Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(configuration?: {
|
||||
@@ -174,7 +175,10 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventPublisherInterface>(TYPES.Files_DomainEventPublisher)
|
||||
.toConstantValue(
|
||||
new SNSDomainEventPublisher(container.get(TYPES.Files_SNS), container.get(TYPES.Files_SNS_TOPIC_ARN)),
|
||||
new SNSOpenTelemetryDomainEventPublisher(
|
||||
container.get(TYPES.Files_SNS),
|
||||
container.get(TYPES.Files_SNS_TOPIC_ARN),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -297,7 +301,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventSubscriberInterface>(TYPES.Files_DomainEventSubscriber)
|
||||
.toConstantValue(
|
||||
new SQSDomainEventSubscriber(
|
||||
new SQSOpenTelemetryDomainEventSubscriber(
|
||||
ServiceIdentifier.NAMES.FilesWorker,
|
||||
container.get<SQSClient>(TYPES.Files_SQS),
|
||||
container.get<string>(TYPES.Files_SQS_QUEUE_URL),
|
||||
container.get<DomainEventMessageHandlerInterface>(TYPES.Files_DomainEventMessageHandler),
|
||||
|
||||
@@ -18,6 +18,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
filePath: string
|
||||
fileName: string
|
||||
fileByteSize: number
|
||||
regularSubscriptionUuid: string
|
||||
}): FileRemovedEvent {
|
||||
return {
|
||||
type: 'FILE_REMOVED',
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface DomainEventFactoryInterface {
|
||||
filePath: string
|
||||
fileName: string
|
||||
fileByteSize: number
|
||||
regularSubscriptionUuid: string
|
||||
}): FileRemovedEvent
|
||||
createSharedVaultFileMovedEvent(payload: {
|
||||
fileByteSize: number
|
||||
|
||||
@@ -17,7 +17,7 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
||||
) {}
|
||||
|
||||
async handle(event: AccountDeletionRequestedEvent): Promise<void> {
|
||||
if (event.payload.regularSubscription === undefined) {
|
||||
if (event.payload.regularSubscriptionUuid === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.regularSubscriptionUuid,
|
||||
userUuid: fileRemoved.userOrSharedVaultUuid,
|
||||
filePath: fileRemoved.filePath,
|
||||
fileName: fileRemoved.fileName,
|
||||
|
||||
@@ -40,6 +40,7 @@ export class SharedSubscriptionInvitationCanceledEventHandler implements DomainE
|
||||
for (const fileRemoved of filesRemoved) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createFileRemovedEvent({
|
||||
regularSubscriptionUuid: event.payload.inviterSubscriptionUuid,
|
||||
userUuid: fileRemoved.userOrSharedVaultUuid,
|
||||
filePath: fileRemoved.filePath,
|
||||
fileName: fileRemoved.fileName,
|
||||
|
||||
@@ -36,6 +36,7 @@ export class RemoveFile implements UseCaseInterface<boolean> {
|
||||
filePath: `${dto.userInput.userUuid}/${dto.userInput.resourceRemoteIdentifier}`,
|
||||
fileName: dto.userInput.resourceRemoteIdentifier,
|
||||
fileByteSize: removedFileSize,
|
||||
regularSubscriptionUuid: dto.userInput.regularSubscriptionUuid,
|
||||
}),
|
||||
)
|
||||
} else if (dto.vaultInput !== undefined) {
|
||||
|
||||
@@ -3,26 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.18.25](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.24...@standardnotes/home-server@1.18.25) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.18.24](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.23...@standardnotes/home-server@1.18.24) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.18.23](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.22...@standardnotes/home-server@1.18.23) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.18.22](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.21...@standardnotes/home-server@1.18.22) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.18.21](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.20...@standardnotes/home-server@1.18.21) (2023-11-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.18.20](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.18.19...@standardnotes/home-server@1.18.20) (2023-11-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.18.25",
|
||||
"version": "1.18.20",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,18 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.47.5](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.47.4...@standardnotes/revisions-server@1.47.5) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* account deletion event ([#904](https://github.com/standardnotes/server/issues/904)) ([d66ae62](https://github.com/standardnotes/server/commit/d66ae62cf4f413cac5f6f4eac45dc0f1ddbc9e32))
|
||||
|
||||
## [1.47.4](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.47.3...@standardnotes/revisions-server@1.47.4) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/server/issues/903)) ([751f3b2](https://github.com/standardnotes/server/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.47.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.47.2...@standardnotes/revisions-server@1.47.3) (2023-10-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.Revisions })
|
||||
sdk.start()
|
||||
|
||||
import * as cors from 'cors'
|
||||
import { urlencoded, json, Request, Response, NextFunction } from 'express'
|
||||
import * as winston from 'winston'
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.RevisionsWorker })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.47.5",
|
||||
"version": "1.47.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
import {
|
||||
ControllerContainer,
|
||||
ControllerContainerInterface,
|
||||
MapperInterface,
|
||||
ServiceIdentifier,
|
||||
} from '@standardnotes/domain-core'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
@@ -29,7 +34,7 @@ import {
|
||||
SQSEventMessageHandler,
|
||||
DirectCallEventMessageHandler,
|
||||
DirectCallDomainEventPublisher,
|
||||
SQSDomainEventSubscriber,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { DumpRepositoryInterface } from '../Domain/Dump/DumpRepositoryInterface'
|
||||
import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler'
|
||||
@@ -337,7 +342,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventSubscriberInterface>(TYPES.Revisions_DomainEventSubscriber)
|
||||
.toConstantValue(
|
||||
new SQSDomainEventSubscriber(
|
||||
new SQSOpenTelemetryDomainEventSubscriber(
|
||||
ServiceIdentifier.NAMES.RevisionsWorker,
|
||||
container.get<SQSClient>(TYPES.Revisions_SQS),
|
||||
container.get<string>(TYPES.Revisions_SQS_QUEUE_URL),
|
||||
container.get<DomainEventMessageHandlerInterface>(TYPES.Revisions_DomainEventMessageHandler),
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let logger: Logger
|
||||
let event: AccountDeletionRequestedEvent
|
||||
|
||||
const createHandler = () => new AccountDeletionRequestedEventHandler(revisionRepository, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.removeByUserUuid = jest.fn()
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
logger.error = jest.fn()
|
||||
|
||||
event = {} as jest.Mocked<AccountDeletionRequestedEvent>
|
||||
event.createdAt = new Date(1)
|
||||
event.payload = {
|
||||
userUuid: '2-3-4',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
}
|
||||
})
|
||||
|
||||
it('should remove all revisions for a user', async () => {
|
||||
event.payload.userUuid = '84c0f8e8-544a-4c7e-9adf-26209303bc1d'
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionRepository.removeByUserUuid).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not remove all revisions for an invalid user uuid', async () => {
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionRepository.removeByUserUuid).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -3,16 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.6](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.26.5...@standardnotes/scheduler-server@1.26.6) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.26.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.26.4...@standardnotes/scheduler-server@1.26.5) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/server/issues/903)) ([751f3b2](https://github.com/standardnotes/server/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.26.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.26.3...@standardnotes/scheduler-server@1.26.4) (2023-10-26)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.SchedulerScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
@@ -29,15 +35,22 @@ void container.load().then((container) => {
|
||||
|
||||
const verifyPredicates: VerifyPredicates = container.get(TYPES.VerifyPredicates)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.SchedulerScheduledTask, 'verify')
|
||||
|
||||
Promise.resolve(verifyJobs(now, verifyPredicates))
|
||||
.then(() => {
|
||||
logger.info('Verification of overdue jobs complete.')
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Could not finish verification of overdue jobs: ${error.message}`)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.SchedulerWorker })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import { DomainEventSubscriberInterface } from '@standardnotes/domain-events'
|
||||
import * as dayjs from 'dayjs'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.4",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -15,9 +15,9 @@ import TYPES from './Types'
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import {
|
||||
SNSDomainEventPublisher,
|
||||
SQSDomainEventSubscriber,
|
||||
SNSOpenTelemetryDomainEventPublisher,
|
||||
SQSEventMessageHandler,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { PredicateRepositoryInterface } from '../Domain/Predicate/PredicateRepositoryInterface'
|
||||
@@ -35,6 +35,7 @@ import { VerifyPredicates } from '../Domain/UseCase/VerifyPredicates/VerifyPredi
|
||||
import { UserRegisteredEventHandler } from '../Domain/Handler/UserRegisteredEventHandler'
|
||||
import { SubscriptionCancelledEventHandler } from '../Domain/Handler/SubscriptionCancelledEventHandler'
|
||||
import { ExitDiscountAppliedEventHandler } from '../Domain/Handler/ExitDiscountAppliedEventHandler'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
async load(): Promise<Container> {
|
||||
@@ -135,7 +136,9 @@ export class ContainerConfigLoader {
|
||||
|
||||
container
|
||||
.bind<DomainEventPublisherInterface>(TYPES.DomainEventPublisher)
|
||||
.toConstantValue(new SNSDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)))
|
||||
.toConstantValue(
|
||||
new SNSOpenTelemetryDomainEventPublisher(container.get(TYPES.SNS), container.get(TYPES.SNS_TOPIC_ARN)),
|
||||
)
|
||||
|
||||
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
|
||||
['PREDICATE_VERIFIED', container.get(TYPES.PredicateVerifiedEventHandler)],
|
||||
@@ -150,7 +153,8 @@ export class ContainerConfigLoader {
|
||||
container
|
||||
.bind<DomainEventSubscriberInterface>(TYPES.DomainEventSubscriber)
|
||||
.toConstantValue(
|
||||
new SQSDomainEventSubscriber(
|
||||
new SQSOpenTelemetryDomainEventSubscriber(
|
||||
ServiceIdentifier.NAMES.SchedulerWorker,
|
||||
container.get<SQSClient>(TYPES.SQS),
|
||||
container.get<string>(TYPES.SQS_QUEUE_URL),
|
||||
container.get<DomainEventMessageHandlerInterface>(TYPES.DomainEventMessageHandler),
|
||||
|
||||
@@ -3,22 +3,6 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.120.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.120.2...@standardnotes/syncing-server@1.120.3) (2023-11-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.120.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.120.1...@standardnotes/syncing-server@1.120.2) (2023-11-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove open telemetry from code ([#903](https://github.com/standardnotes/syncing-server-js/issues/903)) ([751f3b2](https://github.com/standardnotes/syncing-server-js/commit/751f3b25476c5be3d663ad8540c43266acd39493))
|
||||
|
||||
## [1.120.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.120.0...@standardnotes/syncing-server@1.120.1) (2023-11-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** return cursor token upon transfer limit breached ([#902](https://github.com/standardnotes/syncing-server-js/issues/902)) ([71689c1](https://github.com/standardnotes/syncing-server-js/commit/71689c1497728569fc6a07e21fa7bdba68c1bac0))
|
||||
|
||||
# [1.120.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.119.4...@standardnotes/syncing-server@1.120.0) (2023-11-02)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.SyncingServer })
|
||||
sdk.start()
|
||||
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedFallbackController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedHealthCheckController'
|
||||
import '../src/Infra/InversifyExpressUtils/AnnotatedItemsController'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user