mirror of
https://github.com/standardnotes/server
synced 2026-01-21 08:04:27 -05:00
Compare commits
45 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2396053bc1 | ||
|
|
17fd12305e | ||
|
|
425ea4374d | ||
|
|
c076c3c74a | ||
|
|
547cdfd8ec | ||
|
|
a0af8f0025 | ||
|
|
c970b1ea68 | ||
|
|
4d1e2dec26 | ||
|
|
108408a944 | ||
|
|
18d07d431f | ||
|
|
cbc024f67a | ||
|
|
55ec5970da | ||
|
|
58bdca6659 | ||
|
|
ef49b0d3f8 | ||
|
|
9cb691e5ad | ||
|
|
04d09582d4 | ||
|
|
8f90dc172b | ||
|
|
f759261919 | ||
|
|
2606f6d929 | ||
|
|
c288e5d8dc | ||
|
|
4b76d4b71e | ||
|
|
72310130d2 | ||
|
|
f9e51ef06e | ||
|
|
92a5eb0d98 | ||
|
|
77d2ea1a1f | ||
|
|
92f96ddb84 | ||
|
|
15a914e25e | ||
|
|
912a29d091 | ||
|
|
b2c32ce70e | ||
|
|
ed1a708c40 | ||
|
|
e905128d45 | ||
|
|
fd598f372a | ||
|
|
7a3946a9e2 | ||
|
|
cbdd2584d0 | ||
|
|
f3161c2712 | ||
|
|
148542dd5a | ||
|
|
d2b2c339f2 | ||
|
|
d2578c48f0 | ||
|
|
fecfd54728 | ||
|
|
17e4162d3e | ||
|
|
742209d773 | ||
|
|
1fa4b7cf27 | ||
|
|
5dc5507039 | ||
|
|
3035a20b9f | ||
|
|
04b3bb034f |
94
.pnp.cjs
generated
94
.pnp.cjs
generated
@@ -4560,17 +4560,16 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/api", [\
|
||||
["npm:1.26.10", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.10-f6165cafd3-3c3561aec8.zip/node_modules/@standardnotes/api/",\
|
||||
["npm:1.26.26", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.26.26-4338a5fe92-db41aedfa3.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/encryption", "npm:1.21.38"],\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/models", "npm:1.46.8"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/utils", "npm:1.17.5"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4635,17 +4634,17 @@ const RAW_RUNTIME_STATE =
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@simplewebauthn/server", "npm:7.2.0"],\
|
||||
["@simplewebauthn/typescript-types", "npm:7.0.0"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/features", "npm:1.59.5"],\
|
||||
["@standardnotes/features", "npm:1.59.7"],\
|
||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/bcryptjs", "npm:2.4.2"],\
|
||||
@@ -4781,21 +4780,6 @@ const RAW_RUNTIME_STATE =
|
||||
"linkType": "SOFT"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/encryption", [\
|
||||
["npm:1.21.38", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.21.38-d08c3d4766-1393840523.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.21.38"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/event-store", [\
|
||||
["workspace:packages/event-store", {\
|
||||
"packageLocation": "./packages/event-store/",\
|
||||
@@ -4831,10 +4815,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/features", [\
|
||||
["npm:1.59.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.59.5-83c83acde9-173b1f5d52.zip/node_modules/@standardnotes/features/",\
|
||||
["npm:1.59.7", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.59.7-27c3e5296e-1632d64cc1.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.59.5"],\
|
||||
["@standardnotes/features", "npm:1.59.7"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
@@ -4855,7 +4839,7 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/connect-busboy", "npm:1.0.0"],\
|
||||
@@ -4935,14 +4919,16 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/models", [\
|
||||
["npm:1.45.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.45.5-29326e959c-15f26c11b2.zip/node_modules/@standardnotes/models/",\
|
||||
["npm:1.46.8", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.46.8-bc0390832e-8404340f27.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.45.5"],\
|
||||
["@standardnotes/models", "npm:1.46.8"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.59.5"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/features", "npm:1.59.7"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["@standardnotes/utils", "npm:1.17.5"],\
|
||||
["lodash", "npm:4.17.21"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -4967,12 +4953,12 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/responses", [\
|
||||
["npm:1.13.24", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.24-3b4167c7ea-3bcfee90f0.zip/node_modules/@standardnotes/responses/",\
|
||||
["npm:1.13.27", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.13.27-829dec3e6e-9bf55e5f02.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.59.5"],\
|
||||
["@standardnotes/features", "npm:1.59.7"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
@@ -4987,12 +4973,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-s3", "npm:3.342.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
@@ -5129,10 +5115,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/sncrypto-common", [\
|
||||
["npm:1.13.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.3-97ef3850ce-a73af90962.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
["npm:1.13.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.4-3186513fa6-48e0e207f2.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -5143,7 +5129,7 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/sncrypto-node/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.3"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.4"],\
|
||||
["@types/jest", "npm:29.5.2"],\
|
||||
["@types/node", "npm:20.2.5"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.59.8"],\
|
||||
@@ -5171,12 +5157,12 @@ const RAW_RUNTIME_STATE =
|
||||
["@aws-sdk/client-sns", "npm:3.342.0"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/settings", "workspace:packages/settings"],\
|
||||
["@standardnotes/sncrypto-node", "workspace:packages/sncrypto-node"],\
|
||||
@@ -5247,10 +5233,10 @@ const RAW_RUNTIME_STATE =
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/utils", [\
|
||||
["npm:1.16.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.16.5-47f537f49f-d5caa7181f.zip/node_modules/@standardnotes/utils/",\
|
||||
["npm:1.17.5", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.17.5-210b60222d-47e8520174.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/utils", "npm:1.17.5"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.5"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
@@ -5266,14 +5252,14 @@ const RAW_RUNTIME_STATE =
|
||||
["@standardnotes/websockets-server", "workspace:packages/websockets"],\
|
||||
["@aws-sdk/client-sqs", "npm:3.342.0"],\
|
||||
["@newrelic/winston-enricher", "virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:4.0.1"],\
|
||||
["@standardnotes/api", "npm:1.26.10"],\
|
||||
["@standardnotes/api", "npm:1.26.26"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/responses", "npm:1.13.24"],\
|
||||
["@standardnotes/responses", "npm:1.13.27"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.16.5"],\
|
||||
["@standardnotes/utils", "npm:1.17.5"],\
|
||||
["@types/cors", "npm:2.8.13"],\
|
||||
["@types/express", "npm:4.17.17"],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
|
||||
Binary file not shown.
BIN
.yarn/cache/@standardnotes-api-npm-1.26.26-4338a5fe92-db41aedfa3.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-api-npm-1.26.26-4338a5fe92-db41aedfa3.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-models-npm-1.46.8-bc0390832e-8404340f27.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-models-npm-1.46.8-bc0390832e-8404340f27.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/@standardnotes-responses-npm-1.13.27-829dec3e6e-9bf55e5f02.zip
vendored
Normal file
BIN
.yarn/cache/@standardnotes-responses-npm-1.13.27-829dec3e6e-9bf55e5f02.zip
vendored
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.24.9](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.8...@standardnotes/analytics@2.24.9) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.8](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.7...@standardnotes/analytics@2.24.8) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.7](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.6...@standardnotes/analytics@2.24.7) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.6](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.5...@standardnotes/analytics@2.24.6) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.24.5](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.24.4...@standardnotes/analytics@2.24.5) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.24.5",
|
||||
"version": "2.24.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.65.6](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.5...@standardnotes/api-gateway@1.65.6) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.5](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.4...@standardnotes/api-gateway@1.65.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.3...@standardnotes/api-gateway@1.65.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.2...@standardnotes/api-gateway@1.65.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.65.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.65.1...@standardnotes/api-gateway@1.65.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.65.2",
|
||||
"version": "1.65.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,42 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.123.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.123.1...@standardnotes/auth-server@1.123.2) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.123.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.123.0...@standardnotes/auth-server@1.123.1) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.123.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.2...@standardnotes/auth-server@1.123.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.122.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.1...@standardnotes/auth-server@1.122.2) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
## [1.122.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.122.0...@standardnotes/auth-server@1.122.1) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.122.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.121.0...@standardnotes/auth-server@1.122.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.121.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.2...@standardnotes/auth-server@1.121.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **auth:** add notifications model ([#638](https://github.com/standardnotes/server/issues/638)) ([fecfd54](https://github.com/standardnotes/server/commit/fecfd5472824b5adae708db95d351e4ad65ee87b))
|
||||
|
||||
## [1.120.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.120.1...@standardnotes/auth-server@1.120.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
BIN
packages/auth/database.sqlite
Normal file
BIN
packages/auth/database.sqlite
Normal file
Binary file not shown.
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540448427 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540448427'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540448428 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540448428'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540623272 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540623272'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveNotifications1688540623273 implements MigrationInterface {
|
||||
name = 'RemoveNotifications1688540623273'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.120.2",
|
||||
"version": "1.123.2",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -42,17 +42,17 @@
|
||||
"@cbor-extract/cbor-extract-linux-x64": "^2.1.1",
|
||||
"@simplewebauthn/server": "^7.2.0",
|
||||
"@simplewebauthn/typescript-types": "^7.0.0",
|
||||
"@standardnotes/api": "^1.25.3",
|
||||
"@standardnotes/api": "^1.26.26",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
"@standardnotes/features": "^1.58.12",
|
||||
"@standardnotes/features": "^1.59.7",
|
||||
"@standardnotes/predicates": "workspace:*",
|
||||
"@standardnotes/responses": "^1.13.9",
|
||||
"@standardnotes/responses": "^1.13.27",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-common": "^1.13.4",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"axios": "^1.1.3",
|
||||
|
||||
@@ -20,19 +20,23 @@ import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
|
||||
export class AppDataSource {
|
||||
private dataSource: DataSource | undefined
|
||||
private _dataSource: DataSource | undefined
|
||||
|
||||
constructor(private env: Env) {}
|
||||
|
||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||
if (!this.dataSource) {
|
||||
if (!this._dataSource) {
|
||||
throw new Error('DataSource not initialized')
|
||||
}
|
||||
|
||||
return this.dataSource.getRepository(target)
|
||||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
this.env.load()
|
||||
|
||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||
@@ -104,7 +108,7 @@ export class AppDataSource {
|
||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
@@ -112,9 +116,9 @@ export class AppDataSource {
|
||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
await this.dataSource.initialize()
|
||||
return this._dataSource
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/auth/src/Bootstrap/MigrationsDataSource.ts
Normal file
7
packages/auth/src/Bootstrap/MigrationsDataSource.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
||||
@@ -8,12 +8,12 @@ import { User } from '../Domain/User/User'
|
||||
import { Register } from '../Domain/UseCase/Register'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { KeyParamsOrigination, ProtocolVersion } from '@standardnotes/common'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
import { SignInWithRecoveryCodes } from '../Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||
import { GetUserKeyParamsRecovery } from '../Domain/UseCase/GetUserKeyParamsRecovery/GetUserKeyParamsRecovery'
|
||||
import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/GenerateRecoveryCodes'
|
||||
import { Logger } from 'winston'
|
||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
|
||||
describe('AuthController', () => {
|
||||
let clearLoginAttempts: ClearLoginAttempts
|
||||
@@ -73,7 +73,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: 'asdzxc',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
@@ -103,7 +103,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: '',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
@@ -123,7 +123,7 @@ describe('AuthController', () => {
|
||||
email: 'test@test.te',
|
||||
password: 'test',
|
||||
version: ProtocolVersion.V004,
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
origination: KeyParamsOrigination.Registration,
|
||||
userAgent: 'Google Chrome',
|
||||
identifier: 'test@test.te',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import {
|
||||
ApiVersion,
|
||||
UserRegistrationRequestParams,
|
||||
UserServerInterface,
|
||||
UserDeletionResponseBody,
|
||||
UserRegistrationResponseBody,
|
||||
UserUpdateRequestParams,
|
||||
} from '@standardnotes/api'
|
||||
import { ErrorTag, HttpResponse, HttpStatusCode } from '@standardnotes/responses'
|
||||
import { ProtocolVersion } from '@standardnotes/common'
|
||||
@@ -23,6 +23,8 @@ import { GenerateRecoveryCodes } from '../Domain/UseCase/GenerateRecoveryCodes/G
|
||||
import { GenerateRecoveryCodesRequestParams } from '../Infra/Http/Request/GenerateRecoveryCodesRequestParams'
|
||||
import { Logger } from 'winston'
|
||||
import { SessionServiceInterface } from '../Domain/Session/SessionServiceInterface'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
import { UserUpdateResponse } from '@standardnotes/api/dist/Domain/Response/User/UserUpdateResponse'
|
||||
|
||||
export class AuthController implements UserServerInterface {
|
||||
constructor(
|
||||
@@ -37,6 +39,10 @@ export class AuthController implements UserServerInterface {
|
||||
private sessionService: SessionServiceInterface,
|
||||
) {}
|
||||
|
||||
async update(_params: UserUpdateRequestParams): Promise<HttpResponse<UserUpdateResponse>> {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
async deleteAccount(_params: never): Promise<HttpResponse<UserDeletionResponseBody>> {
|
||||
throw new Error('This method is implemented on the payments server.')
|
||||
}
|
||||
@@ -121,7 +127,7 @@ export class AuthController implements UserServerInterface {
|
||||
async signInWithRecoveryCodes(
|
||||
params: SignInWithRecoveryCodesRequestParams,
|
||||
): Promise<HttpResponse<SignInWithRecoveryCodesResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
@@ -162,7 +168,7 @@ export class AuthController implements UserServerInterface {
|
||||
async recoveryKeyParams(
|
||||
params: RecoveryKeyParamsRequestParams,
|
||||
): Promise<HttpResponse<RecoveryKeyParamsResponseBody>> {
|
||||
if (params.apiVersion !== ApiVersion.v0) {
|
||||
if (params.apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AcceptSharedSubscriptionInvitation } from '../Domain/UseCase/AcceptShar
|
||||
import { DeclineSharedSubscriptionInvitation } from '../Domain/UseCase/DeclineSharedSubscriptionInvitation/DeclineSharedSubscriptionInvitation'
|
||||
import { CancelSharedSubscriptionInvitation } from '../Domain/UseCase/CancelSharedSubscriptionInvitation/CancelSharedSubscriptionInvitation'
|
||||
import { ListSharedSubscriptionInvitations } from '../Domain/UseCase/ListSharedSubscriptionInvitations/ListSharedSubscriptionInvitations'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
import { ApiVersion } from '../Domain/Api/ApiVersion'
|
||||
|
||||
describe('SubscriptionInvitesController', () => {
|
||||
let inviteToSharedSubscription: InviteToSharedSubscription
|
||||
@@ -53,7 +53,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
invitations: [],
|
||||
})
|
||||
|
||||
const result = await createController().listInvites({ api: ApiVersion.v0, inviterEmail: 'test@test.te' })
|
||||
const result = await createController().listInvites({ api: ApiVersion.v20200115, inviterEmail: 'test@test.te' })
|
||||
|
||||
expect(listSharedSubscriptionInvitations.execute).toHaveBeenCalledWith({
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -68,7 +68,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
})
|
||||
@@ -87,7 +87,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().cancelInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -100,7 +100,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -117,7 +117,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().declineInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -134,7 +134,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -151,7 +151,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().acceptInvite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
inviteUuid: '1-2-3',
|
||||
})
|
||||
|
||||
@@ -168,7 +168,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -187,7 +187,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
|
||||
it('should not invite to user subscription if the identifier is missing in request', async () => {
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: '',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
@@ -205,7 +205,7 @@ describe('SubscriptionInvitesController', () => {
|
||||
})
|
||||
|
||||
const result = await createController().invite({
|
||||
api: ApiVersion.v0,
|
||||
api: ApiVersion.v20200115,
|
||||
identifier: 'invitee@test.te',
|
||||
inviterUuid: '1-2-3',
|
||||
inviterEmail: 'test@test.te',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as bcrypt from 'bcryptjs'
|
||||
import { RoleName, Username } from '@standardnotes/domain-core'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { inject, injectable } from 'inversify'
|
||||
@@ -16,6 +15,7 @@ import { TimerInterface } from '@standardnotes/time'
|
||||
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
|
||||
import { AuthResponseFactory20200115 } from '../Auth/AuthResponseFactory20200115'
|
||||
import { AuthResponse20200115 } from '../Auth/AuthResponse20200115'
|
||||
import { ApiVersion } from '../Api/ApiVersion'
|
||||
|
||||
@injectable()
|
||||
export class Register implements UseCaseInterface {
|
||||
@@ -39,7 +39,7 @@ export class Register implements UseCaseInterface {
|
||||
|
||||
const { email, password, apiVersion, ephemeralSession, ...registrationFields } = dto
|
||||
|
||||
if (apiVersion !== ApiVersion.v0) {
|
||||
if (apiVersion !== ApiVersion.v20200115) {
|
||||
return {
|
||||
success: false,
|
||||
errorMessage: `Unsupported api version: ${apiVersion}`,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as bcrypt from 'bcryptjs'
|
||||
import { Result, UseCaseInterface, Username, Uuid, Validator } from '@standardnotes/domain-core'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { ApiVersion } from '@standardnotes/api'
|
||||
|
||||
import { AuthResponse20200115 } from '../../Auth/AuthResponse20200115'
|
||||
import { SettingServiceInterface } from '../../Setting/SettingServiceInterface'
|
||||
@@ -16,6 +15,7 @@ import { IncreaseLoginAttempts } from '../IncreaseLoginAttempts'
|
||||
import { ClearLoginAttempts } from '../ClearLoginAttempts'
|
||||
import { DeleteSetting } from '../DeleteSetting/DeleteSetting'
|
||||
import { AuthenticatorRepositoryInterface } from '../../Authenticator/AuthenticatorRepositoryInterface'
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse20200115> {
|
||||
constructor(
|
||||
@@ -100,7 +100,7 @@ export class SignInWithRecoveryCodes implements UseCaseInterface<AuthResponse202
|
||||
|
||||
const authResponse = await this.authResponseFactory.createResponse({
|
||||
user,
|
||||
apiVersion: ApiVersion.v0,
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
userAgent: dto.userAgent,
|
||||
ephemeralSession: false,
|
||||
readonlyAccess: false,
|
||||
|
||||
@@ -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.50.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.49.0...@standardnotes/common@1.50.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
# [1.49.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.48.3...@standardnotes/common@1.49.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.49.0",
|
||||
"version": "1.50.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/* istanbul ignore file */
|
||||
export enum ContentType {
|
||||
Any = '*',
|
||||
Item = 'SF|Item',
|
||||
KeySystemItemsKey = 'SN|KeySystemItemsKey',
|
||||
KeySystemRootKey = 'SN|KeySystemRootKey',
|
||||
TrustedContact = 'SN|TrustedContact',
|
||||
VaultListing = 'SN|VaultListing',
|
||||
RootKey = 'SN|RootKey|NoSync',
|
||||
ItemsKey = 'SN|ItemsKey',
|
||||
EncryptedStorage = 'SN|EncryptedStorage',
|
||||
Privileges = 'SN|Privileges',
|
||||
Note = 'Note',
|
||||
Tag = 'Tag',
|
||||
SmartView = 'SN|SmartTag',
|
||||
Component = 'SN|Component',
|
||||
Editor = 'SN|Editor',
|
||||
ActionsExtension = 'Extension',
|
||||
UserPrefs = 'SN|UserPreferences',
|
||||
HistorySession = 'SN|HistorySession',
|
||||
Theme = 'SN|Theme',
|
||||
File = 'SN|File',
|
||||
FilesafeCredentials = 'SN|FileSafe|Credentials',
|
||||
FilesafeFileMetadata = 'SN|FileSafe|FileMetadata',
|
||||
FilesafeIntegration = 'SN|FileSafe|Integration',
|
||||
ExtensionRepo = 'SN|ExtensionRepo',
|
||||
Unknown = 'Unknown',
|
||||
}
|
||||
|
||||
export function DisplayStringForContentType(contentType: ContentType): string | undefined {
|
||||
const map: Partial<Record<ContentType, string>> = {
|
||||
[ContentType.ActionsExtension]: 'action-based extension',
|
||||
[ContentType.Component]: 'component',
|
||||
[ContentType.Editor]: 'editor',
|
||||
[ContentType.File]: 'file',
|
||||
[ContentType.FilesafeCredentials]: 'FileSafe credential',
|
||||
[ContentType.FilesafeFileMetadata]: 'FileSafe file',
|
||||
[ContentType.FilesafeIntegration]: 'FileSafe integration',
|
||||
[ContentType.ItemsKey]: 'encryption key',
|
||||
[ContentType.Note]: 'note',
|
||||
[ContentType.SmartView]: 'smart view',
|
||||
[ContentType.Tag]: 'tag',
|
||||
[ContentType.Theme]: 'theme',
|
||||
[ContentType.UserPrefs]: 'user preferences',
|
||||
}
|
||||
|
||||
return map[contentType]
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './Content/ContentType'
|
||||
export * from './Content/ContentDecoder'
|
||||
export * from './Content/ContentDecoderInterface'
|
||||
export * from './DataType/AnyRecord'
|
||||
|
||||
@@ -3,6 +3,30 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.1...@standardnotes/domain-core@1.22.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.0...@standardnotes/domain-core@1.21.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.19.0",
|
||||
"version": "1.22.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
39
packages/domain-core/src/Domain/Common/ContentType.spec.ts
Normal file
39
packages/domain-core/src/Domain/Common/ContentType.spec.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { ContentType } from './ContentType'
|
||||
|
||||
describe('ContentType', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.Component)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('SN|Component')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, 0, 'FOOBAR']) {
|
||||
const valueOrError = ContentType.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should return a display name', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.FilesafeFileMetadata)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('FileSafe file')
|
||||
})
|
||||
|
||||
it('should return null for a display name if the value is null', () => {
|
||||
const valueOrError = ContentType.create(null)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toBeNull()
|
||||
})
|
||||
|
||||
it('should fallback to the value if the display name is not found', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.Unknown)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('Unknown')
|
||||
})
|
||||
})
|
||||
79
packages/domain-core/src/Domain/Common/ContentType.ts
Normal file
79
packages/domain-core/src/Domain/Common/ContentType.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { ContentTypeProps } from './ContentTypeProps'
|
||||
|
||||
export class ContentType extends ValueObject<ContentTypeProps> {
|
||||
static readonly TYPES = {
|
||||
Any: '*',
|
||||
Item: 'SF|Item',
|
||||
KeySystemItemsKey: 'SN|KeySystemItemsKey',
|
||||
KeySystemRootKey: 'SN|KeySystemRootKey',
|
||||
TrustedContact: 'SN|TrustedContact',
|
||||
VaultListing: 'SN|VaultListing',
|
||||
RootKey: 'SN|RootKey|NoSync',
|
||||
ItemsKey: 'SN|ItemsKey',
|
||||
EncryptedStorage: 'SN|EncryptedStorage',
|
||||
Privileges: 'SN|Privileges',
|
||||
Note: 'Note',
|
||||
Tag: 'Tag',
|
||||
SmartView: 'SN|SmartTag',
|
||||
Component: 'SN|Component',
|
||||
Editor: 'SN|Editor',
|
||||
ActionsExtension: 'Extension',
|
||||
UserPrefs: 'SN|UserPreferences',
|
||||
HistorySession: 'SN|HistorySession',
|
||||
Theme: 'SN|Theme',
|
||||
File: 'SN|File',
|
||||
FilesafeCredentials: 'SN|FileSafe|Credentials',
|
||||
FilesafeFileMetadata: 'SN|FileSafe|FileMetadata',
|
||||
FilesafeIntegration: 'SN|FileSafe|Integration',
|
||||
ExtensionRepo: 'SN|ExtensionRepo',
|
||||
Unknown: 'Unknown',
|
||||
}
|
||||
|
||||
private readonly displayNamesMap: Partial<Record<string, string>> = {
|
||||
[ContentType.TYPES.ActionsExtension]: 'action-based extension',
|
||||
[ContentType.TYPES.Component]: 'component',
|
||||
[ContentType.TYPES.Editor]: 'editor',
|
||||
[ContentType.TYPES.File]: 'file',
|
||||
[ContentType.TYPES.FilesafeCredentials]: 'FileSafe credential',
|
||||
[ContentType.TYPES.FilesafeFileMetadata]: 'FileSafe file',
|
||||
[ContentType.TYPES.FilesafeIntegration]: 'FileSafe integration',
|
||||
[ContentType.TYPES.ItemsKey]: 'encryption key',
|
||||
[ContentType.TYPES.Note]: 'note',
|
||||
[ContentType.TYPES.SmartView]: 'smart view',
|
||||
[ContentType.TYPES.Tag]: 'tag',
|
||||
[ContentType.TYPES.Theme]: 'theme',
|
||||
[ContentType.TYPES.UserPrefs]: 'user preferences',
|
||||
}
|
||||
|
||||
get value(): string | null {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ContentTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(type: string | null): Result<ContentType> {
|
||||
if (type === null) {
|
||||
return Result.ok<ContentType>(new ContentType({ value: null }))
|
||||
}
|
||||
|
||||
const isValidType = Object.values(this.TYPES).includes(type)
|
||||
if (!isValidType) {
|
||||
return Result.fail<ContentType>(`Invalid content type: ${type}`)
|
||||
} else {
|
||||
return Result.ok<ContentType>(new ContentType({ value: type }))
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayName(): string | null {
|
||||
if (!this.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.displayNamesMap[this.value] || this.value
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
|
||||
return false
|
||||
}
|
||||
|
||||
equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
override equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
if (this.props.value.length !== roleNameCollection.value.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -13,4 +13,25 @@ describe('Uuid', () => {
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check equality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects of different types', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
|
||||
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,4 +7,12 @@ export abstract class ValueObject<T extends ValueObjectProps> {
|
||||
constructor(props: T) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
public equals(vo?: ValueObject<T>): boolean {
|
||||
if (vo === null || vo === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return JSON.stringify(this.props) === JSON.stringify(vo.props)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ export * from './Cache/CacheEntry'
|
||||
export * from './Cache/CacheEntryProps'
|
||||
export * from './Cache/CacheEntryRepositoryInterface'
|
||||
|
||||
export * from './Common/ContentType'
|
||||
export * from './Common/ContentTypeProps'
|
||||
export * from './Common/Dates'
|
||||
export * from './Common/DatesProps'
|
||||
export * from './Common/Email'
|
||||
|
||||
@@ -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.12.9](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.8...@standardnotes/domain-events-infra@1.12.9) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.8](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.7...@standardnotes/domain-events-infra@1.12.8) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.12.7](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.12.6...@standardnotes/domain-events-infra@1.12.7) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.12.7",
|
||||
"version": "1.12.9",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
|
||||
## [2.113.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.113.0...@standardnotes/domain-events@2.113.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [2.113.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.1...@standardnotes/domain-events@2.113.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
## [2.112.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.112.0...@standardnotes/domain-events@2.112.1) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.112.1",
|
||||
"version": "2.113.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.6](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.5...@standardnotes/event-store@1.11.6) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.5](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.4...@standardnotes/event-store@1.11.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.3...@standardnotes/event-store@1.11.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.2...@standardnotes/event-store@1.11.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.11.1...@standardnotes/event-store@1.11.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.11.2",
|
||||
"version": "1.11.6",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.19.7](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.6...@standardnotes/files-server@1.19.7) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.6](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.5...@standardnotes/files-server@1.19.6) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.5](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.4...@standardnotes/files-server@1.19.5) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.4](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.3...@standardnotes/files-server@1.19.4) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.3](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.2...@standardnotes/files-server@1.19.3) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.19.2](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.19.1...@standardnotes/files-server@1.19.2) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.19.2",
|
||||
"version": "1.19.7",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-common": "^1.13.4",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
"@standardnotes/time": "workspace:*",
|
||||
"connect-busboy": "^1.0.0",
|
||||
|
||||
@@ -3,6 +3,90 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.11.41](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.40...@standardnotes/home-server@1.11.41) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.40](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.39...@standardnotes/home-server@1.11.40) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.39](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.38...@standardnotes/home-server@1.11.39) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.38](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.37...@standardnotes/home-server@1.11.38) (2023-07-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.37](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.36...@standardnotes/home-server@1.11.37) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.36](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.35...@standardnotes/home-server@1.11.36) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.35](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.34...@standardnotes/home-server@1.11.35) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.34](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.33...@standardnotes/home-server@1.11.34) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.33](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.32...@standardnotes/home-server@1.11.33) (2023-07-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.32](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.31...@standardnotes/home-server@1.11.32) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.31](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.30...@standardnotes/home-server@1.11.31) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.30](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.29...@standardnotes/home-server@1.11.30) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.29](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.28...@standardnotes/home-server@1.11.29) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.28](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.27...@standardnotes/home-server@1.11.28) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.27](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.26...@standardnotes/home-server@1.11.27) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.26](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.25...@standardnotes/home-server@1.11.26) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.25](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.24...@standardnotes/home-server@1.11.25) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.24](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.23...@standardnotes/home-server@1.11.24) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.23](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.22...@standardnotes/home-server@1.11.23) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.22](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.21...@standardnotes/home-server@1.11.22) (2023-07-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.21](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.20...@standardnotes/home-server@1.11.21) (2023-07-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
## [1.11.20](https://github.com/standardnotes/server/compare/@standardnotes/home-server@1.11.19...@standardnotes/home-server@1.11.20) (2023-07-03)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/home-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/home-server",
|
||||
"version": "1.11.20",
|
||||
"version": "1.11.41",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.24.1](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.24.0...@standardnotes/revisions-server@1.24.1) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.9...@standardnotes/revisions-server@1.24.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.23.9](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.8...@standardnotes/revisions-server@1.23.9) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.8](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.7...@standardnotes/revisions-server@1.23.8) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.7](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.6...@standardnotes/revisions-server@1.23.7) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.23.6](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.23.5...@standardnotes/revisions-server@1.23.6) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.23.6",
|
||||
"version": "1.24.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -27,12 +27,12 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.332.0",
|
||||
"@aws-sdk/client-sqs": "^3.332.0",
|
||||
"@standardnotes/api": "^1.25.3",
|
||||
"@standardnotes/api": "^1.26.26",
|
||||
"@standardnotes/common": "workspace:^",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
"@standardnotes/responses": "^1.13.9",
|
||||
"@standardnotes/responses": "^1.13.27",
|
||||
"@standardnotes/security": "workspace:^",
|
||||
"@standardnotes/time": "workspace:^",
|
||||
"cors": "2.8.5",
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ContentType } from './ContentType'
|
||||
|
||||
describe('ContentType', () => {
|
||||
it('should create a value obejct', () => {
|
||||
const valueOrError = ContentType.create('Note')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should fail to create a value obejct', () => {
|
||||
const valueOrError = ContentType.create('test')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -1,22 +0,0 @@
|
||||
import { ContentType as ContentTypeValues } from '@standardnotes/common'
|
||||
import { Result, ValueObject } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentTypeProps } from './ContentTypeProps'
|
||||
|
||||
export class ContentType extends ValueObject<ContentTypeProps> {
|
||||
get value(): string | null {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ContentTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(contentType: string | null): Result<ContentType> {
|
||||
if (contentType !== null && !Object.values(ContentTypeValues).includes(contentType as ContentTypeValues)) {
|
||||
return Result.fail<ContentType>(`Value is not a valid content type: ${contentType}`)
|
||||
} else {
|
||||
return Result.ok<ContentType>(new ContentType({ value: contentType }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Dates, Uuid } from '@standardnotes/domain-core'
|
||||
import { ContentType } from './ContentType'
|
||||
import { ContentType, Dates, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from './Revision'
|
||||
|
||||
describe('Revision', () => {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Dates } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from './ContentType'
|
||||
import { ContentType, Dates } from '@standardnotes/domain-core'
|
||||
|
||||
export interface RevisionMetadataProps {
|
||||
contentType: ContentType
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Dates, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from './ContentType'
|
||||
import { ContentType, Dates, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface RevisionProps {
|
||||
itemUuid: Uuid
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { MapperInterface, Dates, Uuid } from '@standardnotes/domain-core'
|
||||
import { MapperInterface, Dates, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from '../Domain/Revision/ContentType'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
|
||||
export class RevisionItemStringMapper implements MapperInterface<Revision, string> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { MapperInterface, Dates, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
import { MapperInterface, Dates, UniqueEntityId, ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { ContentType } from '../Domain/Revision/ContentType'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { MapperInterface, Dates, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
import { ContentType } from '../Domain/Revision/ContentType'
|
||||
import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { TypeORMRevision } from '../Infra/TypeORM/TypeORMRevision'
|
||||
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.20.8](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.7...@standardnotes/scheduler-server@1.20.8) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.7](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.6...@standardnotes/scheduler-server@1.20.7) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.6](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.5...@standardnotes/scheduler-server@1.20.6) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.5](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.4...@standardnotes/scheduler-server@1.20.5) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.20.4](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.20.3...@standardnotes/scheduler-server@1.20.4) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.20.4",
|
||||
"version": "1.20.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.21.13](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.12...@standardnotes/settings@1.21.13) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.12](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.11...@standardnotes/settings@1.21.12) (2023-07-07)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.11](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.10...@standardnotes/settings@1.21.11) (2023-07-06)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.10](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.9...@standardnotes/settings@1.21.10) (2023-07-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
## [1.21.9](https://github.com/standardnotes/server/compare/@standardnotes/settings@1.21.8...@standardnotes/settings@1.21.9) (2023-06-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/settings
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/settings",
|
||||
"version": "1.21.9",
|
||||
"version": "1.21.13",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.3](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.15.2...@standardnotes/sncrypto-node@1.15.3) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/sncrypto-node
|
||||
|
||||
## [1.15.2](https://github.com/standardnotes/server/compare/@standardnotes/sncrypto-node@1.15.0...@standardnotes/sncrypto-node@1.15.2) (2023-06-01)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/sncrypto-node",
|
||||
"version": "1.15.2",
|
||||
"version": "1.15.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -23,7 +23,7 @@
|
||||
"test": "jest spec"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/sncrypto-common": "^1.9.0",
|
||||
"@standardnotes/sncrypto-common": "^1.13.4",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -3,6 +3,119 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.63.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.63.0...@standardnotes/syncing-server@1.63.1) (2023-07-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
# [1.63.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.62.1...@standardnotes/syncing-server@1.63.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/syncing-server-js/issues/655)) ([a0af8f0](https://github.com/standardnotes/syncing-server-js/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.62.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.62.0...@standardnotes/syncing-server@1.62.1) (2023-07-11)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* unify use case usage ([#654](https://github.com/standardnotes/syncing-server-js/issues/654)) ([4d1e2de](https://github.com/standardnotes/syncing-server-js/commit/4d1e2dec264b156a4cfb4980ca3b486433ce64b7))
|
||||
|
||||
# [1.62.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.61.0...@standardnotes/syncing-server@1.62.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* messages controller. ([#653](https://github.com/standardnotes/syncing-server-js/issues/653)) ([18d07d4](https://github.com/standardnotes/syncing-server-js/commit/18d07d431f08dc17a276f84c0724935d9014a4fd))
|
||||
|
||||
# [1.61.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.60.0...@standardnotes/syncing-server@1.61.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* message operations use cases. ([#652](https://github.com/standardnotes/syncing-server-js/issues/652)) ([55ec597](https://github.com/standardnotes/syncing-server-js/commit/55ec5970daff9ef51f59e23eca17b312d392542a))
|
||||
|
||||
# [1.60.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.59.1...@standardnotes/syncing-server@1.60.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* sending messages. ([#651](https://github.com/standardnotes/syncing-server-js/issues/651)) ([ef49b0d](https://github.com/standardnotes/syncing-server-js/commit/ef49b0d3f8ab76dfa63a4c691feb9f35ad65c46f))
|
||||
|
||||
## [1.59.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.59.0...@standardnotes/syncing-server@1.59.1) (2023-07-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* restructure use cases ([#650](https://github.com/standardnotes/syncing-server-js/issues/650)) ([04d0958](https://github.com/standardnotes/syncing-server-js/commit/04d09582d4c90706a7c7a4601ce011edf6cbc9c2))
|
||||
|
||||
# [1.59.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.58.1...@standardnotes/syncing-server@1.59.0) (2023-07-10)
|
||||
|
||||
### Features
|
||||
|
||||
* user to user message model. ([#649](https://github.com/standardnotes/syncing-server-js/issues/649)) ([f759261](https://github.com/standardnotes/syncing-server-js/commit/f7592619199596f7bef5787dde25efee017c8e60))
|
||||
|
||||
## [1.58.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.58.0...@standardnotes/syncing-server@1.58.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/syncing-server-js/issues/648)) ([c288e5d](https://github.com/standardnotes/syncing-server-js/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.58.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.57.0...@standardnotes/syncing-server@1.58.0) (2023-07-07)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vault invites controller and use cases ([#647](https://github.com/standardnotes/syncing-server-js/issues/647)) ([7231013](https://github.com/standardnotes/syncing-server-js/commit/72310130d215047a8097a0c42a7b7dddeb4e3827))
|
||||
|
||||
# [1.57.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.56.0...@standardnotes/syncing-server@1.57.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* remove inbound shared vault invites. ([#646](https://github.com/standardnotes/syncing-server-js/issues/646)) ([92a5eb0](https://github.com/standardnotes/syncing-server-js/commit/92a5eb0d98486f25b761f37bc5710c45bd95d965))
|
||||
|
||||
# [1.56.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.55.0...@standardnotes/syncing-server@1.56.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* accept and decline shared vault invites ([#645](https://github.com/standardnotes/syncing-server-js/issues/645)) ([92f96dd](https://github.com/standardnotes/syncing-server-js/commit/92f96ddb84b9b7662899ef187ac38ad2a5769640))
|
||||
|
||||
# [1.55.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.54.0...@standardnotes/syncing-server@1.55.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* shared vault users controller. ([#643](https://github.com/standardnotes/syncing-server-js/issues/643)) ([b2c32ce](https://github.com/standardnotes/syncing-server-js/commit/b2c32ce70e9020b8d755a65432cb286b624a009c))
|
||||
* update shared vault invite. ([#644](https://github.com/standardnotes/syncing-server-js/issues/644)) ([912a29d](https://github.com/standardnotes/syncing-server-js/commit/912a29d091ed1ca0af1712cbd09986a1c173a960))
|
||||
|
||||
# [1.54.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.53.0...@standardnotes/syncing-server@1.54.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/syncing-server-js/issues/642)) ([e905128](https://github.com/standardnotes/syncing-server-js/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.53.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.52.0...@standardnotes/syncing-server@1.53.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* http controllers for shared vaults. ([#641](https://github.com/standardnotes/syncing-server-js/issues/641)) ([7a3946a](https://github.com/standardnotes/syncing-server-js/commit/7a3946a9e2d4168a1d286df321d9972588252b5d))
|
||||
|
||||
# [1.52.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.51.0...@standardnotes/syncing-server@1.52.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/syncing-server-js/issues/640)) ([f3161c2](https://github.com/standardnotes/syncing-server-js/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.51.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.50.0...@standardnotes/syncing-server@1.51.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* add getting shared vaults for a user ([#639](https://github.com/standardnotes/syncing-server-js/issues/639)) ([d2b2c33](https://github.com/standardnotes/syncing-server-js/commit/d2b2c339f2089ea5d538ee40118af083983be5ef))
|
||||
|
||||
# [1.50.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.49.0...@standardnotes/syncing-server@1.50.0) (2023-07-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add invite users to a shared vault. ([#636](https://github.com/standardnotes/syncing-server-js/issues/636)) ([5dc5507](https://github.com/standardnotes/syncing-server-js/commit/5dc5507039c0dfb9df82a85377846651fef73c57))
|
||||
|
||||
# [1.49.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.48.0...@standardnotes/syncing-server@1.49.0) (2023-07-03)
|
||||
|
||||
### Features
|
||||
|
||||
* add creating shared vault file valet tokens. ([#635](https://github.com/standardnotes/syncing-server-js/issues/635)) ([04b3bb0](https://github.com/standardnotes/syncing-server-js/commit/04b3bb034fb5bf6f9d00d5b2e8a1abc4832c5417))
|
||||
|
||||
# [1.48.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.47.0...@standardnotes/syncing-server@1.48.0) (2023-07-03)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540448427 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540448427'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE `notifications` (`uuid` varchar(36) NOT NULL, `user_uuid` varchar(36) NOT NULL, `type` varchar(36) NOT NULL, `payload` text NOT NULL, `created_at_timestamp` bigint NOT NULL, `updated_at_timestamp` bigint NOT NULL, INDEX `index_notifications_on_user_uuid` (`user_uuid`), PRIMARY KEY (`uuid`)) ENGINE=InnoDB',
|
||||
)
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX `index_notifications_on_user_uuid` ON `notifications`')
|
||||
await queryRunner.query('DROP TABLE `notifications`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class AddNotifications1688540623272 implements MigrationInterface {
|
||||
name = 'AddNotifications1688540623272'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
'CREATE TABLE "notifications" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "type" varchar(36) NOT NULL, "payload" text NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)',
|
||||
)
|
||||
await queryRunner.query('CREATE INDEX "index_notifications_on_user_uuid" ON "notifications" ("user_uuid") ')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DROP INDEX "index_notifications_on_user_uuid"')
|
||||
await queryRunner.query('DROP TABLE "notifications"')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.48.0",
|
||||
"version": "1.63.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
@@ -31,12 +31,12 @@
|
||||
"@aws-sdk/client-s3": "^3.332.0",
|
||||
"@aws-sdk/client-sns": "^3.332.0",
|
||||
"@aws-sdk/client-sqs": "^3.332.0",
|
||||
"@standardnotes/api": "^1.25.3",
|
||||
"@standardnotes/api": "^1.26.26",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-core": "workspace:^",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
"@standardnotes/responses": "^1.13.9",
|
||||
"@standardnotes/responses": "^1.13.27",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/settings": "workspace:*",
|
||||
"@standardnotes/sncrypto-node": "workspace:*",
|
||||
|
||||
@@ -9,9 +9,6 @@ import { ItemRepositoryInterface } from '../Domain/Item/ItemRepositoryInterface'
|
||||
import { TypeORMItemRepository } from '../Infra/TypeORM/TypeORMItemRepository'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Item } from '../Domain/Item/Item'
|
||||
import { ItemProjection } from '../Projection/ItemProjection'
|
||||
import { ProjectorInterface } from '../Projection/ProjectorInterface'
|
||||
import { ItemProjector } from '../Projection/ItemProjector'
|
||||
import {
|
||||
DirectCallDomainEventPublisher,
|
||||
DirectCallEventMessageHandler,
|
||||
@@ -26,29 +23,22 @@ import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { ItemTransferCalculatorInterface } from '../Domain/Item/ItemTransferCalculatorInterface'
|
||||
import { ItemTransferCalculator } from '../Domain/Item/ItemTransferCalculator'
|
||||
import { ItemConflict } from '../Domain/Item/ItemConflict'
|
||||
import { ItemFactory } from '../Domain/Item/ItemFactory'
|
||||
import { ItemFactoryInterface } from '../Domain/Item/ItemFactoryInterface'
|
||||
import { ItemService } from '../Domain/Item/ItemService'
|
||||
import { ItemServiceInterface } from '../Domain/Item/ItemServiceInterface'
|
||||
import { ContentFilter } from '../Domain/Item/SaveRule/ContentFilter'
|
||||
import { ContentTypeFilter } from '../Domain/Item/SaveRule/ContentTypeFilter'
|
||||
import { OwnershipFilter } from '../Domain/Item/SaveRule/OwnershipFilter'
|
||||
import { TimeDifferenceFilter } from '../Domain/Item/SaveRule/TimeDifferenceFilter'
|
||||
import { UuidFilter } from '../Domain/Item/SaveRule/UuidFilter'
|
||||
import { ItemSaveValidator } from '../Domain/Item/SaveValidator/ItemSaveValidator'
|
||||
import { ItemSaveValidatorInterface } from '../Domain/Item/SaveValidator/ItemSaveValidatorInterface'
|
||||
import { SyncResponseFactory20161215 } from '../Domain/Item/SyncResponse/SyncResponseFactory20161215'
|
||||
import { SyncResponseFactory20200115 } from '../Domain/Item/SyncResponse/SyncResponseFactory20200115'
|
||||
import { SyncResponseFactoryResolver } from '../Domain/Item/SyncResponse/SyncResponseFactoryResolver'
|
||||
import { SyncResponseFactoryResolverInterface } from '../Domain/Item/SyncResponse/SyncResponseFactoryResolverInterface'
|
||||
import { CheckIntegrity } from '../Domain/UseCase/CheckIntegrity/CheckIntegrity'
|
||||
import { GetItem } from '../Domain/UseCase/GetItem/GetItem'
|
||||
import { SyncItems } from '../Domain/UseCase/SyncItems'
|
||||
import { CheckIntegrity } from '../Domain/UseCase/Syncing/CheckIntegrity/CheckIntegrity'
|
||||
import { GetItem } from '../Domain/UseCase/Syncing/GetItem/GetItem'
|
||||
import { SyncItems } from '../Domain/UseCase/Syncing/SyncItems/SyncItems'
|
||||
import { InversifyExpressAuthMiddleware } from '../Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware'
|
||||
import { ItemConflictProjection } from '../Projection/ItemConflictProjection'
|
||||
import { ItemConflictProjector } from '../Projection/ItemConflictProjector'
|
||||
import { SavedItemProjection } from '../Projection/SavedItemProjection'
|
||||
import { SavedItemProjector } from '../Projection/SavedItemProjector'
|
||||
import { S3Client } from '@aws-sdk/client-s3'
|
||||
import { SQSClient, SQSClientConfig } from '@aws-sdk/client-sqs'
|
||||
import { ContentDecoder } from '@standardnotes/common'
|
||||
@@ -70,9 +60,21 @@ import { ItemBackupServiceInterface } from '../Domain/Item/ItemBackupServiceInte
|
||||
import { FSItemBackupService } from '../Infra/FS/FSItemBackupService'
|
||||
import { AuthHttpService } from '../Infra/HTTP/AuthHttpService'
|
||||
import { S3ItemBackupService } from '../Infra/S3/S3ItemBackupService'
|
||||
import { ControllerContainer, ControllerContainerInterface } from '@standardnotes/domain-core'
|
||||
import { ControllerContainer, ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
import { HomeServerItemsController } from '../Infra/InversifyExpressUtils/HomeServer/HomeServerItemsController'
|
||||
import { Transform } from 'stream'
|
||||
import { TypeORMItem } from '../Infra/TypeORM/TypeORMItem'
|
||||
import { ItemPersistenceMapper } from '../Mapping/Persistence/ItemPersistenceMapper'
|
||||
import { ItemHttpRepresentation } from '../Mapping/Http/ItemHttpRepresentation'
|
||||
import { ItemHttpMapper } from '../Mapping/Http/ItemHttpMapper'
|
||||
import { SavedItemHttpRepresentation } from '../Mapping/Http/SavedItemHttpRepresentation'
|
||||
import { SavedItemHttpMapper } from '../Mapping/Http/SavedItemHttpMapper'
|
||||
import { ItemConflictHttpRepresentation } from '../Mapping/Http/ItemConflictHttpRepresentation'
|
||||
import { ItemConflictHttpMapper } from '../Mapping/Http/ItemConflictHttpMapper'
|
||||
import { ItemBackupRepresentation } from '../Mapping/Backup/ItemBackupRepresentation'
|
||||
import { ItemBackupMapper } from '../Mapping/Backup/ItemBackupMapper'
|
||||
import { SaveNewItem } from '../Domain/UseCase/Syncing/SaveNewItem/SaveNewItem'
|
||||
import { UpdateExistingItem } from '../Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000
|
||||
@@ -122,6 +124,8 @@ export class ContainerConfigLoader {
|
||||
|
||||
logger.debug('Database initialized')
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Sync_Timer).toConstantValue(new Timer())
|
||||
|
||||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
|
||||
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)
|
||||
@@ -201,24 +205,37 @@ export class ContainerConfigLoader {
|
||||
})
|
||||
}
|
||||
|
||||
// Repositories
|
||||
container.bind<ItemRepositoryInterface>(TYPES.Sync_ItemRepository).toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMItemRepository(context.container.get(TYPES.Sync_ORMItemRepository))
|
||||
})
|
||||
// Mapping
|
||||
container
|
||||
.bind<MapperInterface<Item, TypeORMItem>>(TYPES.Sync_ItemPersistenceMapper)
|
||||
.toConstantValue(new ItemPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Item, ItemHttpRepresentation>>(TYPES.Sync_ItemHttpMapper)
|
||||
.toConstantValue(new ItemHttpMapper(container.get(TYPES.Sync_Timer)))
|
||||
container
|
||||
.bind<MapperInterface<Item, SavedItemHttpRepresentation>>(TYPES.Sync_SavedItemHttpMapper)
|
||||
.toConstantValue(new SavedItemHttpMapper(container.get(TYPES.Sync_Timer)))
|
||||
container
|
||||
.bind<MapperInterface<ItemConflict, ItemConflictHttpRepresentation>>(TYPES.Sync_ItemConflictHttpMapper)
|
||||
.toConstantValue(new ItemConflictHttpMapper(container.get(TYPES.Sync_ItemHttpMapper)))
|
||||
container
|
||||
.bind<MapperInterface<Item, ItemBackupRepresentation>>(TYPES.Sync_ItemBackupMapper)
|
||||
.toConstantValue(new ItemBackupMapper(container.get(TYPES.Sync_Timer)))
|
||||
|
||||
// ORM
|
||||
container
|
||||
.bind<Repository<Item>>(TYPES.Sync_ORMItemRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(Item))
|
||||
.bind<Repository<TypeORMItem>>(TYPES.Sync_ORMItemRepository)
|
||||
.toDynamicValue(() => appDataSource.getRepository(TypeORMItem))
|
||||
|
||||
// Projectors
|
||||
// Repositories
|
||||
container
|
||||
.bind<ProjectorInterface<Item, ItemProjection>>(TYPES.Sync_ItemProjector)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemProjector(context.container.get(TYPES.Sync_Timer))
|
||||
})
|
||||
|
||||
container.bind<TimerInterface>(TYPES.Sync_Timer).toDynamicValue(() => new Timer())
|
||||
.bind<ItemRepositoryInterface>(TYPES.Sync_ItemRepository)
|
||||
.toConstantValue(
|
||||
new TypeORMItemRepository(
|
||||
container.get(TYPES.Sync_ORMItemRepository),
|
||||
container.get(TYPES.Sync_ItemPersistenceMapper),
|
||||
),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory)
|
||||
@@ -245,18 +262,6 @@ export class ContainerConfigLoader {
|
||||
)
|
||||
})
|
||||
|
||||
// Projectors
|
||||
container
|
||||
.bind<ProjectorInterface<Item, SavedItemProjection>>(TYPES.Sync_SavedItemProjector)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SavedItemProjector(context.container.get(TYPES.Sync_Timer))
|
||||
})
|
||||
container
|
||||
.bind<ProjectorInterface<ItemConflict, ItemConflictProjection>>(TYPES.Sync_ItemConflictProjector)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemConflictProjector(context.container.get(TYPES.Sync_ItemProjector))
|
||||
})
|
||||
|
||||
// env vars
|
||||
container.bind(TYPES.Sync_AUTH_JWT_SECRET).toConstantValue(env.get('AUTH_JWT_SECRET'))
|
||||
container
|
||||
@@ -287,60 +292,35 @@ export class ContainerConfigLoader {
|
||||
container.bind<GetItem>(TYPES.Sync_GetItem).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetItem(context.container.get(TYPES.Sync_ItemRepository))
|
||||
})
|
||||
container
|
||||
.bind<SaveNewItem>(TYPES.Sync_SaveNewItem)
|
||||
.toConstantValue(
|
||||
new SaveNewItem(
|
||||
container.get(TYPES.Sync_ItemRepository),
|
||||
container.get(TYPES.Sync_Timer),
|
||||
container.get(TYPES.Sync_DomainEventPublisher),
|
||||
container.get(TYPES.Sync_DomainEventFactory),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UpdateExistingItem>(TYPES.Sync_UpdateExistingItem)
|
||||
.toConstantValue(
|
||||
new UpdateExistingItem(
|
||||
container.get(TYPES.Sync_ItemRepository),
|
||||
container.get(TYPES.Sync_Timer),
|
||||
container.get(TYPES.Sync_DomainEventPublisher),
|
||||
container.get(TYPES.Sync_DomainEventFactory),
|
||||
container.get(TYPES.Sync_REVISIONS_FREQUENCY),
|
||||
),
|
||||
)
|
||||
|
||||
// Services
|
||||
container.bind<ItemServiceInterface>(TYPES.Sync_ItemService).toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemService(
|
||||
context.container.get(TYPES.Sync_ItemSaveValidator),
|
||||
context.container.get(TYPES.Sync_ItemFactory),
|
||||
context.container.get(TYPES.Sync_ItemRepository),
|
||||
context.container.get(TYPES.Sync_DomainEventPublisher),
|
||||
context.container.get(TYPES.Sync_DomainEventFactory),
|
||||
context.container.get(TYPES.Sync_REVISIONS_FREQUENCY),
|
||||
context.container.get(TYPES.Sync_CONTENT_SIZE_TRANSFER_LIMIT),
|
||||
context.container.get(TYPES.Sync_ItemTransferCalculator),
|
||||
context.container.get(TYPES.Sync_Timer),
|
||||
context.container.get(TYPES.Sync_ItemProjector),
|
||||
context.container.get(TYPES.Sync_MAX_ITEMS_LIMIT),
|
||||
context.container.get(TYPES.Sync_Logger),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<SyncResponseFactory20161215>(TYPES.Sync_SyncResponseFactory20161215)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SyncResponseFactory20161215(context.container.get(TYPES.Sync_ItemProjector))
|
||||
})
|
||||
container
|
||||
.bind<SyncResponseFactory20200115>(TYPES.Sync_SyncResponseFactory20200115)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SyncResponseFactory20200115(
|
||||
context.container.get(TYPES.Sync_ItemProjector),
|
||||
context.container.get(TYPES.Sync_ItemConflictProjector),
|
||||
context.container.get(TYPES.Sync_SavedItemProjector),
|
||||
)
|
||||
})
|
||||
container
|
||||
.bind<SyncResponseFactoryResolverInterface>(TYPES.Sync_SyncResponseFactoryResolver)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SyncResponseFactoryResolver(
|
||||
context.container.get(TYPES.Sync_SyncResponseFactory20161215),
|
||||
context.container.get(TYPES.Sync_SyncResponseFactory20200115),
|
||||
)
|
||||
})
|
||||
|
||||
container.bind<ItemFactoryInterface>(TYPES.Sync_ItemFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemFactory(context.container.get(TYPES.Sync_Timer), context.container.get(TYPES.Sync_ItemProjector))
|
||||
})
|
||||
|
||||
container.bind<OwnershipFilter>(TYPES.Sync_OwnershipFilter).toDynamicValue(() => new OwnershipFilter())
|
||||
container.bind<OwnershipFilter>(TYPES.Sync_OwnershipFilter).toConstantValue(new OwnershipFilter())
|
||||
container
|
||||
.bind<TimeDifferenceFilter>(TYPES.Sync_TimeDifferenceFilter)
|
||||
.toDynamicValue(
|
||||
(context: interfaces.Context) => new TimeDifferenceFilter(context.container.get(TYPES.Sync_Timer)),
|
||||
)
|
||||
container.bind<UuidFilter>(TYPES.Sync_UuidFilter).toDynamicValue(() => new UuidFilter())
|
||||
container.bind<ContentTypeFilter>(TYPES.Sync_ContentTypeFilter).toDynamicValue(() => new ContentTypeFilter())
|
||||
container.bind<ContentFilter>(TYPES.Sync_ContentFilter).toDynamicValue(() => new ContentFilter())
|
||||
.toConstantValue(new TimeDifferenceFilter(container.get(TYPES.Sync_Timer)))
|
||||
container.bind<ContentTypeFilter>(TYPES.Sync_ContentTypeFilter).toConstantValue(new ContentTypeFilter())
|
||||
container.bind<ContentFilter>(TYPES.Sync_ContentFilter).toConstantValue(new ContentFilter())
|
||||
|
||||
container
|
||||
.bind<ItemSaveValidatorInterface>(TYPES.Sync_ItemSaveValidator)
|
||||
@@ -348,12 +328,47 @@ export class ContainerConfigLoader {
|
||||
return new ItemSaveValidator([
|
||||
context.container.get(TYPES.Sync_OwnershipFilter),
|
||||
context.container.get(TYPES.Sync_TimeDifferenceFilter),
|
||||
context.container.get(TYPES.Sync_UuidFilter),
|
||||
context.container.get(TYPES.Sync_ContentTypeFilter),
|
||||
context.container.get(TYPES.Sync_ContentFilter),
|
||||
])
|
||||
})
|
||||
|
||||
container
|
||||
.bind<ItemServiceInterface>(TYPES.Sync_ItemService)
|
||||
.toConstantValue(
|
||||
new ItemService(
|
||||
container.get(TYPES.Sync_ItemSaveValidator),
|
||||
container.get(TYPES.Sync_ItemRepository),
|
||||
container.get(TYPES.Sync_CONTENT_SIZE_TRANSFER_LIMIT),
|
||||
container.get(TYPES.Sync_ItemTransferCalculator),
|
||||
container.get(TYPES.Sync_Timer),
|
||||
container.get(TYPES.Sync_MAX_ITEMS_LIMIT),
|
||||
container.get(TYPES.Sync_SaveNewItem),
|
||||
container.get(TYPES.Sync_UpdateExistingItem),
|
||||
container.get(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SyncResponseFactory20161215>(TYPES.Sync_SyncResponseFactory20161215)
|
||||
.toConstantValue(new SyncResponseFactory20161215(container.get(TYPES.Sync_ItemHttpMapper)))
|
||||
container
|
||||
.bind<SyncResponseFactory20200115>(TYPES.Sync_SyncResponseFactory20200115)
|
||||
.toConstantValue(
|
||||
new SyncResponseFactory20200115(
|
||||
container.get(TYPES.Sync_ItemHttpMapper),
|
||||
container.get(TYPES.Sync_ItemConflictHttpMapper),
|
||||
container.get(TYPES.Sync_SavedItemHttpMapper),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SyncResponseFactoryResolverInterface>(TYPES.Sync_SyncResponseFactoryResolver)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SyncResponseFactoryResolver(
|
||||
context.container.get(TYPES.Sync_SyncResponseFactory20161215),
|
||||
context.container.get(TYPES.Sync_SyncResponseFactory20200115),
|
||||
)
|
||||
})
|
||||
|
||||
// env vars
|
||||
container
|
||||
.bind(TYPES.Sync_EMAIL_ATTACHMENT_MAX_BYTE_SIZE)
|
||||
@@ -421,14 +436,15 @@ export class ContainerConfigLoader {
|
||||
if (env.get('S3_AWS_REGION', true)) {
|
||||
return new S3ItemBackupService(
|
||||
context.container.get(TYPES.Sync_S3_BACKUP_BUCKET_NAME),
|
||||
context.container.get(TYPES.Sync_ItemProjector),
|
||||
context.container.get(TYPES.Sync_ItemBackupMapper),
|
||||
context.container.get(TYPES.Sync_ItemHttpMapper),
|
||||
context.container.get(TYPES.Sync_Logger),
|
||||
context.container.get(TYPES.Sync_S3),
|
||||
)
|
||||
} else {
|
||||
return new FSItemBackupService(
|
||||
context.container.get(TYPES.Sync_FILE_UPLOAD_PATH),
|
||||
context.container.get(TYPES.Sync_ItemProjector),
|
||||
context.container.get(TYPES.Sync_ItemBackupMapper),
|
||||
context.container.get(TYPES.Sync_Logger),
|
||||
)
|
||||
}
|
||||
@@ -512,7 +528,7 @@ export class ContainerConfigLoader {
|
||||
container.get(TYPES.Sync_SyncItems),
|
||||
container.get(TYPES.Sync_CheckIntegrity),
|
||||
container.get(TYPES.Sync_GetItem),
|
||||
container.get(TYPES.Sync_ItemProjector),
|
||||
container.get(TYPES.Sync_ItemHttpMapper),
|
||||
container.get(TYPES.Sync_SyncResponseFactoryResolver),
|
||||
container.get(TYPES.Sync_ControllerContainer),
|
||||
),
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
import { Item } from '../Domain/Item/Item'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { TypeORMItem } from '../Infra/TypeORM/TypeORMItem'
|
||||
import { TypeORMNotification } from '../Infra/TypeORM/TypeORMNotification'
|
||||
|
||||
export class AppDataSource {
|
||||
private dataSource: DataSource | undefined
|
||||
private _dataSource: DataSource | undefined
|
||||
|
||||
constructor(private env: Env) {}
|
||||
|
||||
getRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): Repository<Entity> {
|
||||
if (!this.dataSource) {
|
||||
if (!this._dataSource) {
|
||||
throw new Error('DataSource not initialized')
|
||||
}
|
||||
|
||||
return this.dataSource.getRepository(target)
|
||||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
this.env.load()
|
||||
|
||||
const isConfiguredForMySQL = this.env.get('DB_TYPE') === 'mysql'
|
||||
@@ -28,7 +33,7 @@ export class AppDataSource {
|
||||
|
||||
const commonDataSourceOptions = {
|
||||
maxQueryExecutionTime,
|
||||
entities: [Item],
|
||||
entities: [TypeORMItem, TypeORMNotification],
|
||||
migrations: [`${__dirname}/../../migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`],
|
||||
migrationsRun: true,
|
||||
logging: <LoggerOptions>this.env.get('DB_DEBUG_LEVEL', true) ?? 'info',
|
||||
@@ -72,7 +77,7 @@ export class AppDataSource {
|
||||
database: inReplicaMode ? undefined : this.env.get('DB_DATABASE'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
this._dataSource = new DataSource(mySQLDataSourceOptions)
|
||||
} else {
|
||||
const sqliteDataSourceOptions: SqliteConnectionOptions = {
|
||||
...commonDataSourceOptions,
|
||||
@@ -80,9 +85,9 @@ export class AppDataSource {
|
||||
database: this.env.get('DB_SQLITE_DATABASE_PATH'),
|
||||
}
|
||||
|
||||
this.dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
this._dataSource = new DataSource(sqliteDataSourceOptions)
|
||||
}
|
||||
|
||||
await this.dataSource.initialize()
|
||||
return this._dataSource
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { AppDataSource } from './DataSource'
|
||||
import { Env } from './Env'
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
export const MigrationsDataSource = new AppDataSource(env).dataSource
|
||||
@@ -12,10 +12,6 @@ const TYPES = {
|
||||
Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'),
|
||||
// Middleware
|
||||
Sync_AuthMiddleware: Symbol.for('Sync_AuthMiddleware'),
|
||||
// Projectors
|
||||
Sync_ItemProjector: Symbol.for('Sync_ItemProjector'),
|
||||
Sync_SavedItemProjector: Symbol.for('Sync_SavedItemProjector'),
|
||||
Sync_ItemConflictProjector: Symbol.for('Sync_ItemConflictProjector'),
|
||||
// env vars
|
||||
Sync_REDIS_URL: Symbol.for('Sync_REDIS_URL'),
|
||||
Sync_SNS_TOPIC_ARN: Symbol.for('Sync_SNS_TOPIC_ARN'),
|
||||
@@ -38,6 +34,27 @@ const TYPES = {
|
||||
Sync_SyncItems: Symbol.for('Sync_SyncItems'),
|
||||
Sync_CheckIntegrity: Symbol.for('Sync_CheckIntegrity'),
|
||||
Sync_GetItem: Symbol.for('Sync_GetItem'),
|
||||
Sync_GetSharedVaults: Symbol.for('Sync_GetSharedVaults'),
|
||||
Sync_CreateSharedVault: Symbol.for('Sync_CreateSharedVault'),
|
||||
Sync_DeleteSharedVault: Symbol.for('Sync_DeleteSharedVault'),
|
||||
Sync_CreateSharedVaultFileValetToken: Symbol.for('Sync_CreateSharedVaultFileValetToken'),
|
||||
Sync_GetSharedVaultUsers: Symbol.for('Sync_GetSharedVaultUsers'),
|
||||
Sync_RemoveSharedVaultUser: Symbol.for('Sync_RemoveSharedVaultUser'),
|
||||
Sync_InviteUserToSharedVault: Symbol.for('Sync_InviteUserToSharedVault'),
|
||||
Sync_UpdateSharedVaultInvite: Symbol.for('Sync_UpdateSharedVaultInvite'),
|
||||
Sync_AcceptInviteToSharedVault: Symbol.for('Sync_AcceptInviteToSharedVault'),
|
||||
Sync_DeclineInviteToSharedVault: Symbol.for('Sync_DeclineInviteToSharedVault'),
|
||||
Sync_DeleteSharedVaultInvitesToUser: Symbol.for('Sync_DeleteSharedVaultInvitesToUser'),
|
||||
Sync_DeleteSharedVaultInvitesSentByUser: Symbol.for('Sync_DeleteSharedVaultInvitesSentByUser'),
|
||||
Sync_GetSharedVaultInvitesSentByUser: Symbol.for('Sync_GetSharedVaultInvitesSentByUser'),
|
||||
Sync_GetSharedVaultInvitesSentToUser: Symbol.for('Sync_GetSharedVaultInvitesSentToUser'),
|
||||
Sync_GetMessagesSentToUser: Symbol.for('Sync_GetMessagesSentToUser'),
|
||||
Sync_GetMessagesSentByUser: Symbol.for('Sync_GetMessagesSentByUser'),
|
||||
Sync_SendMessageToUser: Symbol.for('Sync_SendMessageToUser'),
|
||||
Sync_DeleteAllMessagesSentToUser: Symbol.for('Sync_DeleteAllMessagesSentToUser'),
|
||||
Sync_DeleteMessage: Symbol.for('Sync_DeleteMessage'),
|
||||
Sync_SaveNewItem: Symbol.for('Sync_SaveNewItem'),
|
||||
Sync_UpdateExistingItem: Symbol.for('Sync_UpdateExistingItem'),
|
||||
// Handlers
|
||||
Sync_AccountDeletionRequestedEventHandler: Symbol.for('Sync_AccountDeletionRequestedEventHandler'),
|
||||
Sync_DuplicateItemSyncedEventHandler: Symbol.for('Sync_DuplicateItemSyncedEventHandler'),
|
||||
@@ -61,13 +78,21 @@ const TYPES = {
|
||||
Sync_ItemSaveValidator: Symbol.for('Sync_ItemSaveValidator'),
|
||||
Sync_OwnershipFilter: Symbol.for('Sync_OwnershipFilter'),
|
||||
Sync_TimeDifferenceFilter: Symbol.for('Sync_TimeDifferenceFilter'),
|
||||
Sync_UuidFilter: Symbol.for('Sync_UuidFilter'),
|
||||
Sync_ContentTypeFilter: Symbol.for('Sync_ContentTypeFilter'),
|
||||
Sync_ContentFilter: Symbol.for('Sync_ContentFilter'),
|
||||
Sync_ItemFactory: Symbol.for('Sync_ItemFactory'),
|
||||
Sync_ItemTransferCalculator: Symbol.for('Sync_ItemTransferCalculator'),
|
||||
Sync_ControllerContainer: Symbol.for('Sync_ControllerContainer'),
|
||||
Sync_HomeServerItemsController: Symbol.for('Sync_HomeServerItemsController'),
|
||||
// Mapping
|
||||
Sync_SharedVaultHttpMapper: Symbol.for('Sync_SharedVaultHttpMapper'),
|
||||
Sync_SharedVaultUserHttpMapper: Symbol.for('Sync_SharedVaultUserHttpMapper'),
|
||||
Sync_SharedVaultInviteHttpMapper: Symbol.for('Sync_SharedVaultInviteHttpMapper'),
|
||||
Sync_MessageHttpMapper: Symbol.for('Sync_MessageHttpMapper'),
|
||||
Sync_ItemPersistenceMapper: Symbol.for('Sync_ItemPersistenceMapper'),
|
||||
Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
|
||||
Sync_SavedItemHttpMapper: Symbol.for('Sync_SavedItemHttpMapper'),
|
||||
Sync_ItemConflictHttpMapper: Symbol.for('Sync_ItemConflictHttpMapper'),
|
||||
Sync_ItemBackupMapper: Symbol.for('Sync_ItemBackupMapper'),
|
||||
}
|
||||
|
||||
export default TYPES
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { ExtensionsHttpService } from './ExtensionsHttpService'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { AxiosInstance } from 'axios'
|
||||
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
describe('ExtensionsHttpService', () => {
|
||||
let httpClient: AxiosInstance
|
||||
@@ -34,9 +35,22 @@ describe('ExtensionsHttpService', () => {
|
||||
httpClient = {} as jest.Mocked<AxiosInstance>
|
||||
httpClient.request = jest.fn().mockReturnValue({ status: 200, data: { foo: 'bar' } })
|
||||
|
||||
item = {
|
||||
content: 'test',
|
||||
} as jest.Mocked<Item>
|
||||
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()
|
||||
|
||||
authParams = {} as jest.Mocked<KeyParamsData>
|
||||
|
||||
|
||||
@@ -140,11 +140,11 @@ export class ExtensionsHttpService implements ExtensionsHttpServiceInterface {
|
||||
email: string,
|
||||
): Promise<DomainEventInterface> {
|
||||
const extension = await this.itemRepository.findByUuidAndUserUuid(extensionId, userUuid)
|
||||
if (extension === null || !extension.content) {
|
||||
if (extension === null || !extension.props.content) {
|
||||
throw Error(`Could not find extensions with id ${extensionId}`)
|
||||
}
|
||||
|
||||
const content = this.contentDecoder.decode(extension.content)
|
||||
const content = this.contentDecoder.decode(extension.props.content)
|
||||
switch (this.getExtensionName(content)) {
|
||||
case ExtensionName.Dropbox:
|
||||
return this.createCloudBackupFailedEventBasedOnProvider('DROPBOX', email)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Logger } from 'winston'
|
||||
import { Item } from '../Item/Item'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
@@ -15,10 +16,22 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
||||
const createHandler = () => new AccountDeletionRequestedEventHandler(itemRepository, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
item = {
|
||||
uuid: '1-2-3',
|
||||
content: 'test',
|
||||
} as jest.Mocked<Item>
|
||||
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.findAll = jest.fn().mockReturnValue([item])
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Item } from '../Item/Item'
|
||||
import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface'
|
||||
import { DuplicateItemSyncedEventHandler } from './DuplicateItemSyncedEventHandler'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
describe('DuplicateItemSyncedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
@@ -24,14 +25,39 @@ describe('DuplicateItemSyncedEventHandler', () => {
|
||||
new DuplicateItemSyncedEventHandler(itemRepository, domainEventFactory, domainEventPublisher, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
originalItem = {
|
||||
uuid: '1-2-3',
|
||||
} as jest.Mocked<Item>
|
||||
originalItem = 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()
|
||||
|
||||
duplicateItem = {
|
||||
uuid: '2-3-4',
|
||||
duplicateOf: '1-2-3',
|
||||
} as jest.Mocked<Item>
|
||||
duplicateItem = 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: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
deleted: false,
|
||||
dates: Dates.create(new Date(1616164633241311), new Date(1616164633241311)).getValue(),
|
||||
timestamps: Timestamps.create(1616164633241311, 1616164633241311).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000001'),
|
||||
).getValue()
|
||||
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findByUuidAndUserUuid = jest
|
||||
@@ -81,7 +107,7 @@ describe('DuplicateItemSyncedEventHandler', () => {
|
||||
})
|
||||
|
||||
it('should not copy revisions if duplicate item is not pointing to duplicate anything', async () => {
|
||||
duplicateItem.duplicateOf = null
|
||||
duplicateItem.props.duplicateOf = null
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(domainEventPublisher.publish).not.toHaveBeenCalled()
|
||||
|
||||
@@ -24,22 +24,22 @@ export class DuplicateItemSyncedEventHandler implements DomainEventHandlerInterf
|
||||
return
|
||||
}
|
||||
|
||||
if (!item.duplicateOf) {
|
||||
if (!item.props.duplicateOf) {
|
||||
this.logger.warn(`Item ${event.payload.itemUuid} does not point to any duplicate`)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const existingOriginalItem = await this.itemRepository.findByUuidAndUserUuid(
|
||||
item.duplicateOf,
|
||||
item.props.duplicateOf.value,
|
||||
event.payload.userUuid,
|
||||
)
|
||||
|
||||
if (existingOriginalItem !== null) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createRevisionsCopyRequestedEvent(event.payload.userUuid, {
|
||||
originalItemUuid: existingOriginalItem.uuid,
|
||||
newItemUuid: item.uuid,
|
||||
originalItemUuid: existingOriginalItem.id.toString(),
|
||||
newItemUuid: item.id.toString(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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'
|
||||
|
||||
describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
@@ -29,10 +30,22 @@ describe('ItemRevisionCreationRequestedEventHandler', () => {
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
item = {
|
||||
uuid: '1-2-3',
|
||||
content: 'test',
|
||||
} as jest.Mocked<Item>
|
||||
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)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { IntegrityPayload } from '@standardnotes/responses'
|
||||
|
||||
export type ExtendedIntegrityPayload = IntegrityPayload & {
|
||||
content_type: ContentType
|
||||
content_type: string | null
|
||||
}
|
||||
|
||||
@@ -1,119 +1,22 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
@Entity({ name: 'items' })
|
||||
@Index('index_items_on_user_uuid_and_content_type', ['userUuid', 'contentType'])
|
||||
@Index('user_uuid_and_updated_at_timestamp_and_created_at_timestamp', [
|
||||
'userUuid',
|
||||
'updatedAtTimestamp',
|
||||
'createdAtTimestamp',
|
||||
])
|
||||
@Index('user_uuid_and_deleted', ['userUuid', 'deleted'])
|
||||
export class Item {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
declare uuid: string
|
||||
import { ItemProps } from './ItemProps'
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'duplicate_of',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare duplicateOf: string | null
|
||||
export class Item extends Entity<ItemProps> {
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
name: 'items_key_id',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare itemsKeyId: string | null
|
||||
private constructor(props: ItemProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
@Column({
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare content: string | null
|
||||
static create(props: ItemProps, id?: UniqueEntityId): Result<Item> {
|
||||
if (!props.contentSize) {
|
||||
const contentSize = Buffer.byteLength(JSON.stringify(props))
|
||||
props.contentSize = contentSize
|
||||
}
|
||||
|
||||
@Column({
|
||||
name: 'content_type',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
@Index('index_items_on_content_type')
|
||||
declare contentType: ContentType | null
|
||||
|
||||
@Column({
|
||||
name: 'content_size',
|
||||
type: 'int',
|
||||
nullable: true,
|
||||
})
|
||||
declare contentSize: number | null
|
||||
|
||||
@Column({
|
||||
name: 'enc_item_key',
|
||||
type: 'text',
|
||||
nullable: true,
|
||||
})
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column({
|
||||
name: 'auth_hash',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare authHash: string | null
|
||||
|
||||
@Column({
|
||||
name: 'user_uuid',
|
||||
length: 36,
|
||||
})
|
||||
@Index('index_items_on_user_uuid')
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
type: 'tinyint',
|
||||
precision: 1,
|
||||
nullable: true,
|
||||
default: 0,
|
||||
})
|
||||
@Index('index_items_on_deleted')
|
||||
declare deleted: boolean
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare createdAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'updated_at',
|
||||
type: 'datetime',
|
||||
precision: 6,
|
||||
})
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column({
|
||||
name: 'created_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
declare createdAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_at_timestamp',
|
||||
type: 'bigint',
|
||||
})
|
||||
@Index('updated_at_timestamp')
|
||||
declare updatedAtTimestamp: number
|
||||
|
||||
@Column({
|
||||
name: 'updated_with_session',
|
||||
type: 'varchar',
|
||||
length: 36,
|
||||
nullable: true,
|
||||
})
|
||||
declare updatedWithSession: string | null
|
||||
return Result.ok<Item>(new Item(props, id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { ItemFactory } from './ItemFactory'
|
||||
import { ItemHash } from './ItemHash'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { ItemProjection } from '../../Projection/ItemProjection'
|
||||
import { Item } from './Item'
|
||||
|
||||
describe('ItemFactory', () => {
|
||||
let timer: TimerInterface
|
||||
let itemProjector: ProjectorInterface<Item, ItemProjection>
|
||||
let timeHelper: Timer
|
||||
|
||||
const createFactory = () => new ItemFactory(timer, itemProjector)
|
||||
|
||||
beforeEach(() => {
|
||||
timeHelper = new Timer()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1616164633241568)
|
||||
timer.convertMicrosecondsToDate = jest
|
||||
.fn()
|
||||
.mockImplementation((microseconds: number) => timeHelper.convertMicrosecondsToDate(microseconds))
|
||||
timer.convertStringDateToMicroseconds = jest
|
||||
.fn()
|
||||
.mockImplementation((date: string) => timeHelper.convertStringDateToMicroseconds(date))
|
||||
timer.convertStringDateToDate = jest
|
||||
.fn()
|
||||
.mockImplementation((date: string) => timeHelper.convertStringDateToDate(date))
|
||||
|
||||
itemProjector = {} as jest.Mocked<ProjectorInterface<Item, ItemProjection>>
|
||||
itemProjector.projectFull = jest.fn().mockReturnValue({
|
||||
uuid: '1-2-3',
|
||||
items_key_id: 'foobar',
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'foobar',
|
||||
content: 'foobar',
|
||||
content_type: ContentType.Note,
|
||||
auth_hash: 'foobar',
|
||||
deleted: false,
|
||||
created_at: '2022-09-01 10:00:00',
|
||||
created_at_timestamp: 123123123123123,
|
||||
updated_at: '2022-09-01 10:00:00',
|
||||
updated_at_timestamp: 123123123123123,
|
||||
updated_with_session: '2-4-5',
|
||||
})
|
||||
})
|
||||
|
||||
it('should create an item based on item hash', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().create({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
createdAtTimestamp: 1616164633241568,
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
updatedAt: expect.any(Date),
|
||||
updatedAtTimestamp: 1616164633241568,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
contentSize: 341,
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a stub item based on item hash with update_at date and timestamps overwritten', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
updated_at: '2021-03-25T09:37:37.943Z',
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().createStub({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
createdAtTimestamp: 1616164633241568,
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
updatedAt: new Date('2021-03-25T09:37:37.943Z'),
|
||||
updatedAtTimestamp: 1616665057943000,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
content: null,
|
||||
contentSize: 341,
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a stub item based on item hash with update_at_timestamp date and timestamps overwritten', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
updated_at_timestamp: 1616164633241568,
|
||||
content: 'foobar',
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().createStub({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
createdAtTimestamp: 1616164633241568,
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
updatedAt: new Date('2021-03-19T14:37:13.241Z'),
|
||||
updatedAtTimestamp: 1616164633241568,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
content: 'foobar',
|
||||
contentSize: 341,
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a stub item based on item hash without updated timestamps', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().createStub({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
createdAtTimestamp: 1616164633241568,
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
updatedAt: expect.any(Date),
|
||||
updatedAtTimestamp: 1616164633241568,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
content: null,
|
||||
contentSize: 341,
|
||||
})
|
||||
})
|
||||
|
||||
it('should create an item based on item hash with all fields filled', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
content: 'asdqwe1',
|
||||
content_type: ContentType.Note,
|
||||
duplicate_of: '222',
|
||||
auth_hash: 'aaa',
|
||||
deleted: true,
|
||||
enc_item_key: 'qweqwe1',
|
||||
items_key_id: 'asdasd1',
|
||||
created_at: timeHelper.formatDate(new Date(1616164633241), 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'),
|
||||
updated_at: timeHelper.formatDate(new Date(1616164633242), 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'),
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().create({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
content: 'asdqwe1',
|
||||
contentSize: 341,
|
||||
contentType: 'Note',
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
createdAtTimestamp: 1616164633241000,
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
authHash: 'aaa',
|
||||
deleted: true,
|
||||
duplicateOf: '222',
|
||||
updatedAt: expect.any(Date),
|
||||
updatedAtTimestamp: 1616164633241568,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
|
||||
it('should create an item based on item hash with created at timestamp', () => {
|
||||
const itemHash = {
|
||||
uuid: '1-2-3',
|
||||
content: 'asdqwe1',
|
||||
content_type: ContentType.Note,
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'qweqwe1',
|
||||
items_key_id: 'asdasd1',
|
||||
created_at_timestamp: 1616164633241312,
|
||||
updated_at: timeHelper.formatDate(new Date(1616164633242), 'YYYY-MM-DDTHH:mm:ss.SSS[Z]'),
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
const item = createFactory().create({ userUuid: 'a-b-c', itemHash, sessionUuid: '1-2-3' })
|
||||
|
||||
expect(item).toEqual({
|
||||
content: 'asdqwe1',
|
||||
contentSize: 341,
|
||||
contentType: 'Note',
|
||||
createdAt: expect.any(Date),
|
||||
updatedWithSession: '1-2-3',
|
||||
createdAtTimestamp: 1616164633241312,
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
updatedAt: expect.any(Date),
|
||||
updatedAtTimestamp: 1616164633241568,
|
||||
userUuid: 'a-b-c',
|
||||
uuid: '1-2-3',
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { ItemProjection } from '../../Projection/ItemProjection'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { Item } from './Item'
|
||||
import { ItemFactoryInterface } from './ItemFactoryInterface'
|
||||
import { ItemHash } from './ItemHash'
|
||||
|
||||
export class ItemFactory implements ItemFactoryInterface {
|
||||
constructor(private timer: TimerInterface, private itemProjector: ProjectorInterface<Item, ItemProjection>) {}
|
||||
|
||||
createStub(dto: { userUuid: string; itemHash: ItemHash; sessionUuid: string | null }): Item {
|
||||
const item = this.create(dto)
|
||||
|
||||
if (dto.itemHash.content === undefined) {
|
||||
item.content = null
|
||||
}
|
||||
|
||||
if (dto.itemHash.updated_at_timestamp) {
|
||||
item.updatedAtTimestamp = dto.itemHash.updated_at_timestamp
|
||||
item.updatedAt = this.timer.convertMicrosecondsToDate(dto.itemHash.updated_at_timestamp)
|
||||
} else if (dto.itemHash.updated_at) {
|
||||
item.updatedAtTimestamp = this.timer.convertStringDateToMicroseconds(dto.itemHash.updated_at)
|
||||
item.updatedAt = this.timer.convertStringDateToDate(dto.itemHash.updated_at)
|
||||
}
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
create(dto: { userUuid: string; itemHash: ItemHash; sessionUuid: string | null }): Item {
|
||||
const newItem = new Item()
|
||||
newItem.uuid = dto.itemHash.uuid
|
||||
newItem.updatedWithSession = dto.sessionUuid
|
||||
newItem.contentSize = 0
|
||||
if (dto.itemHash.content) {
|
||||
newItem.content = dto.itemHash.content
|
||||
}
|
||||
newItem.userUuid = dto.userUuid
|
||||
if (dto.itemHash.content_type) {
|
||||
newItem.contentType = dto.itemHash.content_type
|
||||
}
|
||||
if (dto.itemHash.enc_item_key) {
|
||||
newItem.encItemKey = dto.itemHash.enc_item_key
|
||||
}
|
||||
if (dto.itemHash.items_key_id) {
|
||||
newItem.itemsKeyId = dto.itemHash.items_key_id
|
||||
}
|
||||
if (dto.itemHash.duplicate_of) {
|
||||
newItem.duplicateOf = dto.itemHash.duplicate_of
|
||||
}
|
||||
if (dto.itemHash.deleted !== undefined) {
|
||||
newItem.deleted = dto.itemHash.deleted
|
||||
}
|
||||
if (dto.itemHash.auth_hash) {
|
||||
newItem.authHash = dto.itemHash.auth_hash
|
||||
}
|
||||
|
||||
const now = this.timer.getTimestampInMicroseconds()
|
||||
const nowDate = this.timer.convertMicrosecondsToDate(now)
|
||||
|
||||
newItem.updatedAtTimestamp = now
|
||||
newItem.updatedAt = nowDate
|
||||
|
||||
newItem.createdAtTimestamp = now
|
||||
newItem.createdAt = nowDate
|
||||
|
||||
if (dto.itemHash.created_at_timestamp) {
|
||||
newItem.createdAtTimestamp = dto.itemHash.created_at_timestamp
|
||||
newItem.createdAt = this.timer.convertMicrosecondsToDate(dto.itemHash.created_at_timestamp)
|
||||
} else if (dto.itemHash.created_at) {
|
||||
newItem.createdAtTimestamp = this.timer.convertStringDateToMicroseconds(dto.itemHash.created_at)
|
||||
newItem.createdAt = this.timer.convertStringDateToDate(dto.itemHash.created_at)
|
||||
}
|
||||
|
||||
newItem.contentSize = Buffer.byteLength(JSON.stringify(this.itemProjector.projectFull(newItem)))
|
||||
|
||||
return newItem
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { Item } from './Item'
|
||||
import { ItemHash } from './ItemHash'
|
||||
|
||||
export interface ItemFactoryInterface {
|
||||
create(dto: { userUuid: string; itemHash: ItemHash; sessionUuid: string | null }): Item
|
||||
createStub(dto: { userUuid: string; itemHash: ItemHash; sessionUuid: string | null }): Item
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
export type ItemHash = {
|
||||
uuid: string
|
||||
content?: string
|
||||
content_type: ContentType
|
||||
content_type: string | null
|
||||
deleted?: boolean
|
||||
duplicate_of?: string | null
|
||||
auth_hash?: string
|
||||
|
||||
16
packages/syncing-server/src/Domain/Item/ItemProps.ts
Normal file
16
packages/syncing-server/src/Domain/Item/ItemProps.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ContentType, Dates, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export interface ItemProps {
|
||||
duplicateOf: Uuid | null
|
||||
itemsKeyId: string | null
|
||||
content: string | null
|
||||
contentType: ContentType
|
||||
encItemKey: string | null
|
||||
authHash: string | null
|
||||
userUuid: Uuid
|
||||
deleted: boolean
|
||||
updatedWithSession: Uuid | null
|
||||
dates: Dates
|
||||
timestamps: Timestamps
|
||||
contentSize?: number
|
||||
}
|
||||
@@ -16,8 +16,8 @@ export interface ItemRepositoryInterface {
|
||||
findItemsForComputingIntegrityPayloads(userUuid: string): Promise<ExtendedIntegrityPayload[]>
|
||||
findByUuidAndUserUuid(uuid: string, userUuid: string): Promise<Item | null>
|
||||
findByUuid(uuid: string): Promise<Item | null>
|
||||
remove(item: Item): Promise<Item>
|
||||
save(item: Item): Promise<Item>
|
||||
remove(item: Item): Promise<void>
|
||||
save(item: Item): Promise<void>
|
||||
markItemsAsDeleted(itemUuids: Array<string>, updatedAtTimestamp: number): Promise<void>
|
||||
updateContentSize(itemUuid: string, contentSize: number): Promise<void>
|
||||
}
|
||||
|
||||
@@ -1,94 +1,101 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { Item } from './Item'
|
||||
import { ItemHash } from './ItemHash'
|
||||
|
||||
import { ItemRepositoryInterface } from './ItemRepositoryInterface'
|
||||
import { ItemService } from './ItemService'
|
||||
import { ApiVersion } from '../Api/ApiVersion'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { Logger } from 'winston'
|
||||
import { Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { ItemSaveValidatorInterface } from './SaveValidator/ItemSaveValidatorInterface'
|
||||
import { ItemFactoryInterface } from './ItemFactoryInterface'
|
||||
import { ItemConflict } from './ItemConflict'
|
||||
import { ItemTransferCalculatorInterface } from './ItemTransferCalculatorInterface'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { ItemProjection } from '../../Projection/ItemProjection'
|
||||
import { SaveNewItem } from '../UseCase/Syncing/SaveNewItem/SaveNewItem'
|
||||
import { UpdateExistingItem } from '../UseCase/Syncing/UpdateExistingItem/UpdateExistingItem'
|
||||
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId, Result } from '@standardnotes/domain-core'
|
||||
|
||||
describe('ItemService', () => {
|
||||
let itemRepository: ItemRepositoryInterface
|
||||
let domainEventPublisher: DomainEventPublisherInterface
|
||||
let domainEventFactory: DomainEventFactoryInterface
|
||||
const revisionFrequency = 300
|
||||
const contentSizeTransferLimit = 100
|
||||
let timer: TimerInterface
|
||||
let item1: Item
|
||||
let item2: Item
|
||||
let itemHash1: ItemHash
|
||||
let itemHash2: ItemHash
|
||||
let emptyHash: ItemHash
|
||||
let syncToken: string
|
||||
let logger: Logger
|
||||
let itemSaveValidator: ItemSaveValidatorInterface
|
||||
let newItem: Item
|
||||
let itemFactory: ItemFactoryInterface
|
||||
let timeHelper: Timer
|
||||
let itemTransferCalculator: ItemTransferCalculatorInterface
|
||||
let itemProjector: ProjectorInterface<Item, ItemProjection>
|
||||
let saveNewItemUseCase: SaveNewItem
|
||||
let updateExistingItemUseCase: UpdateExistingItem
|
||||
const maxItemsSyncLimit = 300
|
||||
|
||||
const createService = () =>
|
||||
new ItemService(
|
||||
itemSaveValidator,
|
||||
itemFactory,
|
||||
itemRepository,
|
||||
domainEventPublisher,
|
||||
domainEventFactory,
|
||||
revisionFrequency,
|
||||
contentSizeTransferLimit,
|
||||
itemTransferCalculator,
|
||||
timer,
|
||||
itemProjector,
|
||||
maxItemsSyncLimit,
|
||||
saveNewItemUseCase,
|
||||
updateExistingItemUseCase,
|
||||
logger,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
timeHelper = new Timer()
|
||||
|
||||
item1 = {
|
||||
uuid: '1-2-3',
|
||||
userUuid: '1-2-3',
|
||||
createdAt: new Date(1616164633241311),
|
||||
createdAtTimestamp: 1616164633241311,
|
||||
updatedAt: new Date(1616164633241311),
|
||||
updatedAtTimestamp: 1616164633241311,
|
||||
} as jest.Mocked<Item>
|
||||
item2 = {
|
||||
uuid: '2-3-4',
|
||||
userUuid: '1-2-3',
|
||||
createdAt: new Date(1616164633241312),
|
||||
createdAtTimestamp: 1616164633241312,
|
||||
updatedAt: new Date(1616164633241312),
|
||||
updatedAtTimestamp: 1616164633241312,
|
||||
} as jest.Mocked<Item>
|
||||
item1 = 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()
|
||||
item2 = Item.create(
|
||||
{
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
updatedWithSession: null,
|
||||
content: 'foobar2',
|
||||
contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
itemsKeyId: null,
|
||||
duplicateOf: null,
|
||||
deleted: false,
|
||||
dates: Dates.create(new Date(1616164633241312), new Date(1616164633241312)).getValue(),
|
||||
timestamps: Timestamps.create(1616164633241312, 1616164633241312).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000001'),
|
||||
).getValue()
|
||||
|
||||
itemHash1 = {
|
||||
uuid: '1-2-3',
|
||||
content: 'asdqwe1',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'qweqwe1',
|
||||
items_key_id: 'asdasd1',
|
||||
created_at: timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(item1.createdAtTimestamp),
|
||||
timeHelper.convertMicrosecondsToDate(item1.props.timestamps.createdAt),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
updated_at: timeHelper.formatDate(
|
||||
new Date(timeHelper.convertMicrosecondsToMilliseconds(item1.updatedAtTimestamp) + 1),
|
||||
new Date(timeHelper.convertMicrosecondsToMilliseconds(item1.props.timestamps.updatedAt) + 1),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
} as jest.Mocked<ItemHash>
|
||||
@@ -96,31 +103,28 @@ describe('ItemService', () => {
|
||||
itemHash2 = {
|
||||
uuid: '2-3-4',
|
||||
content: 'asdqwe2',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'qweqwe2',
|
||||
items_key_id: 'asdasd2',
|
||||
created_at: timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(item2.createdAtTimestamp),
|
||||
timeHelper.convertMicrosecondsToDate(item2.props.timestamps.createdAt),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
updated_at: timeHelper.formatDate(
|
||||
new Date(timeHelper.convertMicrosecondsToMilliseconds(item2.updatedAtTimestamp) + 1),
|
||||
new Date(timeHelper.convertMicrosecondsToMilliseconds(item2.props.timestamps.updatedAt) + 1),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
emptyHash = {
|
||||
uuid: '2-3-4',
|
||||
} as jest.Mocked<ItemHash>
|
||||
|
||||
itemTransferCalculator = {} as jest.Mocked<ItemTransferCalculatorInterface>
|
||||
itemTransferCalculator.computeItemUuidsToFetch = jest.fn().mockReturnValue([item1.uuid, item2.uuid])
|
||||
itemTransferCalculator.computeItemUuidsToFetch = jest
|
||||
.fn()
|
||||
.mockReturnValue([item1.id.toString(), item2.id.toString()])
|
||||
|
||||
itemRepository = {} as jest.Mocked<ItemRepositoryInterface>
|
||||
itemRepository.findAll = jest.fn().mockReturnValue([item1, item2])
|
||||
itemRepository.countAll = jest.fn().mockReturnValue(2)
|
||||
itemRepository.save = jest.fn().mockImplementation((item: Item) => item)
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1616164633241568)
|
||||
@@ -136,13 +140,6 @@ describe('ItemService', () => {
|
||||
.fn()
|
||||
.mockImplementation((microseconds: number) => timeHelper.convertMicrosecondsToDate(microseconds))
|
||||
|
||||
domainEventPublisher = {} as jest.Mocked<DomainEventPublisherInterface>
|
||||
domainEventPublisher.publish = jest.fn()
|
||||
|
||||
domainEventFactory = {} as jest.Mocked<DomainEventFactoryInterface>
|
||||
domainEventFactory.createDuplicateItemSyncedEvent = jest.fn()
|
||||
domainEventFactory.createItemRevisionCreationRequested = jest.fn()
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.error = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
@@ -152,31 +149,28 @@ describe('ItemService', () => {
|
||||
itemSaveValidator = {} as jest.Mocked<ItemSaveValidatorInterface>
|
||||
itemSaveValidator.validate = jest.fn().mockReturnValue({ passed: true })
|
||||
|
||||
newItem = {
|
||||
contentType: ContentType.Note,
|
||||
} as jest.Mocked<Item>
|
||||
newItem = Item.create(
|
||||
{
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
updatedWithSession: null,
|
||||
content: 'foobar2',
|
||||
contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
itemsKeyId: null,
|
||||
duplicateOf: null,
|
||||
deleted: false,
|
||||
dates: Dates.create(new Date(1616164633241313), new Date(1616164633241313)).getValue(),
|
||||
timestamps: Timestamps.create(1616164633241313, 1616164633241313).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000002'),
|
||||
).getValue()
|
||||
|
||||
itemFactory = {} as jest.Mocked<ItemFactoryInterface>
|
||||
itemFactory.create = jest.fn().mockReturnValue(newItem)
|
||||
itemFactory.createStub = jest.fn().mockReturnValue(newItem)
|
||||
saveNewItemUseCase = {} as jest.Mocked<SaveNewItem>
|
||||
saveNewItemUseCase.execute = jest.fn().mockReturnValue(Result.ok(newItem))
|
||||
|
||||
itemProjector = {} as jest.Mocked<ProjectorInterface<Item, ItemProjection>>
|
||||
itemProjector.projectFull = jest.fn().mockReturnValue({
|
||||
uuid: '1-2-3',
|
||||
items_key_id: 'foobar',
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'foobar',
|
||||
content:
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed viverra tellus in hac habitasse. Tortor posuere ac ut consequat semper. Ut diam quam nulla porttitor. Sapien pellentesque habitant morbi tristique senectus et netus et malesuada. Dapibus ultrices in iaculis nunc. Pellentesque habitant morbi tristique senectus et netus et malesuada fames. Faucibus et molestie ac feugiat sed lectus vestibulum mattis. Eu consequat ac felis donec. Eget velit aliquet sagittis id. Nullam eget felis eget nunc. Turpis in eu mi bibendum neque egestas congue.',
|
||||
content_type: ContentType.Note,
|
||||
auth_hash: 'foobar',
|
||||
deleted: false,
|
||||
created_at: '2022-09-01 10:00:00',
|
||||
created_at_timestamp: 123123123123123,
|
||||
updated_at: '2022-09-01 10:00:00',
|
||||
updated_at_timestamp: 123123123123123,
|
||||
updated_with_session: '2-4-5',
|
||||
})
|
||||
updateExistingItemUseCase = {} as jest.Mocked<UpdateExistingItem>
|
||||
updateExistingItemUseCase.execute = jest.fn().mockReturnValue(Result.ok(item1))
|
||||
})
|
||||
|
||||
it('should retrieve all items for a user from last sync with sync token version 1', async () => {
|
||||
@@ -186,7 +180,7 @@ describe('ItemService', () => {
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
}),
|
||||
).toEqual({
|
||||
items: [item1, item2],
|
||||
@@ -202,7 +196,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortOrder: 'ASC',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
})
|
||||
@@ -213,7 +207,7 @@ describe('ItemService', () => {
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
}),
|
||||
).toEqual({
|
||||
items: [item1, item2],
|
||||
@@ -229,7 +223,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -240,7 +234,7 @@ describe('ItemService', () => {
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
limit: 1000,
|
||||
}),
|
||||
).toEqual({
|
||||
@@ -257,7 +251,7 @@ describe('ItemService', () => {
|
||||
limit: 300,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -270,7 +264,7 @@ describe('ItemService', () => {
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
}),
|
||||
).toEqual({
|
||||
items: [],
|
||||
@@ -295,7 +289,7 @@ describe('ItemService', () => {
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
limit: 1,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
})
|
||||
|
||||
expect(itemsResponse).toEqual({
|
||||
@@ -315,7 +309,7 @@ describe('ItemService', () => {
|
||||
limit: 1,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -329,7 +323,7 @@ describe('ItemService', () => {
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
cursorToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
}),
|
||||
).toEqual({
|
||||
items: [item1, item2],
|
||||
@@ -345,7 +339,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -355,7 +349,7 @@ describe('ItemService', () => {
|
||||
expect(
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
}),
|
||||
).toEqual({
|
||||
items: [item1, item2],
|
||||
@@ -371,7 +365,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -381,7 +375,7 @@ describe('ItemService', () => {
|
||||
await createService().getItems({
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
})
|
||||
|
||||
expect(itemRepository.countAll).toHaveBeenCalledWith({
|
||||
@@ -394,7 +388,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortOrder: 'ASC',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
})
|
||||
@@ -405,7 +399,7 @@ describe('ItemService', () => {
|
||||
userUuid: '1-2-3',
|
||||
syncToken,
|
||||
limit: 0,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
})
|
||||
|
||||
expect(itemRepository.countAll).toHaveBeenCalledWith({
|
||||
@@ -418,7 +412,7 @@ describe('ItemService', () => {
|
||||
limit: 150,
|
||||
})
|
||||
expect(itemRepository.findAll).toHaveBeenCalledWith({
|
||||
uuids: ['1-2-3', '2-3-4'],
|
||||
uuids: ['00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000001'],
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
@@ -432,7 +426,7 @@ describe('ItemService', () => {
|
||||
userUuid: '1-2-3',
|
||||
syncToken: '2:',
|
||||
limit: 0,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
@@ -449,7 +443,7 @@ describe('ItemService', () => {
|
||||
userUuid: '1-2-3',
|
||||
syncToken: '1234567890',
|
||||
limit: 0,
|
||||
contentType: ContentType.Note,
|
||||
contentType: ContentType.TYPES.Note,
|
||||
})
|
||||
} catch (e) {
|
||||
error = e
|
||||
@@ -459,12 +453,38 @@ describe('ItemService', () => {
|
||||
})
|
||||
|
||||
it('should front load keys items to top of the collection for better client performance', async () => {
|
||||
const item3 = {
|
||||
uuid: '1-2-3',
|
||||
} as jest.Mocked<Item>
|
||||
const item4 = {
|
||||
uuid: '4-5-6',
|
||||
} as jest.Mocked<Item>
|
||||
const item3 = 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-000000000003'),
|
||||
).getValue()
|
||||
const item4 = Item.create(
|
||||
{
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
updatedWithSession: null,
|
||||
content: 'foobar2',
|
||||
contentType: ContentType.create(ContentType.TYPES.Note).getValue(),
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
itemsKeyId: null,
|
||||
duplicateOf: null,
|
||||
deleted: false,
|
||||
dates: Dates.create(new Date(1616164633241312), new Date(1616164633241312)).getValue(),
|
||||
timestamps: Timestamps.create(1616164633241312, 1616164633241312).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000004'),
|
||||
).getValue()
|
||||
|
||||
itemRepository.findAll = jest.fn().mockReturnValue([item3, item4])
|
||||
|
||||
@@ -485,11 +505,10 @@ describe('ItemService', () => {
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [newItem],
|
||||
syncToken: 'MjpOYU4=',
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTMxNA==',
|
||||
})
|
||||
|
||||
expect(domainEventFactory.createItemRevisionCreationRequested).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(1)
|
||||
expect(saveNewItemUseCase.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not save new items in read only access mode', async () => {
|
||||
@@ -513,16 +532,29 @@ describe('ItemService', () => {
|
||||
savedItems: [],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
|
||||
expect(saveNewItemUseCase.execute).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should save new items that are duplicates', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(null)
|
||||
const duplicateItem = {
|
||||
updatedAtTimestamp: 1616164633241570,
|
||||
duplicateOf: '1-2-3',
|
||||
contentType: ContentType.Note,
|
||||
} as jest.Mocked<Item>
|
||||
itemFactory.create = jest.fn().mockReturnValueOnce(duplicateItem)
|
||||
const duplicateItem = 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: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(),
|
||||
deleted: false,
|
||||
dates: Dates.create(new Date(1616164633241570), new Date(1616164633241570)).getValue(),
|
||||
timestamps: Timestamps.create(1616164633241570, 1616164633241570).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000005'),
|
||||
).getValue()
|
||||
saveNewItemUseCase.execute = jest.fn().mockReturnValue(Result.ok(duplicateItem))
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
@@ -537,10 +569,6 @@ describe('ItemService', () => {
|
||||
savedItems: [duplicateItem],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU3MQ==',
|
||||
})
|
||||
|
||||
expect(domainEventFactory.createItemRevisionCreationRequested).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(2)
|
||||
expect(domainEventFactory.createDuplicateItemSyncedEvent).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should skip items that are conflicting on validation', async () => {
|
||||
@@ -568,7 +596,7 @@ describe('ItemService', () => {
|
||||
it('should mark items as saved that are skipped on validation', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const skipped = {} as jest.Mocked<Item>
|
||||
const skipped = item1
|
||||
const validationResult = { passed: false, skipped }
|
||||
itemSaveValidator.validate = jest.fn().mockReturnValue(validationResult)
|
||||
|
||||
@@ -583,7 +611,7 @@ describe('ItemService', () => {
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [skipped],
|
||||
syncToken: 'MjpOYU4=',
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTMxMg==',
|
||||
})
|
||||
})
|
||||
|
||||
@@ -593,7 +621,7 @@ describe('ItemService', () => {
|
||||
const itemHash3 = {
|
||||
uuid: '3-4-5',
|
||||
content: 'asdqwe3',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'qweqwe3',
|
||||
items_key_id: 'asdasd3',
|
||||
@@ -607,11 +635,41 @@ describe('ItemService', () => {
|
||||
const item3Timestamp = 1616164633241569
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValueOnce(saveProcedureStartTimestamp)
|
||||
|
||||
itemFactory.create = jest
|
||||
saveNewItemUseCase.execute = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce({ updatedAtTimestamp: item1Timestamp, duplicateOf: null } as jest.Mocked<Item>)
|
||||
.mockReturnValueOnce({ updatedAtTimestamp: item2Timestamp, duplicateOf: null } as jest.Mocked<Item>)
|
||||
.mockReturnValueOnce({ updatedAtTimestamp: item3Timestamp, duplicateOf: null } as jest.Mocked<Item>)
|
||||
.mockReturnValueOnce(
|
||||
Result.ok(
|
||||
Item.create(
|
||||
{
|
||||
...item1.props,
|
||||
timestamps: Timestamps.create(item1Timestamp, item1Timestamp).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000001'),
|
||||
).getValue(),
|
||||
),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
Result.ok(
|
||||
Item.create(
|
||||
{
|
||||
...item2.props,
|
||||
timestamps: Timestamps.create(item2Timestamp, item2Timestamp).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000002'),
|
||||
).getValue(),
|
||||
),
|
||||
)
|
||||
.mockReturnValueOnce(
|
||||
Result.ok(
|
||||
Item.create(
|
||||
{
|
||||
...item2.props,
|
||||
timestamps: Timestamps.create(item3Timestamp, item3Timestamp).getValue(),
|
||||
},
|
||||
new UniqueEntityId('00000000-0000-0000-0000-000000000003'),
|
||||
).getValue(),
|
||||
),
|
||||
)
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1, itemHash3, itemHash2],
|
||||
@@ -638,66 +696,14 @@ describe('ItemService', () => {
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
savedItems: [item1],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTMxMg==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update existing items from legacy clients', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
delete itemHash1.updated_at
|
||||
delete itemHash1.updated_at_timestamp
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20161215,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update existing items with created_at_timestamp', async () => {
|
||||
itemHash1.created_at_timestamp = 123
|
||||
itemHash1.updated_at_timestamp = item1.updatedAtTimestamp
|
||||
it('should mark as skipped existing items that failed to update', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
updateExistingItemUseCase.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
@@ -708,240 +714,49 @@ describe('ItemService', () => {
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
conflicts: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: 123,
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
type: 'uuid_conflict',
|
||||
unsavedItem: itemHash1,
|
||||
},
|
||||
],
|
||||
savedItems: [],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update existing empty hashes', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item2)
|
||||
emptyHash.updated_at = timeHelper.formatDate(
|
||||
new Date(timeHelper.convertMicrosecondsToMilliseconds(item2.updatedAtTimestamp) + 1),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
)
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [emptyHash],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
contentSize: 950,
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '2-3-4',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should create a revision for existing item if revisions frequency is matched', async () => {
|
||||
timer.convertMicrosecondsToSeconds = itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update existing items with empty user-agent', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should update existing items with auth hash', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
itemHash1.auth_hash = 'test'
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
itemsKeyId: 'asdasd1',
|
||||
authHash: 'test',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should mark existing item as deleted', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
itemHash1.deleted = true
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: null,
|
||||
contentSize: 0,
|
||||
authHash: null,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: null,
|
||||
deleted: true,
|
||||
itemsKeyId: null,
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should mark existing item as duplicate', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(item1)
|
||||
|
||||
itemHash1.duplicate_of = '1-2-3'
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [],
|
||||
savedItems: [
|
||||
{
|
||||
content: 'asdqwe1',
|
||||
contentSize: 950,
|
||||
contentType: 'Note',
|
||||
createdAtTimestamp: expect.any(Number),
|
||||
createdAt: expect.any(Date),
|
||||
encItemKey: 'qweqwe1',
|
||||
duplicateOf: '1-2-3',
|
||||
itemsKeyId: 'asdasd1',
|
||||
userUuid: '1-2-3',
|
||||
updatedAtTimestamp: expect.any(Number),
|
||||
updatedAt: expect.any(Date),
|
||||
updatedWithSession: '2-3-4',
|
||||
uuid: '1-2-3',
|
||||
},
|
||||
],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
expect(domainEventPublisher.publish).toHaveBeenCalledTimes(2)
|
||||
expect(domainEventFactory.createDuplicateItemSyncedEvent).toHaveBeenCalledTimes(1)
|
||||
expect(domainEventFactory.createItemRevisionCreationRequested).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('should skip saving conflicting items and mark them as sync conflicts when saving to database fails', async () => {
|
||||
it('should skip saving conflicting items and mark them as sync conflicts when saving fails', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(null)
|
||||
itemRepository.save = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Something bad happened')
|
||||
saveNewItemUseCase.execute = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const result = await createService().saveItems({
|
||||
itemHashes: [itemHash1, itemHash2],
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
readOnlyAccess: false,
|
||||
sessionUuid: '2-3-4',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
conflicts: [
|
||||
{
|
||||
type: 'uuid_conflict',
|
||||
unsavedItem: itemHash1,
|
||||
},
|
||||
{
|
||||
type: 'uuid_conflict',
|
||||
unsavedItem: itemHash2,
|
||||
},
|
||||
],
|
||||
savedItems: [],
|
||||
syncToken: 'MjoxNjE2MTY0NjMzLjI0MTU2OQ==',
|
||||
})
|
||||
})
|
||||
|
||||
it('should skip saving conflicting items and mark them as sync conflicts when saving throws an error', async () => {
|
||||
itemRepository.findByUuid = jest.fn().mockReturnValue(null)
|
||||
saveNewItemUseCase.execute = jest.fn().mockImplementation(() => {
|
||||
throw new Error('Oops')
|
||||
})
|
||||
|
||||
const result = await createService().saveItems({
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { Time, TimerInterface } from '@standardnotes/time'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface'
|
||||
import { GetItemsDTO } from './GetItemsDTO'
|
||||
import { GetItemsResult } from './GetItemsResult'
|
||||
import { Item } from './Item'
|
||||
import { ItemConflict } from './ItemConflict'
|
||||
import { ItemFactoryInterface } from './ItemFactoryInterface'
|
||||
import { ItemHash } from './ItemHash'
|
||||
import { ItemQuery } from './ItemQuery'
|
||||
import { ItemRepositoryInterface } from './ItemRepositoryInterface'
|
||||
import { ItemServiceInterface } from './ItemServiceInterface'
|
||||
@@ -18,8 +13,9 @@ import { SaveItemsResult } from './SaveItemsResult'
|
||||
import { ItemSaveValidatorInterface } from './SaveValidator/ItemSaveValidatorInterface'
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
import { ItemTransferCalculatorInterface } from './ItemTransferCalculatorInterface'
|
||||
import { ProjectorInterface } from '../../Projection/ProjectorInterface'
|
||||
import { ItemProjection } from '../../Projection/ItemProjection'
|
||||
import { SaveNewItem } from '../UseCase/Syncing/SaveNewItem/SaveNewItem'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
import { UpdateExistingItem } from '../UseCase/Syncing/UpdateExistingItem/UpdateExistingItem'
|
||||
|
||||
export class ItemService implements ItemServiceInterface {
|
||||
private readonly DEFAULT_ITEMS_LIMIT = 150
|
||||
@@ -27,16 +23,13 @@ export class ItemService implements ItemServiceInterface {
|
||||
|
||||
constructor(
|
||||
private itemSaveValidator: ItemSaveValidatorInterface,
|
||||
private itemFactory: ItemFactoryInterface,
|
||||
private itemRepository: ItemRepositoryInterface,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
private revisionFrequency: number,
|
||||
private contentSizeTransferLimit: number,
|
||||
private itemTransferCalculator: ItemTransferCalculatorInterface,
|
||||
private timer: TimerInterface,
|
||||
private itemProjector: ProjectorInterface<Item, ItemProjection>,
|
||||
private maxItemsSyncLimit: number,
|
||||
private saveNewItem: SaveNewItem,
|
||||
private updateExistingItem: UpdateExistingItem,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
@@ -73,7 +66,7 @@ export class ItemService implements ItemServiceInterface {
|
||||
|
||||
let cursorToken = undefined
|
||||
if (totalItemsCount > upperBoundLimit) {
|
||||
const lastSyncTime = items[items.length - 1].updatedAtTimestamp / Time.MicrosecondsInASecond
|
||||
const lastSyncTime = items[items.length - 1].props.timestamps.updatedAt / Time.MicrosecondsInASecond
|
||||
cursorToken = Buffer.from(`${this.SYNC_TOKEN_VERSION}:${lastSyncTime}`, 'utf-8').toString('base64')
|
||||
}
|
||||
|
||||
@@ -118,15 +111,47 @@ export class ItemService implements ItemServiceInterface {
|
||||
}
|
||||
|
||||
if (existingItem) {
|
||||
const updatedItem = await this.updateExistingItem({
|
||||
const udpatedItemOrError = await this.updateExistingItem.execute({
|
||||
existingItem,
|
||||
itemHash,
|
||||
sessionUuid: dto.sessionUuid,
|
||||
})
|
||||
if (udpatedItemOrError.isFailed()) {
|
||||
this.logger.error(
|
||||
`[${dto.userUuid}] Updating item ${itemHash.uuid} failed. Error: ${udpatedItemOrError.getError()}`,
|
||||
)
|
||||
|
||||
conflicts.push({
|
||||
unsavedItem: itemHash,
|
||||
type: ConflictType.UuidConflict,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
const updatedItem = udpatedItemOrError.getValue()
|
||||
|
||||
savedItems.push(updatedItem)
|
||||
} else {
|
||||
try {
|
||||
const newItem = await this.saveNewItem({ userUuid: dto.userUuid, itemHash, sessionUuid: dto.sessionUuid })
|
||||
const newItemOrError = await this.saveNewItem.execute({
|
||||
userUuid: dto.userUuid,
|
||||
itemHash,
|
||||
sessionUuid: dto.sessionUuid,
|
||||
})
|
||||
if (newItemOrError.isFailed()) {
|
||||
this.logger.error(
|
||||
`[${dto.userUuid}] Saving item ${itemHash.uuid} failed. Error: ${newItemOrError.getError()}`,
|
||||
)
|
||||
|
||||
conflicts.push({
|
||||
unsavedItem: itemHash,
|
||||
type: ConflictType.UuidConflict,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
const newItem = newItemOrError.getValue()
|
||||
|
||||
savedItems.push(newItem)
|
||||
} catch (error) {
|
||||
this.logger.error(`[${dto.userUuid}] Saving item ${itemHash.uuid} failed. Error: ${(error as Error).message}`)
|
||||
@@ -153,15 +178,15 @@ export class ItemService implements ItemServiceInterface {
|
||||
async frontLoadKeysItemsToTop(userUuid: string, retrievedItems: Array<Item>): Promise<Array<Item>> {
|
||||
const itemsKeys = await this.itemRepository.findAll({
|
||||
userUuid,
|
||||
contentType: ContentType.ItemsKey,
|
||||
contentType: ContentType.TYPES.ItemsKey,
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'ASC',
|
||||
})
|
||||
|
||||
const retrievedItemsIds: Array<string> = retrievedItems.map((item: Item) => item.uuid)
|
||||
const retrievedItemsIds: Array<string> = retrievedItems.map((item: Item) => item.id.toString())
|
||||
|
||||
itemsKeys.forEach((itemKey: Item) => {
|
||||
if (retrievedItemsIds.indexOf(itemKey.uuid) === -1) {
|
||||
if (retrievedItemsIds.indexOf(itemKey.id.toString()) === -1) {
|
||||
retrievedItems.unshift(itemKey)
|
||||
}
|
||||
})
|
||||
@@ -172,9 +197,9 @@ export class ItemService implements ItemServiceInterface {
|
||||
private calculateSyncToken(lastUpdatedTimestamp: number, savedItems: Array<Item>): string {
|
||||
if (savedItems.length) {
|
||||
const sortedItems = savedItems.sort((itemA: Item, itemB: Item) => {
|
||||
return itemA.updatedAtTimestamp > itemB.updatedAtTimestamp ? 1 : -1
|
||||
return itemA.props.timestamps.updatedAt > itemB.props.timestamps.updatedAt ? 1 : -1
|
||||
})
|
||||
lastUpdatedTimestamp = sortedItems[sortedItems.length - 1].updatedAtTimestamp
|
||||
lastUpdatedTimestamp = sortedItems[sortedItems.length - 1].props.timestamps.updatedAt
|
||||
}
|
||||
|
||||
const lastUpdatedTimestampWithMicrosecondPreventingSyncDoubles = lastUpdatedTimestamp + 1
|
||||
@@ -187,103 +212,6 @@ export class ItemService implements ItemServiceInterface {
|
||||
).toString('base64')
|
||||
}
|
||||
|
||||
private async updateExistingItem(dto: {
|
||||
existingItem: Item
|
||||
itemHash: ItemHash
|
||||
sessionUuid: string | null
|
||||
}): Promise<Item> {
|
||||
dto.existingItem.updatedWithSession = dto.sessionUuid
|
||||
dto.existingItem.contentSize = 0
|
||||
if (dto.itemHash.content) {
|
||||
dto.existingItem.content = dto.itemHash.content
|
||||
}
|
||||
if (dto.itemHash.content_type) {
|
||||
dto.existingItem.contentType = dto.itemHash.content_type
|
||||
}
|
||||
if (dto.itemHash.deleted !== undefined) {
|
||||
dto.existingItem.deleted = dto.itemHash.deleted
|
||||
}
|
||||
let wasMarkedAsDuplicate = false
|
||||
if (dto.itemHash.duplicate_of) {
|
||||
wasMarkedAsDuplicate = !dto.existingItem.duplicateOf
|
||||
dto.existingItem.duplicateOf = dto.itemHash.duplicate_of
|
||||
}
|
||||
if (dto.itemHash.auth_hash) {
|
||||
dto.existingItem.authHash = dto.itemHash.auth_hash
|
||||
}
|
||||
if (dto.itemHash.enc_item_key) {
|
||||
dto.existingItem.encItemKey = dto.itemHash.enc_item_key
|
||||
}
|
||||
if (dto.itemHash.items_key_id) {
|
||||
dto.existingItem.itemsKeyId = dto.itemHash.items_key_id
|
||||
}
|
||||
|
||||
const updatedAt = this.timer.getTimestampInMicroseconds()
|
||||
const secondsFromLastUpdate = this.timer.convertMicrosecondsToSeconds(
|
||||
updatedAt - dto.existingItem.updatedAtTimestamp,
|
||||
)
|
||||
|
||||
if (dto.itemHash.created_at_timestamp) {
|
||||
dto.existingItem.createdAtTimestamp = dto.itemHash.created_at_timestamp
|
||||
dto.existingItem.createdAt = this.timer.convertMicrosecondsToDate(dto.itemHash.created_at_timestamp)
|
||||
} else if (dto.itemHash.created_at) {
|
||||
dto.existingItem.createdAtTimestamp = this.timer.convertStringDateToMicroseconds(dto.itemHash.created_at)
|
||||
dto.existingItem.createdAt = this.timer.convertStringDateToDate(dto.itemHash.created_at)
|
||||
}
|
||||
|
||||
dto.existingItem.updatedAtTimestamp = updatedAt
|
||||
dto.existingItem.updatedAt = this.timer.convertMicrosecondsToDate(updatedAt)
|
||||
|
||||
dto.existingItem.contentSize = Buffer.byteLength(JSON.stringify(this.itemProjector.projectFull(dto.existingItem)))
|
||||
|
||||
if (dto.itemHash.deleted === true) {
|
||||
dto.existingItem.deleted = true
|
||||
dto.existingItem.content = null
|
||||
dto.existingItem.contentSize = 0
|
||||
dto.existingItem.encItemKey = null
|
||||
dto.existingItem.authHash = null
|
||||
dto.existingItem.itemsKeyId = null
|
||||
}
|
||||
|
||||
const savedItem = await this.itemRepository.save(dto.existingItem)
|
||||
|
||||
if (secondsFromLastUpdate >= this.revisionFrequency) {
|
||||
if ([ContentType.Note, ContentType.File].includes(savedItem.contentType as ContentType)) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createItemRevisionCreationRequested(savedItem.uuid, savedItem.userUuid),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (wasMarkedAsDuplicate) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createDuplicateItemSyncedEvent(savedItem.uuid, savedItem.userUuid),
|
||||
)
|
||||
}
|
||||
|
||||
return savedItem
|
||||
}
|
||||
|
||||
private async saveNewItem(dto: { userUuid: string; itemHash: ItemHash; sessionUuid: string | null }): Promise<Item> {
|
||||
const newItem = this.itemFactory.create(dto)
|
||||
|
||||
const savedItem = await this.itemRepository.save(newItem)
|
||||
|
||||
if ([ContentType.Note, ContentType.File].includes(savedItem.contentType as ContentType)) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createItemRevisionCreationRequested(savedItem.uuid, savedItem.userUuid),
|
||||
)
|
||||
}
|
||||
|
||||
if (savedItem.duplicateOf) {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createDuplicateItemSyncedEvent(savedItem.uuid, savedItem.userUuid),
|
||||
)
|
||||
}
|
||||
|
||||
return savedItem
|
||||
}
|
||||
|
||||
private getLastSyncTime(dto: GetItemsDTO): number | undefined {
|
||||
let token = dto.syncToken
|
||||
if (dto.cursorToken !== undefined && dto.cursorToken !== null) {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { Item } from '../Item'
|
||||
|
||||
import { ContentFilter } from './ContentFilter'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
describe('ContentFilter', () => {
|
||||
let existingItem: Item
|
||||
@@ -21,7 +20,7 @@ describe('ContentFilter', () => {
|
||||
itemHash: {
|
||||
uuid: '123e4567-e89b-12d3-a456-426655440000',
|
||||
content: invalidContent as unknown as string,
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem: null,
|
||||
})
|
||||
@@ -32,7 +31,7 @@ describe('ContentFilter', () => {
|
||||
unsavedItem: {
|
||||
uuid: '123e4567-e89b-12d3-a456-426655440000',
|
||||
content: invalidContent,
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
type: 'content_error',
|
||||
},
|
||||
@@ -50,7 +49,7 @@ describe('ContentFilter', () => {
|
||||
itemHash: {
|
||||
uuid: '123e4567-e89b-12d3-a456-426655440000',
|
||||
content: validContent as unknown as string,
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem,
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { Item } from '../Item'
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('ContentTypeFilter', () => {
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '123e4567-e89b-12d3-a456-426655440000',
|
||||
content_type: invalidContentType as ContentType,
|
||||
content_type: invalidContentType,
|
||||
},
|
||||
existingItem: null,
|
||||
})
|
||||
@@ -54,7 +54,7 @@ describe('ContentTypeFilter', () => {
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '123e4567-e89b-12d3-a456-426655440000',
|
||||
content_type: validContentType as ContentType,
|
||||
content_type: validContentType,
|
||||
},
|
||||
existingItem,
|
||||
})
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
import { ContentType } from '@standardnotes/domain-core'
|
||||
|
||||
import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
|
||||
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
|
||||
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
|
||||
export class ContentTypeFilter implements ItemSaveRuleInterface {
|
||||
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
|
||||
const validContentType = Object.values(ContentType).includes(dto.itemHash.content_type as ContentType)
|
||||
|
||||
if (!validContentType) {
|
||||
const contentTypeOrError = ContentType.create(dto.itemHash.content_type)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
return {
|
||||
passed: false,
|
||||
conflict: {
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { Item } from '../Item'
|
||||
|
||||
import { OwnershipFilter } from './OwnershipFilter'
|
||||
import { Uuid, ContentType, Dates, Timestamps, UniqueEntityId } from '@standardnotes/domain-core'
|
||||
|
||||
describe('OwnershipFilter', () => {
|
||||
let existingItem: Item
|
||||
const createFilter = () => new OwnershipFilter()
|
||||
|
||||
beforeEach(() => {
|
||||
existingItem = {} as jest.Mocked<Item>
|
||||
existingItem.userUuid = '2-3-4'
|
||||
existingItem = 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()
|
||||
})
|
||||
|
||||
it('should filter out items belonging to a different user', async () => {
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000001',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem,
|
||||
})
|
||||
@@ -32,7 +45,7 @@ describe('OwnershipFilter', () => {
|
||||
conflict: {
|
||||
unsavedItem: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
type: 'uuid_conflict',
|
||||
},
|
||||
@@ -40,14 +53,12 @@ describe('OwnershipFilter', () => {
|
||||
})
|
||||
|
||||
it('should leave items belonging to the same user', async () => {
|
||||
existingItem.userUuid = '1-2-3'
|
||||
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem,
|
||||
})
|
||||
@@ -59,11 +70,11 @@ describe('OwnershipFilter', () => {
|
||||
|
||||
it('should leave non existing items', async () => {
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem: null,
|
||||
})
|
||||
@@ -72,4 +83,27 @@ describe('OwnershipFilter', () => {
|
||||
passed: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return an error if the user uuid is invalid', async () => {
|
||||
const result = await createFilter().check({
|
||||
userUuid: 'invalid',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
existingItem,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
passed: false,
|
||||
conflict: {
|
||||
unsavedItem: {
|
||||
uuid: '2-3-4',
|
||||
content_type: ContentType.TYPES.Note,
|
||||
},
|
||||
type: 'uuid_error',
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,10 +2,23 @@ import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO'
|
||||
import { ItemSaveRuleResult } from './ItemSaveRuleResult'
|
||||
import { ItemSaveRuleInterface } from './ItemSaveRuleInterface'
|
||||
import { ConflictType } from '@standardnotes/responses'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
export class OwnershipFilter implements ItemSaveRuleInterface {
|
||||
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
|
||||
const itemBelongsToADifferentUser = dto.existingItem !== null && dto.existingItem.userUuid !== dto.userUuid
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return {
|
||||
passed: false,
|
||||
conflict: {
|
||||
unsavedItem: dto.itemHash,
|
||||
type: ConflictType.UuidError,
|
||||
},
|
||||
}
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const itemBelongsToADifferentUser = dto.existingItem !== null && !dto.existingItem.props.userUuid.equals(userUuid)
|
||||
if (itemBelongsToADifferentUser) {
|
||||
return {
|
||||
passed: false,
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { Time, Timer, TimerInterface } from '@standardnotes/time'
|
||||
import { ContentType, Dates, Timestamps, UniqueEntityId, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
|
||||
@@ -26,28 +25,36 @@ describe('TimeDifferenceFilter', () => {
|
||||
.fn()
|
||||
.mockImplementation((date: string) => timeHelper.convertStringDateToMicroseconds(date))
|
||||
|
||||
existingItem = {
|
||||
uuid: '1-2-3',
|
||||
userUuid: '1-2-3',
|
||||
createdAt: new Date(1616164633241311),
|
||||
createdAtTimestamp: 1616164633241311,
|
||||
updatedAt: new Date(1616164633241311),
|
||||
updatedAtTimestamp: 1616164633241311,
|
||||
} as jest.Mocked<Item>
|
||||
existingItem = 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()
|
||||
|
||||
itemHash = {
|
||||
uuid: '1-2-3',
|
||||
content: 'asdqwe1',
|
||||
content_type: ContentType.Note,
|
||||
content_type: ContentType.TYPES.Note,
|
||||
duplicate_of: null,
|
||||
enc_item_key: 'qweqwe1',
|
||||
items_key_id: 'asdasd1',
|
||||
created_at: timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.createdAtTimestamp),
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.props.timestamps.createdAt),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
updated_at: timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.updatedAtTimestamp + 1),
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.props.timestamps.updatedAt + 1),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
),
|
||||
} as jest.Mocked<ItemHash>
|
||||
@@ -83,7 +90,7 @@ describe('TimeDifferenceFilter', () => {
|
||||
})
|
||||
|
||||
it('should filter out items having update at timestamp different in microseconds precision', async () => {
|
||||
itemHash.updated_at_timestamp = existingItem.updatedAtTimestamp + 1
|
||||
itemHash.updated_at_timestamp = existingItem.props.timestamps.updatedAt + 1
|
||||
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
@@ -102,7 +109,7 @@ describe('TimeDifferenceFilter', () => {
|
||||
})
|
||||
|
||||
it('should leave items having update at timestamp same in microseconds precision', async () => {
|
||||
itemHash.updated_at_timestamp = existingItem.updatedAtTimestamp
|
||||
itemHash.updated_at_timestamp = existingItem.props.timestamps.updatedAt
|
||||
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
@@ -119,7 +126,9 @@ describe('TimeDifferenceFilter', () => {
|
||||
it('should filter out items having update at timestamp different by a second for legacy clients', async () => {
|
||||
itemHash.updated_at = timeHelper.formatDate(
|
||||
new Date(
|
||||
timeHelper.convertMicrosecondsToMilliseconds(existingItem.updatedAtTimestamp) + Time.MicrosecondsInASecond + 1,
|
||||
timeHelper.convertMicrosecondsToMilliseconds(existingItem.props.timestamps.updatedAt) +
|
||||
Time.MicrosecondsInASecond +
|
||||
1,
|
||||
),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
)
|
||||
@@ -142,7 +151,7 @@ describe('TimeDifferenceFilter', () => {
|
||||
|
||||
it('should leave items having update at timestamp different by less then a second for legacy clients', async () => {
|
||||
itemHash.updated_at = timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.updatedAtTimestamp),
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.props.timestamps.updatedAt),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
)
|
||||
|
||||
@@ -161,7 +170,7 @@ describe('TimeDifferenceFilter', () => {
|
||||
it('should filter out items having update at timestamp different by a millisecond', async () => {
|
||||
itemHash.updated_at = timeHelper.formatDate(
|
||||
new Date(
|
||||
timeHelper.convertMicrosecondsToMilliseconds(existingItem.updatedAtTimestamp) +
|
||||
timeHelper.convertMicrosecondsToMilliseconds(existingItem.props.timestamps.updatedAt) +
|
||||
Time.MicrosecondsInAMillisecond +
|
||||
1,
|
||||
),
|
||||
@@ -186,7 +195,7 @@ describe('TimeDifferenceFilter', () => {
|
||||
|
||||
it('should leave items having update at timestamp different by less than a millisecond', async () => {
|
||||
itemHash.updated_at = timeHelper.formatDate(
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.updatedAtTimestamp),
|
||||
timeHelper.convertMicrosecondsToDate(existingItem.props.timestamps.updatedAt),
|
||||
'YYYY-MM-DDTHH:mm:ss.SSS[Z]',
|
||||
)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export class TimeDifferenceFilter implements ItemSaveRuleInterface {
|
||||
}
|
||||
}
|
||||
|
||||
const ourUpdatedAtTimestamp = dto.existingItem.updatedAtTimestamp
|
||||
const ourUpdatedAtTimestamp = dto.existingItem.props.timestamps.updatedAt
|
||||
const difference = incomingUpdatedAtTimestamp - ourUpdatedAtTimestamp
|
||||
|
||||
if (this.itemHashHasMicrosecondsPrecision(dto.itemHash)) {
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { ApiVersion } from '../../Api/ApiVersion'
|
||||
import { Item } from '../Item'
|
||||
|
||||
import { UuidFilter } from './UuidFilter'
|
||||
|
||||
describe('UuidFilter', () => {
|
||||
const createFilter = () => new UuidFilter()
|
||||
|
||||
it('should filter out items with invalid uuid', async () => {
|
||||
const invalidUuids = [
|
||||
'c73bcdcc-2669-4bf6-81d3-e4an73fb11fd',
|
||||
'c73bcdcc26694bf681d3e4ae73fb11fd',
|
||||
'definitely-not-a-uuid',
|
||||
'1-2-3',
|
||||
'test',
|
||||
"(select load_file('\\\\\\\\iugt7mazsk477",
|
||||
'/etc/passwd',
|
||||
"eval(compile('for x in range(1):\\n i",
|
||||
]
|
||||
|
||||
for (const invalidUuid of invalidUuids) {
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: invalidUuid,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
existingItem: null,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
passed: false,
|
||||
conflict: {
|
||||
unsavedItem: {
|
||||
uuid: invalidUuid,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
type: 'uuid_error',
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('should leave items with valid uuid', async () => {
|
||||
const validUuids = [
|
||||
'123e4567-e89b-12d3-a456-426655440000',
|
||||
'c73bcdcc-2669-4bf6-81d3-e4ae73fb11fd',
|
||||
'C73BCDCC-2669-4Bf6-81d3-E4AE73FB11FD',
|
||||
]
|
||||
|
||||
for (const validUuid of validUuids) {
|
||||
const result = await createFilter().check({
|
||||
userUuid: '1-2-3',
|
||||
apiVersion: ApiVersion.v20200115,
|
||||
itemHash: {
|
||||
uuid: validUuid,
|
||||
content_type: ContentType.Note,
|
||||
},
|
||||
existingItem: {} as jest.Mocked<Item>,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
passed: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user