Compare commits

...

4 Commits

Author SHA1 Message Date
standardci
dbf532f55e chore(release): publish new version
- @standardnotes/analytics@2.27.0
 - @standardnotes/api-gateway@1.75.5
 - @standardnotes/auth-server@1.147.0
 - @standardnotes/common@1.51.0
 - @standardnotes/domain-core@1.34.0
 - @standardnotes/domain-events-infra@1.13.0
 - @standardnotes/domain-events@2.131.0
 - @standardnotes/event-store@1.12.0
 - @standardnotes/files-server@1.24.0
 - @standardnotes/home-server@1.16.7
 - @standardnotes/predicates@1.7.0
 - @standardnotes/revisions-server@1.38.0
 - @standardnotes/scheduler-server@1.21.0
 - @standardnotes/security@1.14.0
 - @standardnotes/settings@1.21.40
 - @standardnotes/syncing-server@1.109.0
 - @standardnotes/time@1.16.0
 - @standardnotes/websockets-server@1.11.0
2023-09-26 10:05:20 +00:00
Karol Sójko
ca6dbc0053 feat: refactor handling revision creation from dump (#854)
* feat: refactor handling revision creation from dump

* fix: dump repository handling
2023-09-26 11:47:41 +02:00
standardci
1bb5980b45 chore(release): publish new version
- @standardnotes/analytics@2.26.24
 - @standardnotes/api-gateway@1.75.4
 - @standardnotes/auth-server@1.146.4
 - @standardnotes/domain-core@1.33.2
 - @standardnotes/event-store@1.11.52
 - @standardnotes/files-server@1.23.2
 - @standardnotes/home-server@1.16.6
 - @standardnotes/revisions-server@1.37.3
 - @standardnotes/scheduler-server@1.20.56
 - @standardnotes/settings@1.21.39
 - @standardnotes/syncing-server@1.108.2
 - @standardnotes/websockets-server@1.10.53
2023-09-25 16:42:43 +00:00
Karol Sójko
a02a28774b fix(domain-core): notification paylod to string casting 2023-09-25 18:24:38 +02:00
58 changed files with 671 additions and 416 deletions

View File

@@ -20,7 +20,7 @@
"release": "lerna version --conventional-graduate --conventional-commits --yes -m \"chore(release): publish new version\"",
"publish": "lerna publish from-git --yes --no-verify-access --loglevel verbose",
"postversion": "./scripts/push-tags-one-by-one.sh",
"e2e": "yarn workspace @standardnotes/home-server run build && PORT=3123 yarn workspace @standardnotes/home-server start",
"e2e": "yarn build && PORT=3123 yarn workspace @standardnotes/home-server start",
"start": "yarn workspace @standardnotes/home-server run build && yarn workspace @standardnotes/home-server start"
},
"devDependencies": {

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.27.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.24...@standardnotes/analytics@2.27.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [2.26.24](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.23...@standardnotes/analytics@2.26.24) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics
## [2.26.23](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.26.22...@standardnotes/analytics@2.26.23) (2023-09-25)
**Note:** Version bump only for package @standardnotes/analytics

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.26.23",
"version": "2.27.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -18,7 +18,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js",
"report": "yarn node dist/bin/report.js",
"setup:env": "cp .env.sample .env",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.75.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.4...@standardnotes/api-gateway@1.75.5) (2023-09-26)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.3...@standardnotes/api-gateway@1.75.4) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway
## [1.75.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.75.2...@standardnotes/api-gateway@1.75.3) (2023-09-25)
**Note:** Version bump only for package @standardnotes/api-gateway

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/api-gateway",
"version": "1.75.3",
"version": "1.75.5",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.147.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.4...@standardnotes/auth-server@1.147.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.146.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.3...@standardnotes/auth-server@1.146.4) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server
## [1.146.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.146.2...@standardnotes/auth-server@1.146.3) (2023-09-25)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.146.3",
"version": "1.147.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -19,7 +19,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --fix --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"cleanup": "yarn node dist/bin/cleanup.js",

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.51.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.4...@standardnotes/common@1.51.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.50.4](https://github.com/standardnotes/server/compare/@standardnotes/common@1.50.3...@standardnotes/common@1.50.4) (2023-09-04)
**Note:** Version bump only for package @standardnotes/common

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/common",
"version": "1.50.4",
"version": "1.51.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"devDependencies": {
"@types/jest": "^29.5.1",

View File

@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.34.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.2...@standardnotes/domain-core@1.34.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.33.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.1...@standardnotes/domain-core@1.33.2) (2023-09-25)
### Bug Fixes
* **domain-core:** notification paylod to string casting ([a02a287](https://github.com/standardnotes/server/commit/a02a28774b6d500200043faefb9ebac3719e7661))
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.0...@standardnotes/domain-core@1.33.1) (2023-09-25)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-core",
"version": "1.33.1",
"version": "1.34.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -21,7 +21,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest --coverage --no-cache --passWithNoTests"
},
"dependencies": {
"uuid": "^9.0.0"

View File

@@ -15,10 +15,10 @@ export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
return JSON.stringify({
version: this.props.version,
type: this.props.type.value,
primaryIdentifier: this.props.primaryIdentifier,
primaryIndentifierType: this.props.primaryIndentifierType,
secondaryIdentifier: this.props.secondaryIdentifier,
secondaryIdentifierType: this.props.secondaryIdentifierType,
primaryIdentifier: this.props.primaryIdentifier.value,
primaryIndentifierType: this.props.primaryIndentifierType.value,
secondaryIdentifier: this.props.secondaryIdentifier?.value,
secondaryIdentifierType: this.props.secondaryIdentifierType?.value,
})
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.34...@standardnotes/domain-events-infra@1.13.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.12.34](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.33...@standardnotes/domain-events-infra@1.12.34) (2023-09-25)
**Note:** Version bump only for package @standardnotes/domain-events-infra

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events-infra",
"version": "1.12.34",
"version": "1.13.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -21,7 +21,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"dependencies": {
"@aws-sdk/client-sns": "^3.332.0",

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.131.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.130.0...@standardnotes/domain-events@2.131.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
# [2.130.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.129.1...@standardnotes/domain-events@2.130.0) (2023-09-25)
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/domain-events",
"version": "2.130.0",
"version": "2.131.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest --coverage --no-cache --passWithNoTests"
},
"dependencies": {
"@standardnotes/predicates": "workspace:*",

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.52...@standardnotes/event-store@1.12.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.11.52](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.51...@standardnotes/event-store@1.11.52) (2023-09-25)
**Note:** Version bump only for package @standardnotes/event-store
## [1.11.51](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.50...@standardnotes/event-store@1.11.51) (2023-09-25)
**Note:** Version bump only for package @standardnotes/event-store

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/event-store",
"version": "1.11.51",
"version": "1.12.0",
"description": "Event Store Service",
"private": true,
"main": "dist/src/index.js",
@@ -13,7 +13,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js"
},
"author": "Karol Sójko <karol@standardnotes.com>",

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.24.0](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.2...@standardnotes/files-server@1.24.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/files/issues/854)) ([ca6dbc0](https://github.com/standardnotes/files/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.23.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.1...@standardnotes/files-server@1.23.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/files-server
## [1.23.1](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.23.0...@standardnotes/files-server@1.23.1) (2023-09-25)
**Note:** Version bump only for package @standardnotes/files-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/files-server",
"version": "1.23.1",
"version": "1.24.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -22,7 +22,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --fix --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js"
},

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.16.7](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.6...@standardnotes/home-server@1.16.7) (2023-09-26)
**Note:** Version bump only for package @standardnotes/home-server
## [1.16.6](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.5...@standardnotes/home-server@1.16.6) (2023-09-25)
**Note:** Version bump only for package @standardnotes/home-server
## [1.16.5](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.16.4...@standardnotes/home-server@1.16.5) (2023-09-25)
**Note:** Version bump only for package @standardnotes/home-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/home-server",
"version": "1.16.5",
"version": "1.16.7",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.11...@standardnotes/predicates@1.7.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.6.11](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.6.10...@standardnotes/predicates@1.6.11) (2023-09-04)
**Note:** Version bump only for package @standardnotes/predicates

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/predicates",
"version": "1.6.11",
"version": "1.7.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -22,7 +22,7 @@
"start": "tsc -p tsconfig.json --watch",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage --passWithNoTests"
"test": "jest --coverage --no-cache --passWithNoTests"
},
"devDependencies": {
"@types/jest": "^29.5.1",

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.3...@standardnotes/revisions-server@1.38.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.37.3](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.2...@standardnotes/revisions-server@1.37.3) (2023-09-25)
**Note:** Version bump only for package @standardnotes/revisions-server
## [1.37.2](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.37.1...@standardnotes/revisions-server@1.37.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.37.2",
"version": "1.38.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js"
},

View File

@@ -71,6 +71,7 @@ import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionReq
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
import { CreateRevisionFromDump } from '../Domain/UseCase/CreateRevisionFromDump/CreateRevisionFromDump'
export class ContainerConfigLoader {
constructor(private mode: 'server' | 'worker' = 'server') {}
@@ -330,6 +331,21 @@ export class ContainerConfigLoader {
.toDynamicValue((context: interfaces.Context) => {
return new RevisionMetadataHttpMapper(context.container.get(TYPES.Revisions_GetRequiredRoleToViewRevision))
})
container
.bind<MapperInterface<Revision, string>>(TYPES.Revisions_RevisionItemStringMapper)
.toDynamicValue(() => new RevisionItemStringMapper())
container
.bind<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository)
.toConstantValue(
env.get('S3_AWS_REGION', true)
? new S3DumpRepository(
container.get(TYPES.Revisions_S3_BACKUP_BUCKET_NAME),
container.get(TYPES.Revisions_S3),
container.get(TYPES.Revisions_RevisionItemStringMapper),
)
: new FSDumpRepository(container.get(TYPES.Revisions_RevisionItemStringMapper)),
)
// use cases
container
@@ -385,6 +401,14 @@ export class ContainerConfigLoader {
: container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
),
)
container
.bind<CreateRevisionFromDump>(TYPES.Revisions_CreateRevisionFromDump)
.toConstantValue(
new CreateRevisionFromDump(
container.get<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository),
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
),
)
// env vars
container.bind(TYPES.Revisions_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
@@ -409,31 +433,12 @@ export class ContainerConfigLoader {
)
})
// Map
container
.bind<MapperInterface<Revision, string>>(TYPES.Revisions_RevisionItemStringMapper)
.toDynamicValue(() => new RevisionItemStringMapper())
container
.bind<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository)
.toConstantValue(
env.get('S3_AWS_REGION', true)
? new S3DumpRepository(
container.get(TYPES.Revisions_S3_BACKUP_BUCKET_NAME),
container.get(TYPES.Revisions_S3),
container.get(TYPES.Revisions_RevisionItemStringMapper),
container.get(TYPES.Revisions_Logger),
)
: new FSDumpRepository(container.get(TYPES.Revisions_RevisionItemStringMapper)),
)
// Handlers
container
.bind<ItemDumpedEventHandler>(TYPES.Revisions_ItemDumpedEventHandler)
.toConstantValue(
new ItemDumpedEventHandler(
container.get<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository),
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
container.get<CreateRevisionFromDump>(TYPES.Revisions_CreateRevisionFromDump),
container.get<winston.Logger>(TYPES.Revisions_Logger),
),
)

View File

@@ -49,6 +49,7 @@ const TYPES = {
'Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser',
),
Revisions_RemoveRevisionsFromSharedVault: Symbol.for('Revisions_RemoveRevisionsFromSharedVault'),
Revisions_CreateRevisionFromDump: Symbol.for('Revisions_CreateRevisionFromDump'),
// Controller
Revisions_ControllerContainer: Symbol.for('Revisions_ControllerContainer'),
Revisions_RevisionsController: Symbol.for('Revisions_RevisionsController'),

View File

@@ -1,6 +1,8 @@
import { Result } from '@standardnotes/domain-core'
import { Revision } from '../Revision/Revision'
export interface DumpRepositoryInterface {
getRevisionFromDumpPath(path: string): Promise<Revision | null>
getRevisionFromDumpPath(path: string): Promise<Result<Revision>>
removeDump(path: string): Promise<void>
}

View File

@@ -1,79 +0,0 @@
import { ItemDumpedEvent } from '@standardnotes/domain-events'
import { Logger } from 'winston'
import { Uuid, ContentType, Dates } from '@standardnotes/domain-core'
import { DumpRepositoryInterface } from '../Dump/DumpRepositoryInterface'
import { Revision } from '../Revision/Revision'
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
import { ItemDumpedEventHandler } from './ItemDumpedEventHandler'
import { RevisionRepositoryResolverInterface } from '../Revision/RevisionRepositoryResolverInterface'
describe('ItemDumpedEventHandler', () => {
let dumpRepository: DumpRepositoryInterface
let revisionRepository: RevisionRepositoryInterface
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
let revision: Revision
let event: ItemDumpedEvent
let logger: Logger
const createHandler = () => new ItemDumpedEventHandler(dumpRepository, revisionRepositoryResolver, logger)
beforeEach(() => {
revision = Revision.create({
itemUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(),
userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(),
content: 'test',
contentType: ContentType.create('Note').getValue(),
itemsKeyId: 'test',
encItemKey: 'test',
authHash: 'test',
creationDate: new Date(1),
dates: Dates.create(new Date(1), new Date(2)).getValue(),
}).getValue()
dumpRepository = {} as jest.Mocked<DumpRepositoryInterface>
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(revision)
dumpRepository.removeDump = jest.fn()
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
revisionRepository.insert = jest.fn()
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
event = {} as jest.Mocked<ItemDumpedEvent>
event.payload = {
fileDumpPath: 'foobar',
roleNames: ['CORE_USER'],
}
logger = {} as jest.Mocked<Logger>
logger.debug = jest.fn()
logger.error = jest.fn()
})
it('should save a revision from file dump', async () => {
await createHandler().handle(event)
expect(revisionRepository.insert).toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
it('should do nothing if role names are not valid', async () => {
event.payload.roleNames = ['INVALID_ROLE_NAME']
await createHandler().handle(event)
expect(revisionRepository.insert).not.toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
it('should not save a revision if it could not be created from dump', async () => {
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(revisionRepository.insert).not.toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
})

View File

@@ -1,44 +1,22 @@
import { DomainEventHandlerInterface, ItemDumpedEvent } from '@standardnotes/domain-events'
import { DumpRepositoryInterface } from '../Dump/DumpRepositoryInterface'
import { RevisionRepositoryResolverInterface } from '../Revision/RevisionRepositoryResolverInterface'
import { RoleNameCollection } from '@standardnotes/domain-core'
import { Logger } from 'winston'
import { CreateRevisionFromDump } from '../UseCase/CreateRevisionFromDump/CreateRevisionFromDump'
export class ItemDumpedEventHandler implements DomainEventHandlerInterface {
constructor(
private dumpRepository: DumpRepositoryInterface,
private revisionRepositoryResolver: RevisionRepositoryResolverInterface,
private createRevisionFromDump: CreateRevisionFromDump,
private logger: Logger,
) {}
async handle(event: ItemDumpedEvent): Promise<void> {
const revision = await this.dumpRepository.getRevisionFromDumpPath(event.payload.fileDumpPath)
if (revision === null) {
this.logger.error(`Revision not found for dump path ${event.payload.fileDumpPath}`)
const result = await this.createRevisionFromDump.execute({
filePath: event.payload.fileDumpPath,
roleNames: event.payload.roleNames,
})
await this.dumpRepository.removeDump(event.payload.fileDumpPath)
return
if (result.isFailed()) {
this.logger.error(`Item dumped event handler failed: ${result.getError()}`)
}
const roleNamesOrError = RoleNameCollection.create(event.payload.roleNames)
if (roleNamesOrError.isFailed()) {
this.logger.error(`Invalid role names ${event.payload.roleNames}`)
await this.dumpRepository.removeDump(event.payload.fileDumpPath)
return
}
const roleNames = roleNamesOrError.getValue()
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
const successfullyInserted = await revisionRepository.insert(revision)
if (!successfullyInserted) {
this.logger.error(`Could not insert revision ${revision.id.toString()}`)
}
await this.dumpRepository.removeDump(event.payload.fileDumpPath)
}
}

View File

@@ -0,0 +1,99 @@
import { Uuid, ContentType, Dates, Result } from '@standardnotes/domain-core'
import { DumpRepositoryInterface } from '../../Dump/DumpRepositoryInterface'
import { Revision } from '../../Revision/Revision'
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { CreateRevisionFromDump } from './CreateRevisionFromDump'
describe('CreateRevisionFromDump', () => {
let revisionRepository: RevisionRepositoryInterface
let revision: Revision
let dumpRepository: DumpRepositoryInterface
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
const createUseCase = () => new CreateRevisionFromDump(dumpRepository, revisionRepositoryResolver)
beforeEach(() => {
revision = Revision.create({
itemUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(),
userUuid: Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue(),
content: 'test',
contentType: ContentType.create('Note').getValue(),
itemsKeyId: 'test',
encItemKey: 'test',
authHash: 'test',
creationDate: new Date(1),
dates: Dates.create(new Date(1), new Date(2)).getValue(),
}).getValue()
dumpRepository = {} as jest.Mocked<DumpRepositoryInterface>
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(Result.ok(revision))
dumpRepository.removeDump = jest.fn()
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
revisionRepository.insert = jest.fn().mockReturnValue(true)
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
})
it('should create a revision from file dump', async () => {
const result = await createUseCase().execute({
filePath: 'foobar',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBeFalsy()
expect(revisionRepository.insert).toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
it('should fail if file path is empty', async () => {
const result = await createUseCase().execute({
filePath: '',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBeTruthy()
expect(revisionRepository.insert).not.toHaveBeenCalled()
expect(dumpRepository.removeDump).not.toHaveBeenCalled()
})
it('should fail if role name is invalid', async () => {
const result = await createUseCase().execute({
filePath: 'foobar',
roleNames: ['INVALID_ROLE_NAME'],
})
expect(result.isFailed()).toBeTruthy()
expect(revisionRepository.insert).not.toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
it('should fail if revision cannot be found', async () => {
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(Result.fail('Oops'))
const result = await createUseCase().execute({
filePath: 'foobar',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBeTruthy()
expect(revisionRepository.insert).not.toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
it('should fail if revision cannot be inserted', async () => {
revisionRepository.insert = jest.fn().mockReturnValue(false)
const result = await createUseCase().execute({
filePath: 'foobar',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBeTruthy()
expect(revisionRepository.insert).toHaveBeenCalled()
expect(dumpRepository.removeDump).toHaveBeenCalled()
})
})

View File

@@ -0,0 +1,47 @@
import { Result, RoleNameCollection, UseCaseInterface, Validator } from '@standardnotes/domain-core'
import { DumpRepositoryInterface } from '../../Dump/DumpRepositoryInterface'
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
import { CreateRevisionFromDumpDTO } from './CreateRevisionFromDumpDTO'
export class CreateRevisionFromDump implements UseCaseInterface<void> {
constructor(
private dumpRepository: DumpRepositoryInterface,
private revisionRepositoryResolver: RevisionRepositoryResolverInterface,
) {}
async execute(dto: CreateRevisionFromDumpDTO): Promise<Result<void>> {
const filePathValidationResult = Validator.isNotEmptyString(dto.filePath)
if (filePathValidationResult.isFailed()) {
return Result.fail(`Could not create revision from dump: ${filePathValidationResult.getError()}`)
}
const revisionOrError = await this.dumpRepository.getRevisionFromDumpPath(dto.filePath)
if (revisionOrError.isFailed()) {
await this.dumpRepository.removeDump(dto.filePath)
return Result.fail(`Could not create revision from dump: ${revisionOrError.getError()}`)
}
const revision = revisionOrError.getValue()
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
if (roleNamesOrError.isFailed()) {
await this.dumpRepository.removeDump(dto.filePath)
return Result.fail(`Could not create revision from dump: ${roleNamesOrError.getError()}`)
}
const roleNames = roleNamesOrError.getValue()
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
const successfullyInserted = await revisionRepository.insert(revision)
if (!successfullyInserted) {
await this.dumpRepository.removeDump(dto.filePath)
return Result.fail(`Could not insert revision from dump: ${revision.id.toString()}`)
}
await this.dumpRepository.removeDump(dto.filePath)
return Result.ok()
}
}

View File

@@ -0,0 +1,4 @@
export interface CreateRevisionFromDumpDTO {
filePath: string
roleNames: string[]
}

View File

@@ -1,4 +1,4 @@
import { MapperInterface } from '@standardnotes/domain-core'
import { MapperInterface, Result } from '@standardnotes/domain-core'
import { promises } from 'fs'
import { DumpRepositoryInterface } from '../../Domain/Dump/DumpRepositoryInterface'
@@ -7,12 +7,16 @@ import { Revision } from '../../Domain/Revision/Revision'
export class FSDumpRepository implements DumpRepositoryInterface {
constructor(private revisionStringItemMapper: MapperInterface<Revision, string>) {}
async getRevisionFromDumpPath(path: string): Promise<Revision | null> {
const contents = (await promises.readFile(path)).toString()
async getRevisionFromDumpPath(path: string): Promise<Result<Revision>> {
try {
const contents = (await promises.readFile(path)).toString()
const revision = this.revisionStringItemMapper.toDomain(contents)
const revision = this.revisionStringItemMapper.toDomain(contents)
return revision
return Result.ok(revision)
} catch (error) {
return Result.fail(`Failed to read dump file: ${(error as Error).message}`)
}
}
async removeDump(path: string): Promise<void> {

View File

@@ -1,6 +1,5 @@
import { DeleteObjectCommand, GetObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { MapperInterface } from '@standardnotes/domain-core'
import { Logger } from 'winston'
import { MapperInterface, Result } from '@standardnotes/domain-core'
import { DumpRepositoryInterface } from '../../Domain/Dump/DumpRepositoryInterface'
import { Revision } from '../../Domain/Revision/Revision'
@@ -10,26 +9,27 @@ export class S3DumpRepository implements DumpRepositoryInterface {
private dumpBucketName: string,
private s3Client: S3Client,
private revisionStringItemMapper: MapperInterface<Revision, string>,
private logger: Logger,
) {}
async getRevisionFromDumpPath(path: string): Promise<Revision | null> {
const s3Object = await this.s3Client.send(
new GetObjectCommand({
Bucket: this.dumpBucketName,
Key: path,
}),
)
async getRevisionFromDumpPath(path: string): Promise<Result<Revision>> {
try {
const s3Object = await this.s3Client.send(
new GetObjectCommand({
Bucket: this.dumpBucketName,
Key: path,
}),
)
if (s3Object.Body === undefined) {
this.logger.warn(`Could not find revision dump at path: ${path}`)
if (s3Object.Body === undefined) {
return Result.fail(`Could not find revision dump at path: ${path}`)
}
return null
const revision = this.revisionStringItemMapper.toDomain(await s3Object.Body.transformToString())
return Result.ok(revision)
} catch (error) {
return Result.fail(`Failed to read dump file: ${(error as Error).message}`)
}
const revision = this.revisionStringItemMapper.toDomain(await s3Object.Body.transformToString())
return revision
}
async removeDump(path: string): Promise<void> {

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.56...@standardnotes/scheduler-server@1.21.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.20.56](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.55...@standardnotes/scheduler-server@1.20.56) (2023-09-25)
**Note:** Version bump only for package @standardnotes/scheduler-server
## [1.20.55](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.54...@standardnotes/scheduler-server@1.20.55) (2023-09-25)
**Note:** Version bump only for package @standardnotes/scheduler-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/scheduler-server",
"version": "1.20.55",
"version": "1.21.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -16,7 +16,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"worker": "yarn node dist/bin/worker.js",
"verify:jobs": "yarn node dist/bin/verify.js",
"setup:env": "cp .env.sample .env",

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.14.0](https://github.com/standardnotes/server/compare/@standardnotes/security@1.13.1...@standardnotes/security@1.14.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.13.1](https://github.com/standardnotes/server/compare/@standardnotes/security@1.13.0...@standardnotes/security@1.13.1) (2023-09-12)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/security",
"version": "1.13.1",
"version": "1.14.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -22,7 +22,7 @@
"start": "tsc -p tsconfig.json --watch",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"dependencies": {
"jsonwebtoken": "^9.0.0",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.21.40](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.39...@standardnotes/settings@1.21.40) (2023-09-26)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.39](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.38...@standardnotes/settings@1.21.39) (2023-09-25)
**Note:** Version bump only for package @standardnotes/settings
## [1.21.38](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.37...@standardnotes/settings@1.21.38) (2023-09-25)
**Note:** Version bump only for package @standardnotes/settings

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/settings",
"version": "1.21.38",
"version": "1.21.40",
"engines": {
"node": ">=18.0.0 <21.0.0"
},

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.109.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.108.2...@standardnotes/syncing-server@1.109.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/syncing-server-js/issues/854)) ([ca6dbc0](https://github.com/standardnotes/syncing-server-js/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.108.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.108.1...@standardnotes/syncing-server@1.108.2) (2023-09-25)
**Note:** Version bump only for package @standardnotes/syncing-server
## [1.108.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.108.0...@standardnotes/syncing-server@1.108.1) (2023-09-25)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.108.1",
"version": "1.109.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"lint": "eslint . --ext .ts",
"lint:fix": "eslint . --ext .ts --fix",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"content-size": "yarn node dist/bin/content.js",

View File

@@ -175,6 +175,7 @@ import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferShar
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
import { TransferSharedVaultItems } from '../Domain/UseCase/SharedVaults/TransferSharedVaultItems/TransferSharedVaultItems'
import { DumpItem } from '../Domain/UseCase/Syncing/DumpItem/DumpItem'
export class ContainerConfigLoader {
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
@@ -590,6 +591,24 @@ export class ContainerConfigLoader {
]),
)
container
.bind<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService)
.toConstantValue(
env.get('S3_AWS_REGION', true)
? new S3ItemBackupService(
container.get(TYPES.Sync_S3_BACKUP_BUCKET_NAME),
container.get(TYPES.Sync_ItemBackupMapper),
container.get(TYPES.Sync_ItemHttpMapper),
container.get(TYPES.Sync_Logger),
container.get(TYPES.Sync_S3),
)
: new FSItemBackupService(
container.get(TYPES.Sync_FILE_UPLOAD_PATH),
container.get(TYPES.Sync_ItemBackupMapper),
container.get(TYPES.Sync_Logger),
),
)
// use cases
container
.bind<GetItems>(TYPES.Sync_GetItems)
@@ -932,6 +951,16 @@ export class ContainerConfigLoader {
container.get<DeleteSharedVault>(TYPES.Sync_DeleteSharedVault),
),
)
container
.bind<DumpItem>(TYPES.Sync_DumpItem)
.toConstantValue(
new DumpItem(
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
),
)
// Services
container
@@ -959,24 +988,6 @@ export class ContainerConfigLoader {
)
})
container
.bind<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService)
.toConstantValue(
env.get('S3_AWS_REGION', true)
? new S3ItemBackupService(
container.get(TYPES.Sync_S3_BACKUP_BUCKET_NAME),
container.get(TYPES.Sync_ItemBackupMapper),
container.get(TYPES.Sync_ItemHttpMapper),
container.get(TYPES.Sync_Logger),
container.get(TYPES.Sync_S3),
)
: new FSItemBackupService(
container.get(TYPES.Sync_FILE_UPLOAD_PATH),
container.get(TYPES.Sync_ItemBackupMapper),
container.get(TYPES.Sync_Logger),
),
)
// Handlers
container
.bind<DuplicateItemSyncedEventHandler>(TYPES.Sync_DuplicateItemSyncedEventHandler)
@@ -1002,10 +1013,8 @@ export class ContainerConfigLoader {
.bind<ItemRevisionCreationRequestedEventHandler>(TYPES.Sync_ItemRevisionCreationRequestedEventHandler)
.toConstantValue(
new ItemRevisionCreationRequestedEventHandler(
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
container.get<DumpItem>(TYPES.Sync_DumpItem),
container.get<Logger>(TYPES.Sync_Logger),
),
)
container

View File

@@ -92,6 +92,7 @@ const TYPES = {
Sync_RemoveUserFromSharedVaults: Symbol.for('Sync_RemoveUserFromSharedVaults'),
Sync_TransferSharedVault: Symbol.for('Sync_TransferSharedVault'),
Sync_TransferSharedVaultItems: Symbol.for('Sync_TransferSharedVaultItems'),
Sync_DumpItem: Symbol.for('Sync_DumpItem'),
// Handlers
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),

View File

@@ -1,125 +0,0 @@
import 'reflect-metadata'
import {
DomainEventPublisherInterface,
DomainEventService,
ItemRevisionCreationRequestedEvent,
} from '@standardnotes/domain-events'
import { Item } from '../Item/Item'
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
import { ItemRevisionCreationRequestedEventHandler } from './ItemRevisionCreationRequestedEventHandler'
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
import { ItemRepositoryResolverInterface } from '../Item/ItemRepositoryResolverInterface'
describe('ItemRevisionCreationRequestedEventHandler', () => {
let itemRepositoryResolver: ItemRepositoryResolverInterface
let itemRepository: ItemRepositoryInterface
let event: ItemRevisionCreationRequestedEvent
let item: Item
let itemBackupService: ItemBackupServiceInterface
let domainEventFactory: DomainEventFactoryInterface
let domainEventPublisher: DomainEventPublisherInterface
const createHandler = () =>
new ItemRevisionCreationRequestedEventHandler(
itemRepositoryResolver,
itemBackupService,
domainEventFactory,
domainEventPublisher,
)
beforeEach(() => {
item = Item.create(
{
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
updatedWithSession: null,
content: 'foobar1',
contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
encItemKey: null,
authHash: null,
itemsKeyId: null,
duplicateOf: null,
deleted: false,
dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
},
new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
).getValue()
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
itemRepository.findByUuid = jest.fn().mockReturnValue(item)
itemRepositoryResolver = {} as jest.Mocked<ItemRepositoryResolverInterface>
itemRepositoryResolver.resolve = jest.fn().mockReturnValue(itemRepository)
event = {} as jest.Mocked<ItemRevisionCreationRequestedEvent>
event.createdAt = new Date(1)
event.payload = {
itemUuid: '00000000-0000-0000-0000-000000000000',
roleNames: ['CORE_USER'],
}
event.meta = {
correlation: {
userIdentifier: '1-2-3',
userIdentifierType: 'uuid',
},
origin: DomainEventService.SyncingServer,
}
itemBackupService = {} as jest.Mocked<ItemBackupServiceInterface>
itemBackupService.dump = jest.fn().mockReturnValue('foo://bar')
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createItemDumpedEvent = jest.fn()
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
})
it('should create a revision for an item', async () => {
await createHandler().handle(event)
expect(domainEventPublisher.publish).toHaveBeenCalled()
expect(domainEventFactory.createItemDumpedEvent).toHaveBeenCalled()
})
it('should do nothing if roles names are not valid', async () => {
event.payload.roleNames = ['INVALID_ROLE_NAME']
await createHandler().handle(event)
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(domainEventFactory.createItemDumpedEvent).not.toHaveBeenCalled()
})
it('should not create a revision for an item that does not exist', async () => {
itemRepository.findByUuid = jest.fn().mockReturnValue(null)
itemRepositoryResolver.resolve = jest.fn().mockReturnValue(itemRepository)
await createHandler().handle(event)
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
})
it('should not create a revision for an item if the dump was not created', async () => {
itemBackupService.dump = jest.fn().mockReturnValue('')
await createHandler().handle(event)
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(domainEventFactory.createItemDumpedEvent).not.toHaveBeenCalled()
})
it('should not create a revision if the item uuid is invalid', async () => {
event.payload.itemUuid = 'invalid-uuid'
await createHandler().handle(event)
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
expect(domainEventFactory.createItemDumpedEvent).not.toHaveBeenCalled()
})
})

View File

@@ -1,59 +1,22 @@
import {
ItemRevisionCreationRequestedEvent,
DomainEventHandlerInterface,
DomainEventPublisherInterface,
} from '@standardnotes/domain-events'
import { RoleNameCollection, Uuid } from '@standardnotes/domain-core'
import { ItemRevisionCreationRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface'
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
import { ItemRepositoryResolverInterface } from '../Item/ItemRepositoryResolverInterface'
import { DumpItem } from '../UseCase/Syncing/DumpItem/DumpItem'
import { Logger } from 'winston'
export class ItemRevisionCreationRequestedEventHandler implements DomainEventHandlerInterface {
constructor(
private itemRepositoryResolver: ItemRepositoryResolverInterface,
private itemBackupService: ItemBackupServiceInterface,
private domainEventFactory: DomainEventFactoryInterface,
private domainEventPublisher: DomainEventPublisherInterface,
private dumpItem: DumpItem,
private logger: Logger,
) {}
async handle(event: ItemRevisionCreationRequestedEvent): Promise<void> {
const roleNamesOrError = RoleNameCollection.create(event.payload.roleNames)
if (roleNamesOrError.isFailed()) {
return
}
const roleNames = roleNamesOrError.getValue()
const result = await this.dumpItem.execute({
itemUuid: event.payload.itemUuid,
roleNames: event.payload.roleNames,
})
const itemRepository = this.itemRepositoryResolver.resolve(roleNames)
await this.createItemDump(event, itemRepository)
}
private async createItemDump(
event: ItemRevisionCreationRequestedEvent,
itemRepository: ItemRepositoryInterface,
): Promise<void> {
const itemUuidOrError = Uuid.create(event.payload.itemUuid)
if (itemUuidOrError.isFailed()) {
return
}
const itemUuid = itemUuidOrError.getValue()
const item = await itemRepository.findByUuid(itemUuid)
if (item === null) {
return
}
const fileDumpPath = await this.itemBackupService.dump(item)
if (fileDumpPath) {
await this.domainEventPublisher.publish(
this.domainEventFactory.createItemDumpedEvent({
fileDumpPath,
userUuid: event.meta.correlation.userIdentifier,
roleNames: event.payload.roleNames,
}),
)
if (result.isFailed()) {
this.logger.error(`Item revision requested handler failed: ${result.getError()}`)
}
}
}

View File

@@ -1,7 +1,9 @@
import { KeyParamsData } from '@standardnotes/responses'
import { Result } from '@standardnotes/domain-core'
import { Item } from './Item'
export interface ItemBackupServiceInterface {
backup(items: Array<Item>, authParams: KeyParamsData, contentSizeLimit?: number): Promise<string[]>
dump(item: Item): Promise<string>
dump(item: Item): Promise<Result<string>>
}

View File

@@ -0,0 +1,117 @@
import { DomainEventInterface, DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
import { ItemBackupServiceInterface } from '../../../Item/ItemBackupServiceInterface'
import { ItemRepositoryResolverInterface } from '../../../Item/ItemRepositoryResolverInterface'
import { DumpItem } from './DumpItem'
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
import { Item } from '../../../Item/Item'
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId, Result } from '@standardnotes/domain-core'
describe('DumpItem', () => {
let itemRepositoryResolver: ItemRepositoryResolverInterface
let itemRepository: ItemRepositoryInterface
let item: Item
let itemBackupService: ItemBackupServiceInterface
let domainEventFactory: DomainEventFactoryInterface
let domainEventPublisher: DomainEventPublisherInterface
const createUseCase = () =>
new DumpItem(itemRepositoryResolver, itemBackupService, domainEventFactory, domainEventPublisher)
beforeEach(() => {
item = Item.create(
{
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
updatedWithSession: null,
content: 'foobar',
contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
encItemKey: null,
authHash: null,
itemsKeyId: null,
duplicateOf: null,
deleted: false,
dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
},
new UniqueEntityId('00000000-0000-0000-0000-000000000000'),
).getValue()
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
itemRepository.findByUuid = jest.fn().mockResolvedValue(item)
itemRepositoryResolver = {} as jest.Mocked<ItemRepositoryResolverInterface>
itemRepositoryResolver.resolve = jest.fn().mockReturnValue(itemRepository)
itemBackupService = {} as jest.Mocked<ItemBackupServiceInterface>
itemBackupService.dump = jest.fn().mockResolvedValue(Result.ok('dump-path'))
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
domainEventFactory.createItemDumpedEvent = jest.fn().mockReturnValue({} as jest.Mocked<DomainEventInterface>)
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
domainEventPublisher.publish = jest.fn()
})
it('should dump item', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(false)
expect(itemBackupService.dump).toHaveBeenCalled()
expect(domainEventPublisher.publish).toHaveBeenCalled()
})
it('should fail if item cannot be found', async () => {
itemRepository.findByUuid = jest.fn().mockResolvedValue(null)
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should fail if item cannot be dumped', async () => {
itemBackupService.dump = jest.fn().mockResolvedValue(Result.fail('error'))
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should fail if item uuid is invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: 'invalid-uuid',
roleNames: ['CORE_USER'],
})
expect(result.isFailed()).toBe(true)
})
it('should fail if role names are invalid', async () => {
const useCase = createUseCase()
const result = await useCase.execute({
itemUuid: '00000000-0000-0000-0000-000000000000',
roleNames: ['invalid-role'],
})
expect(result.isFailed()).toBe(true)
})
})

View File

@@ -0,0 +1,63 @@
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
import { DumpItemDTO } from './DumpItemDTO'
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
import { ItemBackupServiceInterface } from '../../../Item/ItemBackupServiceInterface'
import { ItemRepositoryInterface } from '../../../Item/ItemRepositoryInterface'
import { ItemRepositoryResolverInterface } from '../../../Item/ItemRepositoryResolverInterface'
export class DumpItem implements UseCaseInterface<void> {
constructor(
private itemRepositoryResolver: ItemRepositoryResolverInterface,
private itemBackupService: ItemBackupServiceInterface,
private domainEventFactory: DomainEventFactoryInterface,
private domainEventPublisher: DomainEventPublisherInterface,
) {}
async execute(dto: DumpItemDTO): Promise<Result<void>> {
const itemUuidOrError = Uuid.create(dto.itemUuid)
if (itemUuidOrError.isFailed()) {
return Result.fail(`Failed to dump item: ${itemUuidOrError.getError()}`)
}
const itemUuid = itemUuidOrError.getValue()
const itemRepositoryOrError = this.getItemRepository(dto.roleNames)
if (itemRepositoryOrError.isFailed()) {
return Result.fail(`Failed to dump item: ${itemRepositoryOrError.getError()}`)
}
const itemRepository = itemRepositoryOrError.getValue()
const item = await itemRepository.findByUuid(itemUuid)
if (item === null) {
return Result.fail('Failed to dump item: Item not found')
}
const fileDumpPathOrError = await this.itemBackupService.dump(item)
if (fileDumpPathOrError.isFailed()) {
return Result.fail(`Failed to dump item: ${fileDumpPathOrError.getError()}`)
}
const fileDumpPath = fileDumpPathOrError.getValue()
await this.domainEventPublisher.publish(
this.domainEventFactory.createItemDumpedEvent({
fileDumpPath,
userUuid: item.props.userUuid.value,
roleNames: dto.roleNames,
}),
)
return Result.ok()
}
private getItemRepository(stringRoleNames: string[]): Result<ItemRepositoryInterface> {
const roleNamesOrError = RoleNameCollection.create(stringRoleNames)
if (roleNamesOrError.isFailed()) {
return Result.fail(`Could not obtain item repository based on role names: ${roleNamesOrError.getError()}`)
}
const roleNames = roleNamesOrError.getValue()
const itemRepository = this.itemRepositoryResolver.resolve(roleNames)
return Result.ok(itemRepository)
}
}

View File

@@ -0,0 +1,4 @@
export interface DumpItemDTO {
itemUuid: string
roleNames: string[]
}

View File

@@ -1,5 +1,5 @@
import { KeyParamsData } from '@standardnotes/responses'
import { MapperInterface } from '@standardnotes/domain-core'
import { MapperInterface, Result } from '@standardnotes/domain-core'
import { promises } from 'fs'
import * as uuid from 'uuid'
import { Logger } from 'winston'
@@ -20,25 +20,29 @@ export class FSItemBackupService implements ItemBackupServiceInterface {
throw new Error('Method not implemented.')
}
async dump(item: Item): Promise<string> {
const contents = JSON.stringify({
item: this.mapper.toProjection(item),
})
async dump(item: Item): Promise<Result<string>> {
try {
const contents = JSON.stringify({
item: this.mapper.toProjection(item),
})
const path = `${this.fileUploadPath}/dumps/${uuid.v4()}`
const path = `${this.fileUploadPath}/dumps/${uuid.v4()}`
this.logger.debug(`Dumping item ${item.id.toString()} to ${path}`)
this.logger.debug(`Dumping item ${item.id.toString()} to ${path}`)
await promises.mkdir(dirname(path), { recursive: true })
await promises.mkdir(dirname(path), { recursive: true })
await promises.writeFile(path, contents)
await promises.writeFile(path, contents)
const fileCreated = (await promises.stat(path)).isFile()
const fileCreated = (await promises.stat(path)).isFile()
if (!fileCreated) {
throw new Error(`Could not create dump file ${path}`)
if (!fileCreated) {
return Result.fail(`Could not create dump file ${path}`)
}
return Result.ok(path)
} catch (error) {
return Result.fail(`Could not dump item: ${(error as Error).message}`)
}
return path
}
}

View File

@@ -5,7 +5,7 @@ import { Logger } from 'winston'
import { Item } from '../../Domain/Item/Item'
import { ItemBackupServiceInterface } from '../../Domain/Item/ItemBackupServiceInterface'
import { MapperInterface } from '@standardnotes/domain-core'
import { MapperInterface, Result } from '@standardnotes/domain-core'
import { ItemBackupRepresentation } from '../../Mapping/Backup/ItemBackupRepresentation'
import { ItemHttpRepresentation } from '../../Mapping/Http/ItemHttpRepresentation'
@@ -18,25 +18,29 @@ export class S3ItemBackupService implements ItemBackupServiceInterface {
private s3Client?: S3Client,
) {}
async dump(item: Item): Promise<string> {
if (!this.s3BackupBucketName || this.s3Client === undefined) {
this.logger.warn('S3 backup not configured')
async dump(item: Item): Promise<Result<string>> {
try {
if (!this.s3BackupBucketName || this.s3Client === undefined) {
this.logger.warn('S3 backup not configured')
return ''
}
return Result.fail('S3 backup not configured')
}
const s3Key = uuid.v4()
await this.s3Client.send(
new PutObjectCommand({
Bucket: this.s3BackupBucketName,
Key: s3Key,
Body: JSON.stringify({
item: this.backupMapper.toProjection(item),
const s3Key = uuid.v4()
await this.s3Client.send(
new PutObjectCommand({
Bucket: this.s3BackupBucketName,
Key: s3Key,
Body: JSON.stringify({
item: this.backupMapper.toProjection(item),
}),
}),
}),
)
)
return s3Key
return Result.ok(s3Key)
} catch (error) {
return Result.fail(`Could not dump item: ${(error as Error).message}`)
}
}
async backup(items: Item[], authParams: KeyParamsData, contentSizeLimit?: number): Promise<string[]> {

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/time@1.15.3...@standardnotes/time@1.16.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.15.3](https://github.com/standardnotes/server/compare/@standardnotes/time@1.15.2...@standardnotes/time@1.15.3) (2023-09-04)
**Note:** Version bump only for package @standardnotes/time

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/time",
"version": "1.15.3",
"version": "1.16.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -20,7 +20,7 @@
"clean": "rm -fr dist",
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"test": "jest spec --coverage"
"test": "jest --coverage --no-cache"
},
"dependencies": {
"dayjs": "^1.11.6",

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.53...@standardnotes/websockets-server@1.11.0) (2023-09-26)
### Features
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
## [1.10.53](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.52...@standardnotes/websockets-server@1.10.53) (2023-09-25)
**Note:** Version bump only for package @standardnotes/websockets-server
## [1.10.52](https://github.com/standardnotes/server/compare/@standardnotes/websockets-server@1.10.51...@standardnotes/websockets-server@1.10.52) (2023-09-25)
**Note:** Version bump only for package @standardnotes/websockets-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/websockets-server",
"version": "1.10.52",
"version": "1.11.0",
"engines": {
"node": ">=18.0.0 <21.0.0"
},
@@ -16,7 +16,7 @@
"build": "tsc --build",
"lint": "eslint . --ext .ts",
"pretest": "yarn lint && yarn build",
"test": "jest --coverage --config=./jest.config.js --maxWorkers=50%",
"test": "jest --coverage --no-cache --config=./jest.config.js --maxWorkers=50%",
"start": "yarn node dist/bin/server.js",
"worker": "yarn node dist/bin/worker.js",
"typeorm": "typeorm-ts-node-commonjs"