feat: remove transition state (#882)
* fix: Dockerfile build deps * feat: remove transition state
This commit is contained in:
parent
3ef8e9ea24
commit
a2c1ebe675
145 changed files with 152 additions and 4228 deletions
240
.pnp.cjs
generated
240
.pnp.cjs
generated
|
@ -3065,16 +3065,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@mongodb-js/saslprep", [\
|
||||
["npm:1.1.0", {\
|
||||
"packageLocation": "./.yarn/cache/@mongodb-js-saslprep-npm-1.1.0-3906c025b8-1a631b92d2.zip/node_modules/@mongodb-js/saslprep/",\
|
||||
"packageDependencies": [\
|
||||
["@mongodb-js/saslprep", "npm:1.1.0"],\
|
||||
["sparse-bitfield", "npm:3.0.3"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@nodelib/fs.scandir", [\
|
||||
["npm:2.1.5", {\
|
||||
"packageLocation": "./.yarn/cache/@nodelib-fs.scandir-npm-2.1.5-89c67370dd-6ab2a9b8a1.zip/node_modules/@nodelib/fs.scandir/",\
|
||||
|
@ -5897,13 +5887,12 @@ const RAW_RUNTIME_STATE =
|
|||
["inversify-express-utils", "npm:6.4.3"],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.17"],\
|
||||
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.17"],\
|
||||
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
||||
["winston", "npm:3.9.0"]\
|
||||
],\
|
||||
|
@ -6083,7 +6072,6 @@ const RAW_RUNTIME_STATE =
|
|||
["ioredis", "npm:5.3.2"],\
|
||||
["jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.5.0"],\
|
||||
["jsonwebtoken", "npm:9.0.0"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["prettier", "npm:3.0.3"],\
|
||||
["prettyjson", "npm:1.2.5"],\
|
||||
|
@ -6091,7 +6079,7 @@ const RAW_RUNTIME_STATE =
|
|||
["semver", "npm:7.5.4"],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-jest", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:29.1.0"],\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.17"],\
|
||||
["typeorm", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:0.3.17"],\
|
||||
["typescript", "patch:typescript@npm%3A5.0.4#optional!builtin<compat/typescript>::version=5.0.4&hash=b5f058"],\
|
||||
["ua-parser-js", "npm:1.0.35"],\
|
||||
["uuid", "npm:9.0.0"],\
|
||||
|
@ -6729,26 +6717,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/webidl-conversions", [\
|
||||
["npm:7.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/@types-webidl-conversions-npm-7.0.0-0903313151-60142c7ddd.zip/node_modules/@types/webidl-conversions/",\
|
||||
"packageDependencies": [\
|
||||
["@types/webidl-conversions", "npm:7.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/whatwg-url", [\
|
||||
["npm:8.2.2", {\
|
||||
"packageLocation": "./.yarn/cache/@types-whatwg-url-npm-8.2.2-54c5c24e6c-5dc5afe078.zip/node_modules/@types/whatwg-url/",\
|
||||
"packageDependencies": [\
|
||||
["@types/whatwg-url", "npm:8.2.2"],\
|
||||
["@types/node", "npm:20.2.5"],\
|
||||
["@types/webidl-conversions", "npm:7.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@types/yargs", [\
|
||||
["npm:17.0.24", {\
|
||||
"packageLocation": "./.yarn/cache/@types-yargs-npm-17.0.24-b034cf1d8b-03d9a985cb.zip/node_modules/@types/yargs/",\
|
||||
|
@ -7949,15 +7917,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["bson", [\
|
||||
["npm:6.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/bson-npm-6.0.0-7b3cba060e-e7614bdc53.zip/node_modules/bson/",\
|
||||
"packageDependencies": [\
|
||||
["bson", "npm:6.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["buffer", [\
|
||||
["npm:5.7.1", {\
|
||||
"packageLocation": "./.yarn/cache/buffer-npm-5.7.1-513ef8259e-997434d3c6.zip/node_modules/buffer/",\
|
||||
|
@ -12561,15 +12520,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["memory-pager", [\
|
||||
["npm:1.5.0", {\
|
||||
"packageLocation": "./.yarn/cache/memory-pager-npm-1.5.0-46e20e6c81-ffe3461b6a.zip/node_modules/memory-pager/",\
|
||||
"packageDependencies": [\
|
||||
["memory-pager", "npm:1.5.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["meow", [\
|
||||
["npm:8.1.2", {\
|
||||
"packageLocation": "./.yarn/cache/meow-npm-8.1.2-bcfe48d4f3-d4770f9013.zip/node_modules/meow/",\
|
||||
|
@ -12914,66 +12864,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["mongodb", [\
|
||||
["npm:6.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/mongodb-npm-6.0.0-7c1e74de91-501feaecb7.zip/node_modules/mongodb/",\
|
||||
"packageDependencies": [\
|
||||
["mongodb", "npm:6.0.0"]\
|
||||
],\
|
||||
"linkType": "SOFT"\
|
||||
}],\
|
||||
["virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0", {\
|
||||
"packageLocation": "./.yarn/__virtual__/mongodb-virtual-789f2eaaac/0/cache/mongodb-npm-6.0.0-7c1e74de91-501feaecb7.zip/node_modules/mongodb/",\
|
||||
"packageDependencies": [\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["@aws-sdk/credential-providers", null],\
|
||||
["@mongodb-js/saslprep", "npm:1.1.0"],\
|
||||
["@mongodb-js/zstd", null],\
|
||||
["@types/aws-sdk__credential-providers", null],\
|
||||
["@types/gcp-metadata", null],\
|
||||
["@types/kerberos", null],\
|
||||
["@types/mongodb-client-encryption", null],\
|
||||
["@types/mongodb-js__zstd", null],\
|
||||
["@types/snappy", null],\
|
||||
["@types/socks", null],\
|
||||
["bson", "npm:6.0.0"],\
|
||||
["gcp-metadata", null],\
|
||||
["kerberos", null],\
|
||||
["mongodb-client-encryption", null],\
|
||||
["mongodb-connection-string-url", "npm:2.6.0"],\
|
||||
["snappy", null],\
|
||||
["socks", null]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@aws-sdk/credential-providers",\
|
||||
"@mongodb-js/zstd",\
|
||||
"@types/aws-sdk__credential-providers",\
|
||||
"@types/gcp-metadata",\
|
||||
"@types/kerberos",\
|
||||
"@types/mongodb-client-encryption",\
|
||||
"@types/mongodb-js__zstd",\
|
||||
"@types/snappy",\
|
||||
"@types/socks",\
|
||||
"gcp-metadata",\
|
||||
"kerberos",\
|
||||
"mongodb-client-encryption",\
|
||||
"snappy",\
|
||||
"socks"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["mongodb-connection-string-url", [\
|
||||
["npm:2.6.0", {\
|
||||
"packageLocation": "./.yarn/cache/mongodb-connection-string-url-npm-2.6.0-af011ba17f-d0903b9824.zip/node_modules/mongodb-connection-string-url/",\
|
||||
"packageDependencies": [\
|
||||
["mongodb-connection-string-url", "npm:2.6.0"],\
|
||||
["@types/whatwg-url", "npm:8.2.2"],\
|
||||
["whatwg-url", "npm:11.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["ms", [\
|
||||
["npm:2.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/ms-npm-2.0.0-9e1101a471-0e6a22b8b7.zip/node_modules/ms/",\
|
||||
|
@ -15055,16 +14945,6 @@ const RAW_RUNTIME_STATE =
|
|||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["sparse-bitfield", [\
|
||||
["npm:3.0.3", {\
|
||||
"packageLocation": "./.yarn/cache/sparse-bitfield-npm-3.0.3-cb80d0c89f-174da88dbb.zip/node_modules/sparse-bitfield/",\
|
||||
"packageDependencies": [\
|
||||
["sparse-bitfield", "npm:3.0.3"],\
|
||||
["memory-pager", "npm:1.5.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["spdx-correct", [\
|
||||
["npm:3.2.0", {\
|
||||
"packageLocation": "./.yarn/cache/spdx-correct-npm-3.2.0-ffae008484-cc2e4dbef8.zip/node_modules/spdx-correct/",\
|
||||
|
@ -15714,14 +15594,6 @@ const RAW_RUNTIME_STATE =
|
|||
["tr46", "npm:0.0.3"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:3.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-b09a15886c.zip/node_modules/tr46/",\
|
||||
"packageDependencies": [\
|
||||
["tr46", "npm:3.0.0"],\
|
||||
["punycode", "npm:2.3.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["treeverse", [\
|
||||
|
@ -16175,98 +16047,6 @@ const RAW_RUNTIME_STATE =
|
|||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.17", {\
|
||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-bfb7ebf128/0/cache/typeorm-npm-0.3.17-f8c2578e7f-3a7fe2a5e9.zip/node_modules/typeorm/",\
|
||||
"packageDependencies": [\
|
||||
["typeorm", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:0.3.17"],\
|
||||
["@google-cloud/spanner", null],\
|
||||
["@sap/hana-client", null],\
|
||||
["@sqltools/formatter", "npm:1.2.5"],\
|
||||
["@types/better-sqlite3", null],\
|
||||
["@types/google-cloud__spanner", null],\
|
||||
["@types/hdb-pool", null],\
|
||||
["@types/ioredis", "npm:5.0.0"],\
|
||||
["@types/mongodb", null],\
|
||||
["@types/mssql", null],\
|
||||
["@types/mysql2", null],\
|
||||
["@types/oracledb", null],\
|
||||
["@types/pg", null],\
|
||||
["@types/pg-native", null],\
|
||||
["@types/pg-query-stream", null],\
|
||||
["@types/redis", null],\
|
||||
["@types/sap__hana-client", null],\
|
||||
["@types/sql.js", null],\
|
||||
["@types/sqlite3", null],\
|
||||
["@types/ts-node", null],\
|
||||
["@types/typeorm-aurora-data-api-driver", null],\
|
||||
["app-root-path", "npm:3.1.0"],\
|
||||
["better-sqlite3", null],\
|
||||
["buffer", "npm:6.0.3"],\
|
||||
["chalk", "npm:4.1.2"],\
|
||||
["cli-highlight", "npm:2.1.11"],\
|
||||
["date-fns", "npm:2.30.0"],\
|
||||
["debug", "virtual:ac3d8e680759ce54399273724d44e041d6c9b73454d191d411a8c44bb27e22f02aaf6ed9d3ad0ac1c298eac4833cff369c9c7b84c573016112c4f84be2cd8543#npm:4.3.4"],\
|
||||
["dotenv", "npm:16.1.3"],\
|
||||
["glob", "npm:8.1.0"],\
|
||||
["hdb-pool", null],\
|
||||
["ioredis", "npm:5.3.2"],\
|
||||
["mkdirp", "npm:2.1.6"],\
|
||||
["mongodb", "virtual:365b8c88cdf194291829ee28b79556e2328175d26a621363e703848100bea0042e9500db2a1206c9bbc3a4a76a1d169639ef774b2ea3a1a98584a9936b58c6be#npm:6.0.0"],\
|
||||
["mssql", null],\
|
||||
["mysql2", "npm:3.3.3"],\
|
||||
["oracledb", null],\
|
||||
["pg", null],\
|
||||
["pg-native", null],\
|
||||
["pg-query-stream", null],\
|
||||
["redis", null],\
|
||||
["reflect-metadata", "npm:0.1.13"],\
|
||||
["sha.js", "npm:2.4.11"],\
|
||||
["sql.js", null],\
|
||||
["sqlite3", "virtual:31b5a94a105c89c9294c3d524a7f8929fe63ee5a2efadf21951ca4c0cfd2ecf02e8f4ef5a066bbda091f1e3a56e57c6749069a080618c96b22e51131a330fc4a#npm:5.1.6"],\
|
||||
["ts-node", null],\
|
||||
["tslib", "npm:2.5.2"],\
|
||||
["typeorm-aurora-data-api-driver", null],\
|
||||
["uuid", "npm:9.0.0"],\
|
||||
["yargs", "npm:17.7.2"]\
|
||||
],\
|
||||
"packagePeers": [\
|
||||
"@google-cloud/spanner",\
|
||||
"@sap/hana-client",\
|
||||
"@types/better-sqlite3",\
|
||||
"@types/google-cloud__spanner",\
|
||||
"@types/hdb-pool",\
|
||||
"@types/ioredis",\
|
||||
"@types/mongodb",\
|
||||
"@types/mssql",\
|
||||
"@types/mysql2",\
|
||||
"@types/oracledb",\
|
||||
"@types/pg-native",\
|
||||
"@types/pg-query-stream",\
|
||||
"@types/pg",\
|
||||
"@types/redis",\
|
||||
"@types/sap__hana-client",\
|
||||
"@types/sql.js",\
|
||||
"@types/sqlite3",\
|
||||
"@types/ts-node",\
|
||||
"@types/typeorm-aurora-data-api-driver",\
|
||||
"better-sqlite3",\
|
||||
"hdb-pool",\
|
||||
"ioredis",\
|
||||
"mongodb",\
|
||||
"mssql",\
|
||||
"mysql2",\
|
||||
"oracledb",\
|
||||
"pg-native",\
|
||||
"pg-query-stream",\
|
||||
"pg",\
|
||||
"redis",\
|
||||
"sql.js",\
|
||||
"sqlite3",\
|
||||
"ts-node",\
|
||||
"typeorm-aurora-data-api-driver"\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["virtual:c66bf20e88479ada0172094776519a9f51acc4731d22079b60a295bcec7ea42d5545cbce58a77a50d932bf953298799135e99707486e343da6d99ba1d167bdbd#npm:0.3.17", {\
|
||||
"packageLocation": "./.yarn/__virtual__/typeorm-virtual-bfa664706d/0/cache/typeorm-npm-0.3.17-f8c2578e7f-3a7fe2a5e9.zip/node_modules/typeorm/",\
|
||||
"packageDependencies": [\
|
||||
|
@ -16659,13 +16439,6 @@ const RAW_RUNTIME_STATE =
|
|||
["webidl-conversions", "npm:3.0.1"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:7.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/webidl-conversions-npm-7.0.0-e8c8e30c68-4c4f65472c.zip/node_modules/webidl-conversions/",\
|
||||
"packageDependencies": [\
|
||||
["webidl-conversions", "npm:7.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["webpack", [\
|
||||
|
@ -16724,15 +16497,6 @@ const RAW_RUNTIME_STATE =
|
|||
}]\
|
||||
]],\
|
||||
["whatwg-url", [\
|
||||
["npm:11.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/whatwg-url-npm-11.0.0-073529d93a-dfcd51c6f4.zip/node_modules/whatwg-url/",\
|
||||
"packageDependencies": [\
|
||||
["whatwg-url", "npm:11.0.0"],\
|
||||
["tr46", "npm:3.0.0"],\
|
||||
["webidl-conversions", "npm:7.0.0"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:5.0.0", {\
|
||||
"packageLocation": "./.yarn/cache/whatwg-url-npm-5.0.0-374fb45e60-f95adbc1e8.zip/node_modules/whatwg-url/",\
|
||||
"packageDependencies": [\
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/bson-npm-6.0.0-7b3cba060e-e7614bdc53.zip
vendored
BIN
.yarn/cache/bson-npm-6.0.0-7b3cba060e-e7614bdc53.zip
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-b09a15886c.zip
vendored
BIN
.yarn/cache/tr46-npm-3.0.0-e1ae1ea7c9-b09a15886c.zip
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,6 +3,8 @@ FROM node:20.6.1-alpine
|
|||
ENV NODE_ENV production
|
||||
|
||||
RUN apk add --update --no-cache \
|
||||
g++ \
|
||||
make \
|
||||
openssl \
|
||||
curl \
|
||||
bash \
|
||||
|
|
|
@ -57,9 +57,6 @@ fi
|
|||
if [ -z "$CACHE_TYPE" ]; then
|
||||
export CACHE_TYPE="redis"
|
||||
fi
|
||||
if [ -z "$SECONDARY_DB_ENABLED" ]; then
|
||||
export SECONDARY_DB_ENABLED=false
|
||||
fi
|
||||
export DB_MIGRATIONS_PATH="dist/migrations/*.js"
|
||||
|
||||
#########
|
||||
|
|
|
@ -39,7 +39,7 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
|||
crossServiceToken = await this.crossServiceTokenCache.get(cacheKey)
|
||||
}
|
||||
|
||||
if (this.crossServiceTokenIsEmptyOrRequiresRevalidation(crossServiceToken)) {
|
||||
if (crossServiceToken === null) {
|
||||
const authResponse = await this.serviceProxy.validateSession({
|
||||
authorization: authHeaderValue,
|
||||
sharedVaultOwnerContext: sharedVaultOwnerContextHeaderValue,
|
||||
|
@ -129,14 +129,4 @@ export abstract class AuthMiddleware extends BaseMiddleware {
|
|||
|
||||
return Math.min(crossServiceTokenDefaultCacheExpiration, sessionAccessExpiration, sessionRefreshExpiration)
|
||||
}
|
||||
|
||||
private crossServiceTokenIsEmptyOrRequiresRevalidation(crossServiceToken: string | null) {
|
||||
if (crossServiceToken === null) {
|
||||
return true
|
||||
}
|
||||
|
||||
const decodedToken = <CrossServiceTokenData>verify(crossServiceToken, this.jwtSecret, { algorithms: ['HS256'] })
|
||||
|
||||
return decodedToken.ongoing_transition === true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,6 @@ export class ItemsController extends BaseHttpController {
|
|||
)
|
||||
}
|
||||
|
||||
@httpPost('/transition')
|
||||
async transition(request: Request, response: Response): Promise<void> {
|
||||
await this.serviceProxy.callSyncingServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'items/transition'),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/:uuid')
|
||||
async getItem(request: Request, response: Response): Promise<void> {
|
||||
await this.serviceProxy.callSyncingServer(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, httpDelete, httpGet, httpPost } from 'inversify-express-utils'
|
||||
import { BaseHttpController, controller, httpDelete, httpGet } from 'inversify-express-utils'
|
||||
|
||||
import { TYPES } from '../../Bootstrap/Types'
|
||||
import { ServiceProxyInterface } from '../../Service/Http/ServiceProxyInterface'
|
||||
|
@ -55,14 +55,4 @@ export class RevisionsControllerV2 extends BaseHttpController {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
@httpPost('/revisions/transition')
|
||||
async transition(request: Request, response: Response): Promise<void> {
|
||||
await this.serviceProxy.callRevisionsServer(
|
||||
request,
|
||||
response,
|
||||
this.endpointResolver.resolveEndpointOrMethodIdentifier('POST', 'revisions/transition'),
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import { OpenTelemetrySDK, OpenTelemetryTracer } from '@standardnotes/domain-events-infra'
|
||||
import { ServiceIdentifier, RoleName, TransitionStatus } from '@standardnotes/domain-core'
|
||||
|
||||
const sdk = new OpenTelemetrySDK({ serviceName: ServiceIdentifier.NAMES.AuthScheduledTask })
|
||||
sdk.start()
|
||||
|
||||
import { Logger } from 'winston'
|
||||
import * as dayjs from 'dayjs'
|
||||
import * as utc from 'dayjs/plugin/utc'
|
||||
|
||||
import { ContainerConfigLoader } from '../src/Bootstrap/Container'
|
||||
import TYPES from '../src/Bootstrap/Types'
|
||||
import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { TransitionStatusRepositoryInterface } from '../src/Domain/Transition/TransitionStatusRepositoryInterface'
|
||||
|
||||
const inputArgs = process.argv.slice(2)
|
||||
const startDateString = inputArgs[0]
|
||||
const endDateString = inputArgs[1]
|
||||
const forceRunParam = inputArgs[2]
|
||||
|
||||
const requestTransition = async (
|
||||
transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
userRepository: UserRepositoryInterface,
|
||||
logger: Logger,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
timer: TimerInterface,
|
||||
): Promise<void> => {
|
||||
const startDate = new Date(startDateString)
|
||||
const endDate = new Date(endDateString)
|
||||
|
||||
const usersCount = await userRepository.countAllCreatedBetween(startDate, endDate)
|
||||
|
||||
const timestamp = timer.getTimestampInMicroseconds()
|
||||
|
||||
logger.info(
|
||||
`[TRANSITION ${timestamp}] Found ${usersCount} users created between ${startDateString} and ${endDateString}`,
|
||||
)
|
||||
|
||||
let itemTransitionsTriggered = 0
|
||||
let revisionTransitionsTriggered = 0
|
||||
const forceRun = forceRunParam === 'true'
|
||||
|
||||
const pageLimit = 100
|
||||
const totalPages = Math.ceil(usersCount / pageLimit)
|
||||
for (let currentPage = 1; currentPage <= totalPages; currentPage++) {
|
||||
const users = await userRepository.findAllCreatedBetween({
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
offset: (currentPage - 1) * pageLimit,
|
||||
limit: pageLimit,
|
||||
})
|
||||
|
||||
for (const user of users) {
|
||||
const itemsTransitionStatus = await transitionStatusRepository.getStatus(user.uuid, 'items')
|
||||
const revisionsTransitionStatus = await transitionStatusRepository.getStatus(user.uuid, 'revisions')
|
||||
|
||||
const userRoles = await user.roles
|
||||
|
||||
const userHasTransitionRole = userRoles.some((role) => role.name === RoleName.NAMES.TransitionUser)
|
||||
const bothTransitionStatusesAreVerified =
|
||||
itemsTransitionStatus?.value === TransitionStatus.STATUSES.Verified &&
|
||||
revisionsTransitionStatus?.value === TransitionStatus.STATUSES.Verified
|
||||
|
||||
if (!userHasTransitionRole && bothTransitionStatusesAreVerified) {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[TRANSITION ${timestamp}] Transition status for user ${user.uuid} - items status: ${itemsTransitionStatus?.value}, revisions status: ${revisionsTransitionStatus?.value}, has transition role: ${userHasTransitionRole}`,
|
||||
)
|
||||
|
||||
if (
|
||||
itemsTransitionStatus === null ||
|
||||
itemsTransitionStatus.value === TransitionStatus.STATUSES.Failed ||
|
||||
(itemsTransitionStatus.value === TransitionStatus.STATUSES.InProgress && forceRun)
|
||||
) {
|
||||
await transitionStatusRepository.remove(user.uuid, 'items')
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createTransitionRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
type: 'items',
|
||||
timestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
itemTransitionsTriggered++
|
||||
}
|
||||
|
||||
if (
|
||||
revisionsTransitionStatus === null ||
|
||||
revisionsTransitionStatus.value === TransitionStatus.STATUSES.Failed ||
|
||||
(revisionsTransitionStatus.value === TransitionStatus.STATUSES.InProgress && forceRun)
|
||||
) {
|
||||
await transitionStatusRepository.remove(user.uuid, 'revisions')
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createTransitionRequestedEvent({
|
||||
userUuid: user.uuid,
|
||||
type: 'revisions',
|
||||
timestamp,
|
||||
}),
|
||||
)
|
||||
|
||||
revisionTransitionsTriggered++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[TRANSITION ${timestamp}] Triggered ${itemTransitionsTriggered} item transitions and ${revisionTransitionsTriggered} revision transitions for users created between ${startDateString} and ${endDateString}`,
|
||||
)
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader('worker')
|
||||
void container.load().then((container) => {
|
||||
dayjs.extend(utc)
|
||||
|
||||
const env: Env = new Env()
|
||||
env.load()
|
||||
|
||||
const logger: Logger = container.get(TYPES.Auth_Logger)
|
||||
|
||||
logger.info(`Starting transition request for users created between ${startDateString} and ${endDateString}`)
|
||||
|
||||
const userRepository: UserRepositoryInterface = container.get(TYPES.Auth_UserRepository)
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory)
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher)
|
||||
const timer = container.get<TimerInterface>(TYPES.Auth_Timer)
|
||||
const transitionStatusRepository = container.get<TransitionStatusRepositoryInterface>(
|
||||
TYPES.Auth_TransitionStatusRepository,
|
||||
)
|
||||
|
||||
const tracer = new OpenTelemetryTracer()
|
||||
tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'transition')
|
||||
|
||||
Promise.resolve(
|
||||
requestTransition(
|
||||
transitionStatusRepository,
|
||||
userRepository,
|
||||
logger,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
timer,
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
logger.info(`Finished transition request for users created between ${startDateString} and ${endDateString}`)
|
||||
|
||||
tracer.stopSpan()
|
||||
|
||||
process.exit(0)
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(
|
||||
`Error while requesting transition for users created between ${startDateString} and ${endDateString}: ${error}`,
|
||||
)
|
||||
|
||||
tracer.stopSpanWithError(error)
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
})
|
|
@ -1,11 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
|
||||
const pnp = require(path.normalize(path.resolve(__dirname, '../../..', '.pnp.cjs'))).setup()
|
||||
|
||||
const index = require(path.normalize(path.resolve(__dirname, '../dist/bin/transition.js')))
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true })
|
||||
|
||||
exports.default = index
|
|
@ -55,13 +55,6 @@ case "$COMMAND" in
|
|||
node docker/entrypoint-backup.js one_drive daily
|
||||
;;
|
||||
|
||||
'transition' )
|
||||
START_DATE=$1 && shift 1
|
||||
END_DATE=$1 && shift 1
|
||||
echo "[Docker] Starting Transition..."
|
||||
node docker/entrypoint-transition.js $START_DATE $END_DATE
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "[Docker] Unknown command"
|
||||
;;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class RemoveTransitionRole1697704066569 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('DELETE FROM `roles` WHERE name = "TRANSITION_USER"')
|
||||
}
|
||||
|
||||
public async down(): Promise<void> {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -258,11 +258,6 @@ import { UpdateStorageQuotaUsedForUser } from '../Domain/UseCase/UpdateStorageQu
|
|||
import { SharedVaultFileUploadedEventHandler } from '../Domain/Handler/SharedVaultFileUploadedEventHandler'
|
||||
import { SharedVaultFileRemovedEventHandler } from '../Domain/Handler/SharedVaultFileRemovedEventHandler'
|
||||
import { SharedVaultFileMovedEventHandler } from '../Domain/Handler/SharedVaultFileMovedEventHandler'
|
||||
import { TransitionStatusRepositoryInterface } from '../Domain/Transition/TransitionStatusRepositoryInterface'
|
||||
import { RedisTransitionStatusRepository } from '../Infra/Redis/RedisTransitionStatusRepository'
|
||||
import { InMemoryTransitionStatusRepository } from '../Infra/InMemory/InMemoryTransitionStatusRepository'
|
||||
import { TransitionStatusUpdatedEventHandler } from '../Domain/Handler/TransitionStatusUpdatedEventHandler'
|
||||
import { UpdateTransitionStatus } from '../Domain/UseCase/UpdateTransitionStatus/UpdateTransitionStatus'
|
||||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { SharedVaultUserPersistenceMapper } from '../Mapping/SharedVaultUserPersistenceMapper'
|
||||
import { SharedVaultUserRepositoryInterface } from '../Domain/SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
@ -645,9 +640,6 @@ export class ContainerConfigLoader {
|
|||
container.get(TYPES.Auth_Timer),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionStatusRepositoryInterface>(TYPES.Auth_TransitionStatusRepository)
|
||||
.toConstantValue(new InMemoryTransitionStatusRepository())
|
||||
} else {
|
||||
container.bind<PKCERepositoryInterface>(TYPES.Auth_PKCERepository).to(RedisPKCERepository)
|
||||
container.bind<LockRepositoryInterface>(TYPES.Auth_LockRepository).to(LockRepository)
|
||||
|
@ -660,9 +652,6 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<SubscriptionTokenRepositoryInterface>(TYPES.Auth_SubscriptionTokenRepository)
|
||||
.to(RedisSubscriptionTokenRepository)
|
||||
container
|
||||
.bind<TransitionStatusRepositoryInterface>(TYPES.Auth_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionStatusRepository(container.get<Redis>(TYPES.Auth_Redis)))
|
||||
}
|
||||
|
||||
// Services
|
||||
|
@ -985,15 +974,6 @@ export class ContainerConfigLoader {
|
|||
container.get(TYPES.Auth_SubscriptionSettingService),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UpdateTransitionStatus>(TYPES.Auth_UpdateTransitionStatus)
|
||||
.toConstantValue(
|
||||
new UpdateTransitionStatus(
|
||||
container.get<TransitionStatusRepositoryInterface>(TYPES.Auth_TransitionStatusRepository),
|
||||
container.get<RoleServiceInterface>(TYPES.Auth_RoleService),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<AddSharedVaultUser>(TYPES.Auth_AddSharedVaultUser)
|
||||
.toConstantValue(
|
||||
|
@ -1174,14 +1154,6 @@ export class ContainerConfigLoader {
|
|||
container.get(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionStatusUpdatedEventHandler>(TYPES.Auth_TransitionStatusUpdatedEventHandler)
|
||||
.toConstantValue(
|
||||
new TransitionStatusUpdatedEventHandler(
|
||||
container.get<UpdateTransitionStatus>(TYPES.Auth_UpdateTransitionStatus),
|
||||
container.get<winston.Logger>(TYPES.Auth_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<UserAddedToSharedVaultEventHandler>(TYPES.Auth_UserAddedToSharedVaultEventHandler)
|
||||
.toConstantValue(
|
||||
|
@ -1239,7 +1211,6 @@ export class ContainerConfigLoader {
|
|||
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.Auth_PredicateVerificationRequestedEventHandler)],
|
||||
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.Auth_EmailSubscriptionUnsubscribedEventHandler)],
|
||||
['PAYMENTS_ACCOUNT_DELETED', container.get(TYPES.Auth_PaymentsAccountDeletedEventHandler)],
|
||||
['TRANSITION_STATUS_UPDATED', container.get(TYPES.Auth_TransitionStatusUpdatedEventHandler)],
|
||||
['USER_ADDED_TO_SHARED_VAULT', container.get(TYPES.Auth_UserAddedToSharedVaultEventHandler)],
|
||||
['USER_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Auth_UserRemovedFromSharedVaultEventHandler)],
|
||||
[
|
||||
|
|
|
@ -36,7 +36,6 @@ const TYPES = {
|
|||
Auth_AuthenticatorRepository: Symbol.for('Auth_AuthenticatorRepository'),
|
||||
Auth_AuthenticatorChallengeRepository: Symbol.for('Auth_AuthenticatorChallengeRepository'),
|
||||
Auth_CacheEntryRepository: Symbol.for('Auth_CacheEntryRepository'),
|
||||
Auth_TransitionStatusRepository: Symbol.for('Auth_TransitionStatusRepository'),
|
||||
Auth_SharedVaultUserRepository: Symbol.for('Auth_SharedVaultUserRepository'),
|
||||
// ORM
|
||||
Auth_ORMOfflineSettingRepository: Symbol.for('Auth_ORMOfflineSettingRepository'),
|
||||
|
@ -157,7 +156,6 @@ const TYPES = {
|
|||
Auth_SignInWithRecoveryCodes: Symbol.for('Auth_SignInWithRecoveryCodes'),
|
||||
Auth_GetUserKeyParamsRecovery: Symbol.for('Auth_GetUserKeyParamsRecovery'),
|
||||
Auth_UpdateStorageQuotaUsedForUser: Symbol.for('Auth_UpdateStorageQuotaUsedForUser'),
|
||||
Auth_UpdateTransitionStatus: Symbol.for('Auth_UpdateTransitionStatus'),
|
||||
Auth_AddSharedVaultUser: Symbol.for('Auth_AddSharedVaultUser'),
|
||||
Auth_RemoveSharedVaultUser: Symbol.for('Auth_RemoveSharedVaultUser'),
|
||||
Auth_DesignateSurvivor: Symbol.for('Auth_DesignateSurvivor'),
|
||||
|
@ -190,7 +188,6 @@ const TYPES = {
|
|||
Auth_PredicateVerificationRequestedEventHandler: Symbol.for('Auth_PredicateVerificationRequestedEventHandler'),
|
||||
Auth_EmailSubscriptionUnsubscribedEventHandler: Symbol.for('Auth_EmailSubscriptionUnsubscribedEventHandler'),
|
||||
Auth_PaymentsAccountDeletedEventHandler: Symbol.for('Auth_PaymentsAccountDeletedEventHandler'),
|
||||
Auth_TransitionStatusUpdatedEventHandler: Symbol.for('Auth_TransitionStatusUpdatedEventHandler'),
|
||||
Auth_UserAddedToSharedVaultEventHandler: Symbol.for('Auth_UserAddedToSharedVaultEventHandler'),
|
||||
Auth_UserRemovedFromSharedVaultEventHandler: Symbol.for('Auth_UserRemovedFromSharedVaultEventHandler'),
|
||||
Auth_UserDesignatedAsSurvivorInSharedVaultEventHandler: Symbol.for(
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
TransitionRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { Predicate, PredicateVerificationResult } from '@standardnotes/predicates'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
@ -34,25 +33,6 @@ import { KeyParamsData } from '@standardnotes/responses'
|
|||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(@inject(TYPES.Auth_Timer) private timer: TimerInterface) {}
|
||||
|
||||
createTransitionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
type: 'items' | 'revisions'
|
||||
timestamp: number
|
||||
}): TransitionRequestedEvent {
|
||||
return {
|
||||
type: 'TRANSITION_REQUESTED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Auth,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent {
|
||||
return {
|
||||
type: 'SESSION_CREATED',
|
||||
|
|
|
@ -18,7 +18,6 @@ import {
|
|||
StatisticPersistenceRequestedEvent,
|
||||
SessionCreatedEvent,
|
||||
SessionRefreshedEvent,
|
||||
TransitionRequestedEvent,
|
||||
} from '@standardnotes/domain-events'
|
||||
import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType'
|
||||
import { KeyParamsData } from '@standardnotes/responses'
|
||||
|
@ -92,9 +91,4 @@ export interface DomainEventFactoryInterface {
|
|||
}): StatisticPersistenceRequestedEvent
|
||||
createSessionCreatedEvent(dto: { userUuid: string }): SessionCreatedEvent
|
||||
createSessionRefreshedEvent(dto: { userUuid: string }): SessionRefreshedEvent
|
||||
createTransitionRequestedEvent(dto: {
|
||||
userUuid: string
|
||||
type: 'items' | 'revisions'
|
||||
timestamp: number
|
||||
}): TransitionRequestedEvent
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { DomainEventHandlerInterface, TransitionStatusUpdatedEvent } from '@standardnotes/domain-events'
|
||||
import { UpdateTransitionStatus } from '../UseCase/UpdateTransitionStatus/UpdateTransitionStatus'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
export class TransitionStatusUpdatedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private updateTransitionStatusUseCase: UpdateTransitionStatus,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: TransitionStatusUpdatedEvent): Promise<void> {
|
||||
const result = await this.updateTransitionStatusUseCase.execute({
|
||||
status: event.payload.status,
|
||||
userUuid: event.payload.userUuid,
|
||||
transitionType: event.payload.transitionType,
|
||||
transitionTimestamp: event.payload.transitionTimestamp,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`Failed to update transition status for user ${event.payload.userUuid}`)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
|
||||
export interface TransitionStatusRepositoryInterface {
|
||||
updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: TransitionStatus): Promise<void>
|
||||
getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<TransitionStatus | null>
|
||||
remove(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void>
|
||||
}
|
|
@ -9,15 +9,7 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
|||
|
||||
import { CreateCrossServiceToken } from './CreateCrossServiceToken'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import {
|
||||
Result,
|
||||
SharedVaultUser,
|
||||
SharedVaultUserPermission,
|
||||
Timestamps,
|
||||
TransitionStatus,
|
||||
Uuid,
|
||||
} from '@standardnotes/domain-core'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { Result, SharedVaultUser, SharedVaultUserPermission, Timestamps, Uuid } from '@standardnotes/domain-core'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
describe('CreateCrossServiceToken', () => {
|
||||
|
@ -27,7 +19,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
let tokenEncoder: TokenEncoderInterface<CrossServiceTokenData>
|
||||
let userRepository: UserRepositoryInterface
|
||||
let getSettingUseCase: GetSetting
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
let sharedVaultUserRepository: SharedVaultUserRepositoryInterface
|
||||
const jwtTTL = 60
|
||||
|
||||
|
@ -44,7 +35,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
userRepository,
|
||||
jwtTTL,
|
||||
getSettingUseCase,
|
||||
transitionStatusRepository,
|
||||
sharedVaultUserRepository,
|
||||
)
|
||||
|
||||
|
@ -78,11 +68,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
getSettingUseCase = {} as jest.Mocked<GetSetting>
|
||||
getSettingUseCase.execute = jest.fn().mockReturnValue(Result.ok({ setting: { value: '100' } }))
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockReturnValue(TransitionStatus.create(TransitionStatus.STATUSES.Verified).getValue())
|
||||
|
||||
sharedVaultUserRepository = {} as jest.Mocked<SharedVaultUserRepositoryInterface>
|
||||
sharedVaultUserRepository.findByUserUuid = jest.fn().mockReturnValue([
|
||||
SharedVaultUser.create({
|
||||
|
@ -122,46 +107,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
ongoing_transition: false,
|
||||
ongoing_revisions_transition: false,
|
||||
},
|
||||
60,
|
||||
)
|
||||
})
|
||||
|
||||
it('should create a cross service token for user that has an ongoing transaction', async () => {
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockReturnValue(TransitionStatus.create(TransitionStatus.STATUSES.InProgress).getValue())
|
||||
|
||||
await createUseCase().execute({
|
||||
user,
|
||||
session,
|
||||
})
|
||||
|
||||
expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith(
|
||||
{
|
||||
roles: [
|
||||
{
|
||||
name: 'role1',
|
||||
uuid: '1-3-4',
|
||||
},
|
||||
],
|
||||
belongs_to_shared_vaults: [
|
||||
{
|
||||
shared_vault_uuid: '00000000-0000-0000-0000-000000000000',
|
||||
permission: 'read',
|
||||
},
|
||||
],
|
||||
session: {
|
||||
test: 'test',
|
||||
},
|
||||
user: {
|
||||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
ongoing_transition: true,
|
||||
ongoing_revisions_transition: true,
|
||||
},
|
||||
60,
|
||||
)
|
||||
|
@ -190,8 +135,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
ongoing_transition: false,
|
||||
ongoing_revisions_transition: false,
|
||||
},
|
||||
60,
|
||||
)
|
||||
|
@ -220,8 +163,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
ongoing_transition: false,
|
||||
ongoing_revisions_transition: false,
|
||||
},
|
||||
60,
|
||||
)
|
||||
|
@ -277,8 +218,6 @@ describe('CreateCrossServiceToken', () => {
|
|||
email: 'test@test.te',
|
||||
uuid: '00000000-0000-0000-0000-000000000000',
|
||||
},
|
||||
ongoing_revisions_transition: false,
|
||||
ongoing_transition: false,
|
||||
},
|
||||
60,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { TokenEncoderInterface, CrossServiceTokenData } from '@standardnotes/security'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import { Result, TransitionStatus, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { ProjectorInterface } from '../../../Projection/ProjectorInterface'
|
||||
|
@ -12,7 +12,6 @@ import { UserRepositoryInterface } from '../../User/UserRepositoryInterface'
|
|||
import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO'
|
||||
import { GetSetting } from '../GetSetting/GetSetting'
|
||||
import { SettingName } from '@standardnotes/settings'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { SharedVaultUserRepositoryInterface } from '../../SharedVault/SharedVaultUserRepositoryInterface'
|
||||
|
||||
@injectable()
|
||||
|
@ -26,8 +25,6 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
|||
@inject(TYPES.Auth_AUTH_JWT_TTL) private jwtTTL: number,
|
||||
@inject(TYPES.Auth_GetSetting)
|
||||
private getSettingUseCase: GetSetting,
|
||||
@inject(TYPES.Auth_TransitionStatusRepository)
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
@inject(TYPES.Auth_SharedVaultUserRepository) private sharedVaultUserRepository: SharedVaultUserRepositoryInterface,
|
||||
) {}
|
||||
|
||||
|
@ -47,9 +44,6 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
|||
return Result.fail(`Could not find user with uuid ${dto.userUuid}`)
|
||||
}
|
||||
|
||||
const transitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'items')
|
||||
const revisionsTransitionStatus = await this.transitionStatusRepository.getStatus(user.uuid, 'revisions')
|
||||
|
||||
const roles = await user.roles
|
||||
|
||||
const sharedVaultAssociations = await this.sharedVaultUserRepository.findByUserUuid(
|
||||
|
@ -60,8 +54,6 @@ export class CreateCrossServiceToken implements UseCaseInterface<string> {
|
|||
user: this.projectUser(user),
|
||||
roles: this.projectRoles(roles),
|
||||
shared_vault_owner_context: undefined,
|
||||
ongoing_transition: transitionStatus?.value === TransitionStatus.STATUSES.InProgress,
|
||||
ongoing_revisions_transition: revisionsTransitionStatus?.value === TransitionStatus.STATUSES.InProgress,
|
||||
belongs_to_shared_vaults: sharedVaultAssociations.map((association) => ({
|
||||
shared_vault_uuid: association.props.sharedVaultUuid.value,
|
||||
permission: association.props.permission.value,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'reflect-metadata'
|
||||
|
||||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { TokenEncoderInterface, ValetTokenData, ValetTokenOperation } from '@standardnotes/security'
|
||||
|
||||
|
@ -11,7 +10,6 @@ import { User } from '../../User/User'
|
|||
import { UserSubscriptionType } from '../../Subscription/UserSubscriptionType'
|
||||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
|
||||
describe('CreateValetToken', () => {
|
||||
let tokenEncoder: TokenEncoderInterface<ValetTokenData>
|
||||
|
@ -23,7 +21,6 @@ describe('CreateValetToken', () => {
|
|||
let regularSubscription: UserSubscription
|
||||
let sharedSubscription: UserSubscription
|
||||
let user: User
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
|
||||
const createUseCase = () =>
|
||||
new CreateValetToken(
|
||||
|
@ -33,7 +30,6 @@ describe('CreateValetToken', () => {
|
|||
userSubscriptionService,
|
||||
timer,
|
||||
valetTokenTTL,
|
||||
transitionStatusRepository,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -71,11 +67,6 @@ describe('CreateValetToken', () => {
|
|||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(100)
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockReturnValue(TransitionStatus.create(TransitionStatus.STATUSES.Verified).getValue())
|
||||
})
|
||||
|
||||
it('should create a read valet token', async () => {
|
||||
|
@ -176,7 +167,6 @@ describe('CreateValetToken', () => {
|
|||
{
|
||||
sharedSubscriptionUuid: undefined,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
|
@ -217,7 +207,6 @@ describe('CreateValetToken', () => {
|
|||
{
|
||||
sharedSubscriptionUuid: '2-3-4',
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
|
@ -278,7 +267,6 @@ describe('CreateValetToken', () => {
|
|||
{
|
||||
sharedSubscriptionUuid: undefined,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
ongoingTransition: false,
|
||||
permittedOperation: 'write',
|
||||
permittedResources: [
|
||||
{
|
||||
|
|
|
@ -13,8 +13,6 @@ import { CreateValetTokenDTO } from './CreateValetTokenDTO'
|
|||
import { SubscriptionSettingsAssociationServiceInterface } from '../../Setting/SubscriptionSettingsAssociationServiceInterface'
|
||||
import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface'
|
||||
import { CreateValetTokenPayload } from '../../ValetToken/CreateValetTokenPayload'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
|
||||
@injectable()
|
||||
export class CreateValetToken implements UseCaseInterface {
|
||||
|
@ -27,8 +25,6 @@ export class CreateValetToken implements UseCaseInterface {
|
|||
@inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface,
|
||||
@inject(TYPES.Auth_Timer) private timer: TimerInterface,
|
||||
@inject(TYPES.Auth_VALET_TOKEN_TTL) private valetTokenTTL: number,
|
||||
@inject(TYPES.Auth_TransitionStatusRepository)
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateValetTokenDTO): Promise<CreateValetTokenResponseData> {
|
||||
|
@ -87,8 +83,6 @@ export class CreateValetToken implements UseCaseInterface {
|
|||
sharedSubscriptionUuid = sharedSubscription.uuid
|
||||
}
|
||||
|
||||
const transitionStatus = await this.transitionStatusRepository.getStatus(userUuid, 'items')
|
||||
|
||||
const tokenData: ValetTokenData = {
|
||||
userUuid: dto.userUuid,
|
||||
permittedOperation: dto.operation,
|
||||
|
@ -97,7 +91,6 @@ export class CreateValetToken implements UseCaseInterface {
|
|||
uploadBytesLimit,
|
||||
sharedSubscriptionUuid,
|
||||
regularSubscriptionUuid: regularSubscription.uuid,
|
||||
ongoingTransition: transitionStatus?.value === TransitionStatus.STATUSES.InProgress,
|
||||
}
|
||||
|
||||
const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL)
|
||||
|
|
|
@ -1,107 +0,0 @@
|
|||
import { RoleName, TransitionStatus, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { UpdateTransitionStatus } from './UpdateTransitionStatus'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
describe('UpdateTransitionStatus', () => {
|
||||
let transitionStatusRepository: TransitionStatusRepositoryInterface
|
||||
let roleService: RoleServiceInterface
|
||||
let logger: Logger
|
||||
|
||||
const createUseCase = () => new UpdateTransitionStatus(transitionStatusRepository, roleService, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
|
||||
transitionStatusRepository = {} as jest.Mocked<TransitionStatusRepositoryInterface>
|
||||
transitionStatusRepository.updateStatus = jest.fn()
|
||||
transitionStatusRepository.getStatus = jest.fn().mockResolvedValue(null)
|
||||
|
||||
roleService = {} as jest.Mocked<RoleServiceInterface>
|
||||
roleService.removeRoleFromUser = jest.fn()
|
||||
})
|
||||
|
||||
it('should add TRANSITION_USER role', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
status: 'VERIFIED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(roleService.removeRoleFromUser).toHaveBeenCalledWith(
|
||||
Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
RoleName.create(RoleName.NAMES.TransitionUser).getValue(),
|
||||
)
|
||||
})
|
||||
|
||||
it('should update transition status', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
status: TransitionStatus.STATUSES.InProgress,
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(transitionStatusRepository.updateStatus).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should return error when user uuid is invalid', async () => {
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: 'invalid',
|
||||
status: 'STARTED',
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(result.getError()).toEqual('Given value is not a valid uuid: invalid')
|
||||
})
|
||||
|
||||
it('should not update status if transition is already verified', async () => {
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockResolvedValue(TransitionStatus.create(TransitionStatus.STATUSES.Verified).getValue())
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
status: TransitionStatus.STATUSES.InProgress,
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(transitionStatusRepository.updateStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not update status if transition is already failed', async () => {
|
||||
transitionStatusRepository.getStatus = jest
|
||||
.fn()
|
||||
.mockResolvedValue(TransitionStatus.create(TransitionStatus.STATUSES.Failed).getValue())
|
||||
|
||||
const useCase = createUseCase()
|
||||
|
||||
const result = await useCase.execute({
|
||||
userUuid: '00000000-0000-0000-0000-000000000000',
|
||||
status: TransitionStatus.STATUSES.InProgress,
|
||||
transitionType: 'items',
|
||||
transitionTimestamp: 123,
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(transitionStatusRepository.updateStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
|
@ -1,40 +0,0 @@
|
|||
import { Result, RoleName, TransitionStatus, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Transition/TransitionStatusRepositoryInterface'
|
||||
import { UpdateTransitionStatusDTO } from './UpdateTransitionStatusDTO'
|
||||
import { RoleServiceInterface } from '../../Role/RoleServiceInterface'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
export class UpdateTransitionStatus implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private transitionStatusRepository: TransitionStatusRepositoryInterface,
|
||||
private roleService: RoleServiceInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async execute(dto: UpdateTransitionStatusDTO): Promise<Result<void>> {
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const currentStatus = await this.transitionStatusRepository.getStatus(dto.userUuid, dto.transitionType)
|
||||
if (
|
||||
[TransitionStatus.STATUSES.Verified, TransitionStatus.STATUSES.Failed].includes(currentStatus?.value as string)
|
||||
) {
|
||||
this.logger.info(`User ${dto.userUuid} transition already finished.`)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
const transitionStatus = TransitionStatus.create(dto.status).getValue()
|
||||
|
||||
await this.transitionStatusRepository.updateStatus(dto.userUuid, dto.transitionType, transitionStatus)
|
||||
|
||||
if (dto.transitionType === 'items' && transitionStatus.value === TransitionStatus.STATUSES.Verified) {
|
||||
await this.roleService.removeRoleFromUser(userUuid, RoleName.create(RoleName.NAMES.TransitionUser).getValue())
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export interface UpdateTransitionStatusDTO {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
import { TransitionStatusRepositoryInterface } from '../../Domain/Transition/TransitionStatusRepositoryInterface'
|
||||
|
||||
export class InMemoryTransitionStatusRepository implements TransitionStatusRepositoryInterface {
|
||||
private itemStatuses: Map<string, string> = new Map()
|
||||
private revisionStatuses: Map<string, string> = new Map()
|
||||
|
||||
async updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: TransitionStatus): Promise<void> {
|
||||
if (transitionType === 'items') {
|
||||
this.itemStatuses.set(userUuid, status.value)
|
||||
} else {
|
||||
this.revisionStatuses.set(userUuid, status.value)
|
||||
}
|
||||
}
|
||||
|
||||
async remove(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void> {
|
||||
if (transitionType === 'items') {
|
||||
this.itemStatuses.delete(userUuid)
|
||||
} else {
|
||||
this.revisionStatuses.delete(userUuid)
|
||||
}
|
||||
}
|
||||
|
||||
async getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<TransitionStatus | null> {
|
||||
let status: string | null
|
||||
|
||||
if (transitionType === 'items') {
|
||||
status = this.itemStatuses.get(userUuid) ?? null
|
||||
} else {
|
||||
status = this.revisionStatuses.get(userUuid) ?? null
|
||||
}
|
||||
|
||||
if (status === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return TransitionStatus.create(status).getValue()
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { TransitionStatusRepositoryInterface } from '../../Domain/Transition/TransitionStatusRepositoryInterface'
|
||||
import { TransitionStatus } from '@standardnotes/domain-core'
|
||||
|
||||
export class RedisTransitionStatusRepository implements TransitionStatusRepositoryInterface {
|
||||
private readonly PREFIX = 'transition-back'
|
||||
|
||||
constructor(private redisClient: IORedis.Redis) {}
|
||||
|
||||
async remove(userUuid: string, transitionType: 'items' | 'revisions'): Promise<void> {
|
||||
await this.redisClient.del(`${this.PREFIX}:${transitionType}:${userUuid}`)
|
||||
}
|
||||
|
||||
async updateStatus(userUuid: string, transitionType: 'items' | 'revisions', status: TransitionStatus): Promise<void> {
|
||||
switch (status.value) {
|
||||
case TransitionStatus.STATUSES.Failed:
|
||||
case TransitionStatus.STATUSES.Verified:
|
||||
await this.redisClient.set(`${this.PREFIX}:${transitionType}:${userUuid}`, status.value)
|
||||
break
|
||||
case TransitionStatus.STATUSES.InProgress: {
|
||||
const ttl24Hours = 86_400
|
||||
await this.redisClient.setex(`${this.PREFIX}:${transitionType}:${userUuid}`, ttl24Hours, status.value)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getStatus(userUuid: string, transitionType: 'items' | 'revisions'): Promise<TransitionStatus | null> {
|
||||
const status = await this.redisClient.get(`${this.PREFIX}:${transitionType}:${userUuid}`)
|
||||
|
||||
if (status === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
const transitionStatusOrError = TransitionStatus.create(status)
|
||||
if (transitionStatusOrError.isFailed()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return transitionStatusOrError.getValue()
|
||||
}
|
||||
}
|
|
@ -21,36 +21,25 @@ describe('RoleName', () => {
|
|||
const plusUserRole = RoleName.create(RoleName.NAMES.PlusUser).getValue()
|
||||
const coreUser = RoleName.create(RoleName.NAMES.CoreUser).getValue()
|
||||
const internalTeamUser = RoleName.create(RoleName.NAMES.InternalTeamUser).getValue()
|
||||
const transitionUser = RoleName.create(RoleName.NAMES.TransitionUser).getValue()
|
||||
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(transitionUser)).toBeTruthy()
|
||||
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(transitionUser)).toBeTruthy()
|
||||
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(transitionUser)).toBeTruthy()
|
||||
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(transitionUser)).toBeTruthy()
|
||||
|
||||
expect(transitionUser.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(transitionUser.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
|
||||
expect(transitionUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeFalsy()
|
||||
expect(transitionUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
expect(transitionUser.hasMoreOrEqualPowerTo(transitionUser)).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -8,7 +8,6 @@ export class RoleName extends ValueObject<RoleNameProps> {
|
|||
PlusUser: 'PLUS_USER',
|
||||
ProUser: 'PRO_USER',
|
||||
InternalTeamUser: 'INTERNAL_TEAM_USER',
|
||||
TransitionUser: 'TRANSITION_USER',
|
||||
VaultsUser: 'VAULTS_USER',
|
||||
}
|
||||
|
||||
|
@ -21,20 +20,12 @@ export class RoleName extends ValueObject<RoleNameProps> {
|
|||
case RoleName.NAMES.InternalTeamUser:
|
||||
return true
|
||||
case RoleName.NAMES.ProUser:
|
||||
return [
|
||||
RoleName.NAMES.CoreUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
RoleName.NAMES.ProUser,
|
||||
RoleName.NAMES.TransitionUser,
|
||||
].includes(roleName.value)
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser].includes(roleName.value)
|
||||
case RoleName.NAMES.PlusUser:
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.TransitionUser].includes(
|
||||
roleName.value,
|
||||
)
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser].includes(roleName.value)
|
||||
case RoleName.NAMES.CoreUser:
|
||||
case RoleName.NAMES.TransitionUser:
|
||||
case RoleName.NAMES.VaultsUser:
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.TransitionUser].includes(roleName.value)
|
||||
return [RoleName.NAMES.CoreUser].includes(roleName.value)
|
||||
/*istanbul ignore next*/
|
||||
default:
|
||||
throw new Error(`Invalid role name: ${this.value}`)
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TransitionStatusProps } from './TransitionStatusProps'
|
||||
|
||||
export class TransitionStatus extends ValueObject<TransitionStatusProps> {
|
||||
static readonly STATUSES = {
|
||||
InProgress: 'IN_PROGRESS',
|
||||
Failed: 'FAILED',
|
||||
Verified: 'VERIFIED',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: TransitionStatusProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<TransitionStatus> {
|
||||
const isValidName = Object.values(this.STATUSES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<TransitionStatus>('Invalid transition status name.')
|
||||
} else {
|
||||
return Result.ok<TransitionStatus>(new TransitionStatus({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export interface TransitionStatusProps {
|
||||
value: string
|
||||
}
|
|
@ -69,8 +69,5 @@ export * from './SharedVault/SharedVaultUserProps'
|
|||
export * from './Subscription/SubscriptionPlanName'
|
||||
export * from './Subscription/SubscriptionPlanNameProps'
|
||||
|
||||
export * from './Transition/TransitionStatus'
|
||||
export * from './Transition/TransitionStatusProps'
|
||||
|
||||
export * from './UseCase/SyncUseCaseInterface'
|
||||
export * from './UseCase/UseCaseInterface'
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export interface AccountDeletionRequestedEventPayload {
|
||||
userUuid: string
|
||||
roleNames: string[]
|
||||
userCreatedAtTimestamp: number
|
||||
regularSubscriptionUuid: string | undefined
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export interface DuplicateItemSyncedEventPayload {
|
||||
itemUuid: string
|
||||
userUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export interface ItemDumpedEventPayload {
|
||||
fileDumpPath: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export interface ItemRevisionCreationRequestedEventPayload {
|
||||
itemUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export interface RevisionsCopyRequestedEventPayload {
|
||||
newItemUuid: string
|
||||
originalItemUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
|
||||
import { TransitionRequestedEventPayload } from './TransitionRequestedEventPayload'
|
||||
|
||||
export interface TransitionRequestedEvent extends DomainEventInterface {
|
||||
type: 'TRANSITION_REQUESTED'
|
||||
payload: TransitionRequestedEventPayload
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export interface TransitionRequestedEventPayload {
|
||||
userUuid: string
|
||||
type: 'items' | 'revisions'
|
||||
timestamp: number
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
|
||||
import { TransitionStatusUpdatedEventPayload } from './TransitionStatusUpdatedEventPayload'
|
||||
|
||||
export interface TransitionStatusUpdatedEvent extends DomainEventInterface {
|
||||
type: 'TRANSITION_STATUS_UPDATED'
|
||||
payload: TransitionStatusUpdatedEventPayload
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export interface TransitionStatusUpdatedEventPayload {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
page?: number
|
||||
}
|
|
@ -98,10 +98,6 @@ export * from './Event/SubscriptionRevertRequestedEvent'
|
|||
export * from './Event/SubscriptionRevertRequestedEventPayload'
|
||||
export * from './Event/SubscriptionSyncRequestedEvent'
|
||||
export * from './Event/SubscriptionSyncRequestedEventPayload'
|
||||
export * from './Event/TransitionRequestedEvent'
|
||||
export * from './Event/TransitionRequestedEventPayload'
|
||||
export * from './Event/TransitionStatusUpdatedEvent'
|
||||
export * from './Event/TransitionStatusUpdatedEventPayload'
|
||||
export * from './Event/UserAddedToSharedVaultEvent'
|
||||
export * from './Event/UserAddedToSharedVaultEventPayload'
|
||||
export * from './Event/UserDesignatedAsSurvivorInSharedVaultEvent'
|
||||
|
|
|
@ -223,27 +223,4 @@ describe('ValetTokenAuthMiddleware', () => {
|
|||
|
||||
expect(next).toHaveBeenCalledWith(error)
|
||||
})
|
||||
|
||||
it('should throw an error if the valet token indicates an ongoing transition', async () => {
|
||||
request.headers['x-valet-token'] = 'valet-token'
|
||||
|
||||
tokenDecoder.decodeToken = jest.fn().mockReturnValue({
|
||||
userUuid: '1-2-3',
|
||||
permittedResources: [
|
||||
{
|
||||
remoteIdentifier: '00000000-0000-0000-0000-000000000000',
|
||||
unencryptedFileSize: 30,
|
||||
},
|
||||
],
|
||||
permittedOperation: 'write',
|
||||
uploadBytesLimit: -1,
|
||||
uploadBytesUsed: 80,
|
||||
ongoingTransition: true,
|
||||
})
|
||||
|
||||
await createMiddleware().handler(request, response, next)
|
||||
|
||||
expect(response.status).toHaveBeenCalledWith(500)
|
||||
expect(next).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -46,19 +46,6 @@ export class ValetTokenAuthMiddleware extends BaseMiddleware {
|
|||
return
|
||||
}
|
||||
|
||||
if (valetTokenData.ongoingTransition === true) {
|
||||
this.logger.error(`Cannot perform file operations for user ${valetTokenData.userUuid} during transition`)
|
||||
|
||||
response.status(500).send({
|
||||
error: {
|
||||
tag: 'ongoing-transition',
|
||||
message: 'Cannot perform file operations during transition',
|
||||
},
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
for (const resource of valetTokenData.permittedResources) {
|
||||
const resourceUuidOrError = Uuid.create(resource.remoteIdentifier)
|
||||
if (resourceUuidOrError.isFailed()) {
|
||||
|
|
|
@ -9,10 +9,3 @@ PSEUDO_KEY_PARAMS_KEY=
|
|||
VALET_TOKEN_SECRET=
|
||||
|
||||
FILES_SERVER_URL=
|
||||
|
||||
SECONDARY_DB_ENABLED=false
|
||||
MONGO_HOST=localhost
|
||||
MONGO_PORT=27017
|
||||
MONGO_USERNAME=standardnotes
|
||||
MONGO_PASSWORD=standardnotes
|
||||
MONGO_DATABASE=standardnotes
|
||||
|
|
|
@ -17,20 +17,7 @@ DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "l
|
|||
DB_MIGRATIONS_PATH=dist/migrations/*.js
|
||||
DB_TYPE=mysql
|
||||
|
||||
REDIS_URL=redis://cache
|
||||
CACHE_TYPE=redis
|
||||
|
||||
SNS_TOPIC_ARN=
|
||||
SNS_AWS_REGION=
|
||||
SQS_QUEUE_URL=
|
||||
SQS_AWS_REGION=
|
||||
S3_AWS_REGION=
|
||||
S3_BACKUP_BUCKET_NAME=
|
||||
|
||||
# (Optional) Mongo Setup
|
||||
SECONDARY_DB_ENABLED=false
|
||||
MONGO_HOST=
|
||||
MONGO_PORT=
|
||||
MONGO_USERNAME=
|
||||
MONGO_PASSWORD=
|
||||
MONGO_DATABASE=
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
"inversify": "^6.0.1",
|
||||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"mongodb": "^6.0.0",
|
||||
"mysql2": "^3.0.1",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"sqlite3": "^5.1.6",
|
||||
|
|
|
@ -4,11 +4,9 @@ import {
|
|||
MapperInterface,
|
||||
ServiceIdentifier,
|
||||
} from '@standardnotes/domain-core'
|
||||
import Redis from 'ioredis'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
import { MongoRepository, Repository } from 'typeorm'
|
||||
import { Repository } from 'typeorm'
|
||||
import * as winston from 'winston'
|
||||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
|
||||
import { Revision } from '../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../Domain/Revision/RevisionMetadata'
|
||||
|
@ -38,7 +36,6 @@ import {
|
|||
SQSEventMessageHandler,
|
||||
DirectCallEventMessageHandler,
|
||||
DirectCallDomainEventPublisher,
|
||||
SNSOpenTelemetryDomainEventPublisher,
|
||||
SQSOpenTelemetryDomainEventSubscriber,
|
||||
} from '@standardnotes/domain-events-infra'
|
||||
import { DumpRepositoryInterface } from '../Domain/Dump/DumpRepositoryInterface'
|
||||
|
@ -51,30 +48,18 @@ import { S3DumpRepository } from '../Infra/S3/S3ItemDumpRepository'
|
|||
import { RevisionItemStringMapper } from '../Mapping/Backup/RevisionItemStringMapper'
|
||||
import { BaseRevisionsController } from '../Infra/InversifyExpress/Base/BaseRevisionsController'
|
||||
import { Transform } from 'stream'
|
||||
import { MongoDBRevision } from '../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
import { MongoDBRevisionRepository } from '../Infra/TypeORM/MongoDB/MongoDBRevisionRepository'
|
||||
import { SQLLegacyRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionMetadataPersistenceMapper'
|
||||
import { SQLLegacyRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLLegacyRevisionPersistenceMapper'
|
||||
import { MongoDBRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/MongoDB/MongoDBRevisionMetadataPersistenceMapper'
|
||||
import { MongoDBRevisionPersistenceMapper } from '../Mapping/Persistence/MongoDB/MongoDBRevisionPersistenceMapper'
|
||||
import { RevisionHttpMapper } from '../Mapping/Http/RevisionHttpMapper'
|
||||
import { RevisionRepositoryResolverInterface } from '../Domain/Revision/RevisionRepositoryResolverInterface'
|
||||
import { TypeORMRevisionRepositoryResolver } from '../Infra/TypeORM/TypeORMRevisionRepositoryResolver'
|
||||
import { RevisionMetadataHttpRepresentation } from '../Mapping/Http/RevisionMetadataHttpRepresentation'
|
||||
import { RevisionHttpRepresentation } from '../Mapping/Http/RevisionHttpRepresentation'
|
||||
import { TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser } from '../Domain/UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
import { SQLRevisionRepository } from '../Infra/TypeORM/SQL/SQLRevisionRepository'
|
||||
import { SQLRevisionMetadataPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionMetadataPersistenceMapper'
|
||||
import { SQLRevisionPersistenceMapper } from '../Mapping/Persistence/SQL/SQLRevisionPersistenceMapper'
|
||||
import { RemoveRevisionsFromSharedVault } from '../Domain/UseCase/RemoveRevisionsFromSharedVault/RemoveRevisionsFromSharedVault'
|
||||
import { ItemRemovedFromSharedVaultEventHandler } from '../Domain/Handler/ItemRemovedFromSharedVaultEventHandler'
|
||||
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
|
||||
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
|
||||
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
|
||||
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
|
||||
import { CreateRevisionFromDump } from '../Domain/UseCase/CreateRevisionFromDump/CreateRevisionFromDump'
|
||||
|
||||
export class ContainerConfigLoader {
|
||||
|
@ -95,8 +80,6 @@ export class ContainerConfigLoader {
|
|||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
const container = new Container({
|
||||
defaultScope: 'Singleton',
|
||||
|
@ -106,22 +89,6 @@ export class ContainerConfigLoader {
|
|||
.bind<boolean>(TYPES.Revisions_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING)
|
||||
.toConstantValue(isConfiguredForHomeServerOrSelfHosting)
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Revisions_Redis).toConstantValue(redis)
|
||||
container
|
||||
.bind<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionRepository(container.get<Redis>(TYPES.Revisions_Redis)))
|
||||
}
|
||||
|
||||
let logger: winston.Logger
|
||||
if (configuration?.logger) {
|
||||
logger = configuration.logger as winston.Logger
|
||||
|
@ -150,41 +117,10 @@ export class ContainerConfigLoader {
|
|||
|
||||
if (!isConfiguredForHomeServer) {
|
||||
// env vars
|
||||
container.bind(TYPES.Revisions_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN'))
|
||||
container.bind(TYPES.Revisions_SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true))
|
||||
container.bind(TYPES.Revisions_SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL'))
|
||||
container.bind(TYPES.Revisions_S3_AWS_REGION).toConstantValue(env.get('S3_AWS_REGION', true))
|
||||
container.bind(TYPES.Revisions_S3_BACKUP_BUCKET_NAME).toConstantValue(env.get('S3_BACKUP_BUCKET_NAME', true))
|
||||
|
||||
container.bind<SNSClient>(TYPES.Revisions_SNS).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Revisions_Env)
|
||||
|
||||
const snsConfig: SNSClientConfig = {
|
||||
apiVersion: 'latest',
|
||||
region: env.get('SNS_AWS_REGION', true),
|
||||
}
|
||||
if (env.get('SNS_ENDPOINT', true)) {
|
||||
snsConfig.endpoint = env.get('SNS_ENDPOINT', true)
|
||||
}
|
||||
if (env.get('SNS_ACCESS_KEY_ID', true) && env.get('SNS_SECRET_ACCESS_KEY', true)) {
|
||||
snsConfig.credentials = {
|
||||
accessKeyId: env.get('SNS_ACCESS_KEY_ID', true),
|
||||
secretAccessKey: env.get('SNS_SECRET_ACCESS_KEY', true),
|
||||
}
|
||||
}
|
||||
|
||||
return new SNSClient(snsConfig)
|
||||
})
|
||||
|
||||
container
|
||||
.bind<DomainEventPublisherInterface>(TYPES.Revisions_DomainEventPublisher)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new SNSOpenTelemetryDomainEventPublisher(
|
||||
context.container.get(TYPES.Revisions_SNS),
|
||||
context.container.get(TYPES.Revisions_SNS_TOPIC_ARN),
|
||||
)
|
||||
})
|
||||
|
||||
container.bind<SQSClient>(TYPES.Revisions_SQS).toDynamicValue((context: interfaces.Context) => {
|
||||
const env: Env = context.container.get(TYPES.Revisions_Env)
|
||||
|
||||
|
@ -223,10 +159,6 @@ export class ContainerConfigLoader {
|
|||
.toConstantValue(directCallDomainEventPublisher)
|
||||
}
|
||||
|
||||
container
|
||||
.bind<DomainEventFactoryInterface>(TYPES.Revisions_DomainEventFactory)
|
||||
.toConstantValue(new DomainEventFactory(container.get(TYPES.Revisions_Timer)))
|
||||
|
||||
// Map
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, SQLLegacyRevision>>(
|
||||
|
@ -242,14 +174,6 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<MapperInterface<Revision, SQLRevision>>(TYPES.Revisions_SQLRevisionPersistenceMapper)
|
||||
.toConstantValue(new SQLRevisionPersistenceMapper(container.get<TimerInterface>(TYPES.Revisions_Timer)))
|
||||
container
|
||||
.bind<MapperInterface<RevisionMetadata, MongoDBRevision>>(
|
||||
TYPES.Revisions_MongoDBRevisionMetadataPersistenceMapper,
|
||||
)
|
||||
.toConstantValue(new MongoDBRevisionMetadataPersistenceMapper())
|
||||
container
|
||||
.bind<MapperInterface<Revision, MongoDBRevision>>(TYPES.Revisions_MongoDBRevisionPersistenceMapper)
|
||||
.toConstantValue(new MongoDBRevisionPersistenceMapper(container.get<TimerInterface>(TYPES.Revisions_Timer)))
|
||||
|
||||
// ORM
|
||||
container
|
||||
|
@ -284,36 +208,6 @@ export class ContainerConfigLoader {
|
|||
),
|
||||
)
|
||||
|
||||
if (isSecondaryDatabaseEnabled) {
|
||||
container
|
||||
.bind<MongoRepository<MongoDBRevision>>(TYPES.Revisions_ORMMongoRevisionRepository)
|
||||
.toConstantValue(appDataSource.getMongoRepository(MongoDBRevision))
|
||||
|
||||
container
|
||||
.bind<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
.toConstantValue(
|
||||
new MongoDBRevisionRepository(
|
||||
container.get<MongoRepository<MongoDBRevision>>(TYPES.Revisions_ORMMongoRevisionRepository),
|
||||
container.get<MapperInterface<RevisionMetadata, MongoDBRevision>>(
|
||||
TYPES.Revisions_MongoDBRevisionMetadataPersistenceMapper,
|
||||
),
|
||||
container.get<MapperInterface<Revision, MongoDBRevision>>(TYPES.Revisions_MongoDBRevisionPersistenceMapper),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
container
|
||||
.bind<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver)
|
||||
.toConstantValue(
|
||||
new TypeORMRevisionRepositoryResolver(
|
||||
container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: null,
|
||||
),
|
||||
)
|
||||
|
||||
container
|
||||
.bind<GetRequiredRoleToViewRevision>(TYPES.Revisions_GetRequiredRoleToViewRevision)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
|
@ -351,56 +245,28 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<GetRevisionsMetada>(TYPES.Revisions_GetRevisionsMetada)
|
||||
.toConstantValue(
|
||||
new GetRevisionsMetada(
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
),
|
||||
new GetRevisionsMetada(container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)),
|
||||
)
|
||||
container
|
||||
.bind<GetRevision>(TYPES.Revisions_GetRevision)
|
||||
.toConstantValue(
|
||||
new GetRevision(container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver)),
|
||||
new GetRevision(container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)),
|
||||
)
|
||||
container
|
||||
.bind<DeleteRevision>(TYPES.Revisions_DeleteRevision)
|
||||
.toConstantValue(
|
||||
new DeleteRevision(
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
),
|
||||
new DeleteRevision(container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)),
|
||||
)
|
||||
container
|
||||
.bind<CopyRevisions>(TYPES.Revisions_CopyRevisions)
|
||||
.toConstantValue(
|
||||
new CopyRevisions(
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser,
|
||||
)
|
||||
.toConstantValue(
|
||||
new TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser(
|
||||
container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: null,
|
||||
isConfiguredForInMemoryCache
|
||||
? null
|
||||
: container.get<TransitionRepositoryInterface>(TYPES.Revisions_TransitionStatusRepository),
|
||||
container.get<TimerInterface>(TYPES.Revisions_Timer),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Revisions_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Revisions_DomainEventFactory),
|
||||
),
|
||||
new CopyRevisions(container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository)),
|
||||
)
|
||||
container
|
||||
.bind<RemoveRevisionsFromSharedVault>(TYPES.Revisions_RemoveRevisionsFromSharedVault)
|
||||
.toConstantValue(
|
||||
new RemoveRevisionsFromSharedVault(
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<RevisionRepositoryInterface>(TYPES.Revisions_MongoDBRevisionRepository)
|
||||
: container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
|
@ -408,7 +274,7 @@ export class ContainerConfigLoader {
|
|||
.toConstantValue(
|
||||
new CreateRevisionFromDump(
|
||||
container.get<DumpRepositoryInterface>(TYPES.Revisions_DumpRepository),
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -448,7 +314,7 @@ export class ContainerConfigLoader {
|
|||
.bind<AccountDeletionRequestedEventHandler>(TYPES.Revisions_AccountDeletionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new AccountDeletionRequestedEventHandler(
|
||||
container.get<RevisionRepositoryResolverInterface>(TYPES.Revisions_RevisionRepositoryResolver),
|
||||
container.get<RevisionRepositoryInterface>(TYPES.Revisions_SQLRevisionRepository),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
|
@ -468,17 +334,6 @@ export class ContainerConfigLoader {
|
|||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionRequestedEventHandler>(TYPES.Revisions_TransitionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new TransitionRequestedEventHandler(
|
||||
false,
|
||||
container.get<TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser,
|
||||
),
|
||||
container.get<winston.Logger>(TYPES.Revisions_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultRemovedEventHandler>(TYPES.Revisions_SharedVaultRemovedEventHandler)
|
||||
.toConstantValue(
|
||||
|
@ -493,7 +348,6 @@ export class ContainerConfigLoader {
|
|||
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.Revisions_AccountDeletionRequestedEventHandler)],
|
||||
['REVISIONS_COPY_REQUESTED', container.get(TYPES.Revisions_RevisionsCopyRequestedEventHandler)],
|
||||
['ITEM_REMOVED_FROM_SHARED_VAULT', container.get(TYPES.Revisions_ItemRemovedFromSharedVaultEventHandler)],
|
||||
['TRANSITION_REQUESTED', container.get(TYPES.Revisions_TransitionRequestedEventHandler)],
|
||||
['SHARED_VAULT_REMOVED', container.get(TYPES.Revisions_SharedVaultRemovedEventHandler)],
|
||||
])
|
||||
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import { DataSource, EntityTarget, LoggerOptions, MongoRepository, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
|
||||
import { SQLLegacyRevision } from '../Infra/TypeORM/SQL/SQLLegacyRevision'
|
||||
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
import { MongoDBRevision } from '../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
import { SQLRevision } from '../Infra/TypeORM/SQL/SQLRevision'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
private _secondaryDataSource: DataSource | undefined
|
||||
|
||||
constructor(
|
||||
private configuration: {
|
||||
|
@ -27,43 +25,8 @@ export class AppDataSource {
|
|||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
getMongoRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): MongoRepository<Entity> {
|
||||
if (!this._secondaryDataSource) {
|
||||
throw new Error('Secondary DataSource not initialized')
|
||||
}
|
||||
|
||||
return this._secondaryDataSource.getMongoRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
const secondaryDataSource = this.secondaryDataSource
|
||||
if (secondaryDataSource) {
|
||||
await secondaryDataSource.initialize()
|
||||
}
|
||||
}
|
||||
|
||||
get secondaryDataSource(): DataSource | undefined {
|
||||
this.configuration.env.load()
|
||||
|
||||
if (this.configuration.env.get('SECONDARY_DB_ENABLED', true) !== 'true') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
this._secondaryDataSource = new DataSource({
|
||||
type: 'mongodb',
|
||||
host: this.configuration.env.get('MONGO_HOST'),
|
||||
authSource: 'admin',
|
||||
port: parseInt(this.configuration.env.get('MONGO_PORT')),
|
||||
username: this.configuration.env.get('MONGO_USERNAME'),
|
||||
password: this.configuration.env.get('MONGO_PASSWORD', true),
|
||||
database: this.configuration.env.get('MONGO_DATABASE'),
|
||||
entities: [MongoDBRevision],
|
||||
retryWrites: false,
|
||||
synchronize: true,
|
||||
})
|
||||
|
||||
return this._secondaryDataSource
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
|
|
|
@ -13,22 +13,15 @@ const TYPES = {
|
|||
Revisions_SQLRevisionMetadataPersistenceMapper: Symbol.for('Revisions_SQLRevisionMetadataPersistenceMapper'),
|
||||
Revisions_SQLLegacyRevisionPersistenceMapper: Symbol.for('Revisions_SQLLegacyRevisionPersistenceMapper'),
|
||||
Revisions_SQLRevisionPersistenceMapper: Symbol.for('Revisions_SQLRevisionPersistenceMapper'),
|
||||
Revisions_MongoDBRevisionMetadataPersistenceMapper: Symbol.for('Revisions_MongoDBRevisionMetadataPersistenceMapper'),
|
||||
Revisions_MongoDBRevisionPersistenceMapper: Symbol.for('Revisions_MongoDBRevisionPersistenceMapper'),
|
||||
Revisions_RevisionItemStringMapper: Symbol.for('Revisions_RevisionItemStringMapper'),
|
||||
Revisions_RevisionHttpMapper: Symbol.for('Revisions_RevisionHttpMapper'),
|
||||
Revisions_RevisionMetadataHttpMapper: Symbol.for('Revisions_RevisionMetadataHttpMapper'),
|
||||
// ORM
|
||||
Revisions_ORMRevisionRepository: Symbol.for('Revisions_ORMRevisionRepository'),
|
||||
Revisions_ORMLegacyRevisionRepository: Symbol.for('Revisions_ORMLegacyRevisionRepository'),
|
||||
// Mongo
|
||||
Revisions_ORMMongoRevisionRepository: Symbol.for('Revisions_ORMMongoRevisionRepository'),
|
||||
// Repositories
|
||||
Revisions_SQLRevisionRepository: Symbol.for('Revisions_SQLRevisionRepository'),
|
||||
Revisions_MongoDBRevisionRepository: Symbol.for('Revisions_MongoDBRevisionRepository'),
|
||||
Revisions_DumpRepository: Symbol.for('Revisions_DumpRepository'),
|
||||
Revisions_RevisionRepositoryResolver: Symbol.for('Revisions_RevisionRepositoryResolver'),
|
||||
Revisions_TransitionStatusRepository: Symbol.for('Revisions_TransitionStatusRepository'),
|
||||
// env vars
|
||||
Revisions_AUTH_JWT_SECRET: Symbol.for('Revisions_AUTH_JWT_SECRET'),
|
||||
Revisions_SQS_QUEUE_URL: Symbol.for('Revisions_SQS_QUEUE_URL'),
|
||||
|
@ -47,9 +40,6 @@ const TYPES = {
|
|||
Revisions_DeleteRevision: Symbol.for('Revisions_DeleteRevision'),
|
||||
Revisions_CopyRevisions: Symbol.for('Revisions_CopyRevisions'),
|
||||
Revisions_GetRequiredRoleToViewRevision: Symbol.for('Revisions_GetRequiredRoleToViewRevision'),
|
||||
Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser: Symbol.for(
|
||||
'Revisions_TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser',
|
||||
),
|
||||
Revisions_RemoveRevisionsFromSharedVault: Symbol.for('Revisions_RemoveRevisionsFromSharedVault'),
|
||||
Revisions_CreateRevisionFromDump: Symbol.for('Revisions_CreateRevisionFromDump'),
|
||||
// Controller
|
||||
|
@ -61,7 +51,6 @@ const TYPES = {
|
|||
Revisions_AccountDeletionRequestedEventHandler: Symbol.for('Revisions_AccountDeletionRequestedEventHandler'),
|
||||
Revisions_RevisionsCopyRequestedEventHandler: Symbol.for('Revisions_RevisionsCopyRequestedEventHandler'),
|
||||
Revisions_ItemRemovedFromSharedVaultEventHandler: Symbol.for('Revisions_ItemRemovedFromSharedVaultEventHandler'),
|
||||
Revisions_TransitionRequestedEventHandler: Symbol.for('Revisions_TransitionRequestedEventHandler'),
|
||||
Revisions_SharedVaultRemovedEventHandler: Symbol.for('Revisions_SharedVaultRemovedEventHandler'),
|
||||
// Services
|
||||
Revisions_CrossServiceTokenDecoder: Symbol.for('Revisions_CrossServiceTokenDecoder'),
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/* istanbul ignore file */
|
||||
import { DomainEventService, TransitionStatusUpdatedEvent } from '@standardnotes/domain-events'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { DomainEventFactoryInterface } from './DomainEventFactoryInterface'
|
||||
|
||||
export class DomainEventFactory implements DomainEventFactoryInterface {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
}): TransitionStatusUpdatedEvent {
|
||||
return {
|
||||
type: 'TRANSITION_STATUS_UPDATED',
|
||||
createdAt: this.timer.getUTCDate(),
|
||||
meta: {
|
||||
correlation: {
|
||||
userIdentifier: dto.userUuid,
|
||||
userIdentifierType: 'uuid',
|
||||
},
|
||||
origin: DomainEventService.Revisions,
|
||||
},
|
||||
payload: dto,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { TransitionStatusUpdatedEvent } from '@standardnotes/domain-events'
|
||||
|
||||
export interface DomainEventFactoryInterface {
|
||||
createTransitionStatusUpdatedEvent(dto: {
|
||||
userUuid: string
|
||||
transitionType: 'items' | 'revisions'
|
||||
transitionTimestamp: number
|
||||
status: string
|
||||
}): TransitionStatusUpdatedEvent
|
||||
}
|
|
@ -4,23 +4,18 @@ import { AccountDeletionRequestedEvent } from '@standardnotes/domain-events'
|
|||
import { Logger } from 'winston'
|
||||
import { AccountDeletionRequestedEventHandler } from './AccountDeletionRequestedEventHandler'
|
||||
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../Revision/RevisionRepositoryResolverInterface'
|
||||
|
||||
describe('AccountDeletionRequestedEventHandler', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
let logger: Logger
|
||||
let event: AccountDeletionRequestedEvent
|
||||
|
||||
const createHandler = () => new AccountDeletionRequestedEventHandler(revisionRepositoryResolver, logger)
|
||||
const createHandler = () => new AccountDeletionRequestedEventHandler(revisionRepository, logger)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.removeByUserUuid = jest.fn()
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
|
||||
logger = {} as jest.Mocked<Logger>
|
||||
logger.info = jest.fn()
|
||||
logger.warn = jest.fn()
|
||||
|
@ -32,7 +27,6 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||
userUuid: '2-3-4',
|
||||
userCreatedAtTimestamp: 1,
|
||||
regularSubscriptionUuid: '1-2-3',
|
||||
roleNames: ['CORE_USER'],
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -49,13 +43,4 @@ describe('AccountDeletionRequestedEventHandler', () => {
|
|||
|
||||
expect(revisionRepository.removeByUserUuid).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should do nothing if role names are not valid', async () => {
|
||||
event.payload.userUuid = '84c0f8e8-544a-4c7e-9adf-26209303bc1d'
|
||||
event.payload.roleNames = ['INVALID_ROLE_NAME']
|
||||
|
||||
await createHandler().handle(event)
|
||||
|
||||
expect(revisionRepository.removeByUserUuid).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { RoleNameCollection, Uuid } from '@standardnotes/domain-core'
|
||||
import { Uuid } from '@standardnotes/domain-core'
|
||||
import { AccountDeletionRequestedEvent, DomainEventHandlerInterface } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { RevisionRepositoryResolverInterface } from '../Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class AccountDeletionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private revisionRepositoryResolver: RevisionRepositoryResolverInterface,
|
||||
private revisionRepository: RevisionRepositoryInterface,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
|
@ -19,17 +18,7 @@ export class AccountDeletionRequestedEventHandler implements DomainEventHandlerI
|
|||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(event.payload.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
this.logger.error(`Failed account cleanup: ${roleNamesOrError.getError()}`)
|
||||
|
||||
return
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
await revisionRepository.removeByUserUuid(userUuid)
|
||||
await this.revisionRepository.removeByUserUuid(userUuid)
|
||||
|
||||
this.logger.info(`Finished account cleanup for user: ${event.payload.userUuid}`)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ export class ItemDumpedEventHandler implements DomainEventHandlerInterface {
|
|||
async handle(event: ItemDumpedEvent): Promise<void> {
|
||||
const result = await this.createRevisionFromDump.execute({
|
||||
filePath: event.payload.fileDumpPath,
|
||||
roleNames: event.payload.roleNames,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
|
|
|
@ -23,7 +23,6 @@ describe('RevisionsCopyRequestedEventHandler', () => {
|
|||
event.payload = {
|
||||
newItemUuid: '1-2-3',
|
||||
originalItemUuid: '2-3-4',
|
||||
roleNames: ['CORE_USER'],
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ export class RevisionsCopyRequestedEventHandler implements DomainEventHandlerInt
|
|||
const result = await this.copyRevisions.execute({
|
||||
newItemUuid: event.payload.newItemUuid,
|
||||
originalItemUuid: event.payload.originalItemUuid,
|
||||
roleNames: event.payload.roleNames,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { DomainEventHandlerInterface, TransitionRequestedEvent } from '@standardnotes/domain-events'
|
||||
import { Logger } from 'winston'
|
||||
import { TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser } from '../UseCase/Transition/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser/TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser'
|
||||
|
||||
export class TransitionRequestedEventHandler implements DomainEventHandlerInterface {
|
||||
constructor(
|
||||
private disabled: boolean,
|
||||
private transitionRevisionsFromPrimaryToSecondaryDatabaseForUser: TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async handle(event: TransitionRequestedEvent): Promise<void> {
|
||||
if (this.disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (event.payload.type !== 'revisions') {
|
||||
return
|
||||
}
|
||||
|
||||
const result = await this.transitionRevisionsFromPrimaryToSecondaryDatabaseForUser.execute({
|
||||
userUuid: event.payload.userUuid,
|
||||
timestamp: event.payload.timestamp,
|
||||
})
|
||||
|
||||
if (result.isFailed()) {
|
||||
this.logger.error(`[${event.payload.userUuid}] Failed to transition: ${result.getError()}`)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { RoleNameCollection } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionRepositoryInterface } from './RevisionRepositoryInterface'
|
||||
|
||||
export interface RevisionRepositoryResolverInterface {
|
||||
resolve(roleNames: RoleNameCollection): RevisionRepositoryInterface
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export interface TransitionRepositoryInterface {
|
||||
getPagingProgress(userUuid: string): Promise<number>
|
||||
setPagingProgress(userUuid: string, progress: number): Promise<void>
|
||||
getIntegrityProgress(userUuid: string): Promise<number>
|
||||
setIntegrityProgress(userUuid: string, progress: number): Promise<void>
|
||||
}
|
|
@ -2,21 +2,16 @@ import { Result } from '@standardnotes/domain-core'
|
|||
import { Revision } from '../../Revision/Revision'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
import { CopyRevisions } from './CopyRevisions'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
|
||||
describe('CopyRevisions', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
|
||||
const createUseCase = () => new CopyRevisions(revisionRepositoryResolver)
|
||||
const createUseCase = () => new CopyRevisions(revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.findByItemUuid = jest.fn().mockReturnValue([{} as jest.Mocked<Revision>])
|
||||
revisionRepository.insert = jest.fn()
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
})
|
||||
|
||||
it('should not copy revisions to new item if revision creation fails', async () => {
|
||||
|
@ -26,7 +21,6 @@ describe('CopyRevisions', () => {
|
|||
const result = await createUseCase().execute({
|
||||
originalItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
newItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
@ -34,21 +28,10 @@ describe('CopyRevisions', () => {
|
|||
revisionMock.mockRestore()
|
||||
})
|
||||
|
||||
it('should do nothing if the role names are not valid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
originalItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
newItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should copy revisions to new item', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
originalItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
newItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
@ -60,7 +43,6 @@ describe('CopyRevisions', () => {
|
|||
const result = await createUseCase().execute({
|
||||
originalItemUuid: '1-2-3',
|
||||
newItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
@ -70,7 +52,6 @@ describe('CopyRevisions', () => {
|
|||
const result = await createUseCase().execute({
|
||||
newItemUuid: '1-2-3',
|
||||
originalItemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from '../../Revision/Revision'
|
||||
|
||||
import { CopyRevisionsDTO } from './CopyRevisionsDTO'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class CopyRevisions implements UseCaseInterface<string> {
|
||||
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
|
||||
constructor(private revisionRepository: RevisionRepositoryInterface) {}
|
||||
|
||||
async execute(dto: CopyRevisionsDTO): Promise<Result<string>> {
|
||||
const orignalItemUuidOrError = Uuid.create(dto.originalItemUuid)
|
||||
|
@ -21,15 +21,7 @@ export class CopyRevisions implements UseCaseInterface<string> {
|
|||
}
|
||||
const newItemUuid = newItemUuidOrError.getValue()
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
return Result.fail(roleNamesOrError.getError())
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
const revisions = await revisionRepository.findByItemUuid(originalItemUuid)
|
||||
const revisions = await this.revisionRepository.findByItemUuid(originalItemUuid)
|
||||
|
||||
for (const existingRevision of revisions) {
|
||||
const revisionCopyOrError = Revision.create({
|
||||
|
@ -43,7 +35,7 @@ export class CopyRevisions implements UseCaseInterface<string> {
|
|||
|
||||
const revisionCopy = revisionCopyOrError.getValue()
|
||||
|
||||
await revisionRepository.insert(revisionCopy)
|
||||
await this.revisionRepository.insert(revisionCopy)
|
||||
}
|
||||
|
||||
return Result.ok<string>('Revisions copied')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export interface CopyRevisionsDTO {
|
||||
originalItemUuid: string
|
||||
newItemUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -3,16 +3,14 @@ import { Uuid, ContentType, Dates, Result } from '@standardnotes/domain-core'
|
|||
import { DumpRepositoryInterface } from '../../Dump/DumpRepositoryInterface'
|
||||
import { Revision } from '../../Revision/Revision'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { CreateRevisionFromDump } from './CreateRevisionFromDump'
|
||||
|
||||
describe('CreateRevisionFromDump', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revision: Revision
|
||||
let dumpRepository: DumpRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
|
||||
const createUseCase = () => new CreateRevisionFromDump(dumpRepository, revisionRepositoryResolver)
|
||||
const createUseCase = () => new CreateRevisionFromDump(dumpRepository, revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revision = Revision.create({
|
||||
|
@ -33,15 +31,11 @@ describe('CreateRevisionFromDump', () => {
|
|||
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.insert = jest.fn().mockReturnValue(true)
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
})
|
||||
|
||||
it('should create a revision from file dump', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
filePath: 'foobar',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
|
@ -52,7 +46,6 @@ describe('CreateRevisionFromDump', () => {
|
|||
it('should fail if file path is empty', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
filePath: '',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
@ -60,23 +53,11 @@ describe('CreateRevisionFromDump', () => {
|
|||
expect(dumpRepository.removeDump).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if role name is invalid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
filePath: 'foobar',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
expect(revisionRepository.insert).not.toHaveBeenCalled()
|
||||
expect(dumpRepository.removeDump).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail if revision cannot be found', async () => {
|
||||
dumpRepository.getRevisionFromDumpPath = jest.fn().mockReturnValue(Result.fail('Oops'))
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
filePath: 'foobar',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
@ -89,7 +70,6 @@ describe('CreateRevisionFromDump', () => {
|
|||
|
||||
const result = await createUseCase().execute({
|
||||
filePath: 'foobar',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Result, RoleNameCollection, UseCaseInterface, Validator } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Validator } from '@standardnotes/domain-core'
|
||||
import { DumpRepositoryInterface } from '../../Dump/DumpRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { CreateRevisionFromDumpDTO } from './CreateRevisionFromDumpDTO'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class CreateRevisionFromDump implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private dumpRepository: DumpRepositoryInterface,
|
||||
private revisionRepositoryResolver: RevisionRepositoryResolverInterface,
|
||||
private revisionRepository: RevisionRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: CreateRevisionFromDumpDTO): Promise<Result<void>> {
|
||||
|
@ -23,17 +23,7 @@ export class CreateRevisionFromDump implements UseCaseInterface<void> {
|
|||
}
|
||||
const revision = revisionOrError.getValue()
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
await this.dumpRepository.removeDump(dto.filePath)
|
||||
|
||||
return Result.fail(`Could not create revision from dump: ${roleNamesOrError.getError()}`)
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
const successfullyInserted = await revisionRepository.insert(revision)
|
||||
const successfullyInserted = await this.revisionRepository.insert(revision)
|
||||
if (!successfullyInserted) {
|
||||
await this.dumpRepository.removeDump(dto.filePath)
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export interface CreateRevisionFromDumpDTO {
|
||||
filePath: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,47 +1,30 @@
|
|||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { DeleteRevision } from './DeleteRevision'
|
||||
|
||||
describe('DeleteRevision', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
|
||||
const createUseCase = () => new DeleteRevision(revisionRepositoryResolver)
|
||||
const createUseCase = () => new DeleteRevision(revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.removeOneByUuid = jest.fn()
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
})
|
||||
|
||||
it('should delete revision', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeFalsy()
|
||||
expect(result.getValue()).toEqual('Revision removed')
|
||||
})
|
||||
|
||||
it('should do nothing if role names are not valid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not delete revision for an invalid item uuid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '1-2-3',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
@ -51,7 +34,6 @@ describe('DeleteRevision', () => {
|
|||
const result = await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { DeleteRevisionDTO } from './DeleteRevisionDTO'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class DeleteRevision implements UseCaseInterface<string> {
|
||||
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
|
||||
constructor(private revisionRepository: RevisionRepositoryInterface) {}
|
||||
|
||||
async execute(dto: DeleteRevisionDTO): Promise<Result<string>> {
|
||||
const revisionUuidOrError = Uuid.create(dto.revisionUuid)
|
||||
|
@ -19,15 +19,7 @@ export class DeleteRevision implements UseCaseInterface<string> {
|
|||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
return Result.fail(roleNamesOrError.getError())
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
await revisionRepository.removeOneByUuid(revisionUuid, userUuid)
|
||||
await this.revisionRepository.removeOneByUuid(revisionUuid, userUuid)
|
||||
|
||||
return Result.ok<string>('Revision removed')
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export interface DeleteRevisionDTO {
|
||||
userUuid: string
|
||||
revisionUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
import { Revision } from '../../Revision/Revision'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { GetRevision } from './GetRevision'
|
||||
|
||||
describe('GetRevision', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
|
||||
const createUseCase = () => new GetRevision(revisionRepositoryResolver)
|
||||
const createUseCase = () => new GetRevision(revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.findOneByUuid = jest.fn().mockReturnValue({} as jest.Mocked<Revision>)
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
})
|
||||
|
||||
it('should return revision for a given item', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
|
@ -33,31 +27,18 @@ describe('GetRevision', () => {
|
|||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['INVALID_SHARED_VAULT_UUID'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should do nothing if role names are not valid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not return revision for a given item if not found', async () => {
|
||||
revisionRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
|
@ -68,7 +49,6 @@ describe('GetRevision', () => {
|
|||
const result = await createUseCase().execute({
|
||||
revisionUuid: '1-2-3',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
|
@ -79,7 +59,6 @@ describe('GetRevision', () => {
|
|||
const result = await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
revisionUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { Revision } from '../../Revision/Revision'
|
||||
import { GetRevisionDTO } from './GetRevisionDTO'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class GetRevision implements UseCaseInterface<Revision> {
|
||||
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
|
||||
constructor(private revisionRepository: RevisionRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetRevisionDTO): Promise<Result<Revision>> {
|
||||
const revisionUuidOrError = Uuid.create(dto.revisionUuid)
|
||||
|
@ -20,12 +20,6 @@ export class GetRevision implements UseCaseInterface<Revision> {
|
|||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
return Result.fail(roleNamesOrError.getError())
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const sharedVaultUuids = []
|
||||
for (const sharedVaultUuid of dto.sharedVaultUuids) {
|
||||
const sharedVaultUuidOrError = Uuid.create(sharedVaultUuid)
|
||||
|
@ -35,9 +29,7 @@ export class GetRevision implements UseCaseInterface<Revision> {
|
|||
sharedVaultUuids.push(sharedVaultUuidOrError.getValue())
|
||||
}
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
const revision = await revisionRepository.findOneByUuid(revisionUuid, userUuid, sharedVaultUuids)
|
||||
const revision = await this.revisionRepository.findOneByUuid(revisionUuid, userUuid, sharedVaultUuids)
|
||||
|
||||
if (revision === null) {
|
||||
return Result.fail<Revision>(`Could not find revision with uuid: ${revisionUuid.value}`)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
export interface GetRevisionDTO {
|
||||
userUuid: string
|
||||
revisionUuid: string
|
||||
roleNames: string[]
|
||||
sharedVaultUuids: string[]
|
||||
}
|
||||
|
|
|
@ -1,27 +1,21 @@
|
|||
import { RevisionMetadata } from '../../Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { GetRevisionsMetada } from './GetRevisionsMetada'
|
||||
|
||||
describe('GetRevisionsMetada', () => {
|
||||
let revisionRepository: RevisionRepositoryInterface
|
||||
let revisionRepositoryResolver: RevisionRepositoryResolverInterface
|
||||
|
||||
const createUseCase = () => new GetRevisionsMetada(revisionRepositoryResolver)
|
||||
const createUseCase = () => new GetRevisionsMetada(revisionRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
revisionRepository = {} as jest.Mocked<RevisionRepositoryInterface>
|
||||
revisionRepository.findMetadataByItemId = jest.fn().mockReturnValue([{} as jest.Mocked<RevisionMetadata>])
|
||||
|
||||
revisionRepositoryResolver = {} as jest.Mocked<RevisionRepositoryResolverInterface>
|
||||
revisionRepositoryResolver.resolve = jest.fn().mockReturnValue(revisionRepository)
|
||||
})
|
||||
|
||||
it('should return revisions metadata for a given item', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['84c0f8e8-544a-4c7e-9adf-26209303bc1d'],
|
||||
})
|
||||
|
||||
|
@ -33,7 +27,6 @@ describe('GetRevisionsMetada', () => {
|
|||
const result = await createUseCase().execute({
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: ['1-2-3'],
|
||||
})
|
||||
|
||||
|
@ -44,7 +37,6 @@ describe('GetRevisionsMetada', () => {
|
|||
const result = await createUseCase().execute({
|
||||
itemUuid: '1-2-3',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
|
@ -55,18 +47,6 @@ describe('GetRevisionsMetada', () => {
|
|||
const result = await createUseCase().execute({
|
||||
userUuid: '1-2-3',
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['CORE_USER'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
expect(result.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should do nothing if role names are not valid', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
itemUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
userUuid: '84c0f8e8-544a-4c7e-9adf-26209303bc1d',
|
||||
roleNames: ['INVALID_ROLE_NAME'],
|
||||
sharedVaultUuids: [],
|
||||
})
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { Result, RoleNameCollection, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionMetadata } from '../../Revision/RevisionMetadata'
|
||||
|
||||
import { GetRevisionsMetadaDTO } from './GetRevisionsMetadaDTO'
|
||||
import { RevisionRepositoryResolverInterface } from '../../Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../../Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class GetRevisionsMetada implements UseCaseInterface<RevisionMetadata[]> {
|
||||
constructor(private revisionRepositoryResolver: RevisionRepositoryResolverInterface) {}
|
||||
constructor(private revisionRepository: RevisionRepositoryInterface) {}
|
||||
|
||||
async execute(dto: GetRevisionsMetadaDTO): Promise<Result<RevisionMetadata[]>> {
|
||||
const itemUuidOrError = Uuid.create(dto.itemUuid)
|
||||
|
@ -28,15 +28,7 @@ export class GetRevisionsMetada implements UseCaseInterface<RevisionMetadata[]>
|
|||
sharedVaultUuids.push(sharedVaultUuidOrError.getValue())
|
||||
}
|
||||
|
||||
const roleNamesOrError = RoleNameCollection.create(dto.roleNames)
|
||||
if (roleNamesOrError.isFailed()) {
|
||||
return Result.fail(roleNamesOrError.getError())
|
||||
}
|
||||
const roleNames = roleNamesOrError.getValue()
|
||||
|
||||
const revisionRepository = this.revisionRepositoryResolver.resolve(roleNames)
|
||||
|
||||
const revisionsMetdata = await revisionRepository.findMetadataByItemId(
|
||||
const revisionsMetdata = await this.revisionRepository.findMetadataByItemId(
|
||||
itemUuidOrError.getValue(),
|
||||
userUuidOrError.getValue(),
|
||||
sharedVaultUuids,
|
||||
|
|
|
@ -2,5 +2,4 @@ export interface GetRevisionsMetadaDTO {
|
|||
itemUuid: string
|
||||
sharedVaultUuids: string[]
|
||||
userUuid: string
|
||||
roleNames: string[]
|
||||
}
|
||||
|
|
|
@ -1,325 +0,0 @@
|
|||
/* istanbul ignore file */
|
||||
import { Result, TransitionStatus, UseCaseInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO } from './TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO'
|
||||
import { RevisionRepositoryInterface } from '../../../Revision/RevisionRepositoryInterface'
|
||||
import { TransitionRepositoryInterface } from '../../../Transition/TransitionRepositoryInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { DomainEventFactoryInterface } from '../../../Event/DomainEventFactoryInterface'
|
||||
|
||||
export class TransitionRevisionsFromPrimaryToSecondaryDatabaseForUser implements UseCaseInterface<void> {
|
||||
constructor(
|
||||
private primaryRevisionsRepository: RevisionRepositoryInterface,
|
||||
private secondRevisionsRepository: RevisionRepositoryInterface | null,
|
||||
private transitionStatusRepository: TransitionRepositoryInterface | null,
|
||||
private timer: TimerInterface,
|
||||
private logger: Logger,
|
||||
private pageSize: number,
|
||||
private domainEventPublisher: DomainEventPublisherInterface,
|
||||
private domainEventFactory: DomainEventFactoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO): Promise<Result<void>> {
|
||||
this.logger.info(`[TRANSITION][${dto.userUuid}] Transitioning revisions for user`)
|
||||
|
||||
if (this.secondRevisionsRepository === null) {
|
||||
return Result.fail('Secondary revision repository is not set')
|
||||
}
|
||||
|
||||
if (this.transitionStatusRepository === null) {
|
||||
return Result.fail('Transition status repository is not set')
|
||||
}
|
||||
|
||||
const userUuidOrError = Uuid.create(dto.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
return Result.fail(userUuidOrError.getError())
|
||||
}
|
||||
const userUuid = userUuidOrError.getValue()
|
||||
|
||||
if (await this.isAlreadyMigrated(userUuid)) {
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] User already migrated.`)
|
||||
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.Verified, dto.timestamp)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.InProgress, dto.timestamp)
|
||||
|
||||
const migrationTimeStart = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
this.logger.info(`[TRANSITION][${dto.userUuid}] Migrating revisions`)
|
||||
|
||||
const migrationResult = await this.migrateRevisionsForUser(userUuid, dto.timestamp)
|
||||
if (migrationResult.isFailed()) {
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.Failed, dto.timestamp)
|
||||
|
||||
return Result.fail(migrationResult.getError())
|
||||
}
|
||||
|
||||
this.logger.info(`[TRANSITION][${dto.userUuid}] Revisions migrated`)
|
||||
|
||||
await this.allowForPrimaryDatabaseToCatchUp()
|
||||
|
||||
this.logger.info(`[TRANSITION][${dto.userUuid}] Checking integrity between primary and secondary database`)
|
||||
|
||||
const integrityCheckResult = await this.checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid)
|
||||
if (integrityCheckResult.isFailed()) {
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(userUuid.value, 1)
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(userUuid.value, 1)
|
||||
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.Failed, dto.timestamp)
|
||||
|
||||
return Result.fail(integrityCheckResult.getError())
|
||||
}
|
||||
|
||||
const cleanupResult = await this.deleteRevisionsForUser(
|
||||
userUuid,
|
||||
this.secondRevisionsRepository as RevisionRepositoryInterface,
|
||||
)
|
||||
if (cleanupResult.isFailed()) {
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.Failed, dto.timestamp)
|
||||
|
||||
this.logger.error(
|
||||
`[TRANSITION][${dto.userUuid}] Failed to clean up secondary database revisions: ${cleanupResult.getError()}`,
|
||||
)
|
||||
}
|
||||
|
||||
const migrationTimeEnd = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
const migrationDuration = migrationTimeEnd - migrationTimeStart
|
||||
const migrationDurationTimeStructure = this.timer.convertMicrosecondsToTimeStructure(migrationDuration)
|
||||
|
||||
this.logger.info(
|
||||
`[TRANSITION][${dto.userUuid}] Transitioned revisions in ${migrationDurationTimeStructure.hours}h ${migrationDurationTimeStructure.minutes}m ${migrationDurationTimeStructure.seconds}s ${migrationDurationTimeStructure.milliseconds}ms`,
|
||||
)
|
||||
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.Verified, dto.timestamp)
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
private async migrateRevisionsForUser(userUuid: Uuid, timestamp: number): Promise<Result<void>> {
|
||||
try {
|
||||
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getPagingProgress(
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] Migrating from page ${initialPage}`)
|
||||
|
||||
const totalRevisionsCountForUser = await (
|
||||
this.secondRevisionsRepository as RevisionRepositoryInterface
|
||||
).countByUserUuid(userUuid)
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] Total revisions count for user: ${totalRevisionsCountForUser}`)
|
||||
const totalPages = Math.ceil(totalRevisionsCountForUser / this.pageSize)
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] Total pages: ${totalPages}`)
|
||||
let insertedCount = 0
|
||||
let newerCount = 0
|
||||
let identicalCount = 0
|
||||
let updatedCount = 0
|
||||
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
|
||||
const isPageInEvery10Percent = currentPage % Math.ceil(totalPages / 10) === 0
|
||||
if (isPageInEvery10Percent) {
|
||||
this.logger.info(
|
||||
`[TRANSITION][${userUuid.value}] Migrating revisions for user: ${Math.round(
|
||||
(currentPage / totalPages) * 100,
|
||||
)}% completed`,
|
||||
)
|
||||
this.logger.info(
|
||||
`[TRANSITION][${userUuid.value}] Inserted ${insertedCount} revisions so far. Skipped ${newerCount} revisions because they were newer in primary database. Skipped ${identicalCount} revisions because they were identical in primary and secondary database. Updated ${updatedCount} revisions because they were older in primary database.`,
|
||||
)
|
||||
await this.updateTransitionStatus(userUuid, TransitionStatus.STATUSES.InProgress, timestamp)
|
||||
}
|
||||
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setPagingProgress(
|
||||
userUuid.value,
|
||||
currentPage,
|
||||
)
|
||||
|
||||
const query = {
|
||||
userUuid: userUuid,
|
||||
offset: (currentPage - 1) * this.pageSize,
|
||||
limit: this.pageSize,
|
||||
}
|
||||
|
||||
const revisions = await (this.secondRevisionsRepository as RevisionRepositoryInterface).findByUserUuid(query)
|
||||
for (const revision of revisions) {
|
||||
try {
|
||||
const revisionInPrimary = await this.primaryRevisionsRepository.findOneByUuid(
|
||||
Uuid.create(revision.id.toString()).getValue(),
|
||||
revision.props.userUuid as Uuid,
|
||||
[],
|
||||
)
|
||||
|
||||
if (!revisionInPrimary) {
|
||||
await this.primaryRevisionsRepository.insert(revision)
|
||||
|
||||
insertedCount++
|
||||
} else {
|
||||
if (revisionInPrimary.props.dates.updatedAt > revision.props.dates.updatedAt) {
|
||||
this.logger.info(
|
||||
`[TRANSITION][${
|
||||
userUuid.value
|
||||
}] Revision ${revision.id.toString()} is older in secondary than revision in primary database`,
|
||||
)
|
||||
newerCount++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (revisionInPrimary.isIdenticalTo(revision)) {
|
||||
identicalCount++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
await this.primaryRevisionsRepository.update(revision)
|
||||
|
||||
updatedCount++
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`[TRANSITION][${
|
||||
userUuid.value
|
||||
}] Errored when saving revision ${revision.id.toString()} to primary database: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
`[TRANSITION][${userUuid.value}] Inserted ${insertedCount} revisions. Skipped ${newerCount} revisions because they were newer in primary database. Skipped ${identicalCount} revisions because they were identical in primary and secondary database. Updated ${updatedCount} revisions because they were older in primary database.`,
|
||||
)
|
||||
|
||||
return Result.ok()
|
||||
} catch (error) {
|
||||
return Result.fail(`Errored when migrating revisions for user ${userUuid.value}: ${(error as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteRevisionsForUser(
|
||||
userUuid: Uuid,
|
||||
revisionRepository: RevisionRepositoryInterface,
|
||||
): Promise<Result<void>> {
|
||||
try {
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] Deleting all revisions from secondary database`)
|
||||
|
||||
await revisionRepository.removeByUserUuid(userUuid)
|
||||
|
||||
return Result.ok()
|
||||
} catch (error) {
|
||||
return Result.fail(`Errored when deleting revisions for user ${userUuid.value}: ${(error as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
private async allowForPrimaryDatabaseToCatchUp(): Promise<void> {
|
||||
const delay = 1_000
|
||||
await this.timer.sleep(delay)
|
||||
}
|
||||
|
||||
private async checkIntegrityBetweenPrimaryAndSecondaryDatabase(userUuid: Uuid): Promise<Result<boolean>> {
|
||||
try {
|
||||
const initialPage = await (this.transitionStatusRepository as TransitionRepositoryInterface).getIntegrityProgress(
|
||||
userUuid.value,
|
||||
)
|
||||
|
||||
this.logger.info(`[TRANSITION][${userUuid.value}] Checking integrity from page ${initialPage}`)
|
||||
|
||||
const totalRevisionsCountForUserInSecondary = await (
|
||||
this.secondRevisionsRepository as RevisionRepositoryInterface
|
||||
).countByUserUuid(userUuid)
|
||||
const totalRevisionsCountForUserInPrimary = await this.primaryRevisionsRepository.countByUserUuid(userUuid)
|
||||
|
||||
if (totalRevisionsCountForUserInPrimary < totalRevisionsCountForUserInSecondary) {
|
||||
return Result.fail(
|
||||
`Total revisions count for user ${userUuid.value} in primary database (${totalRevisionsCountForUserInPrimary}) does not match total revisions count in secondary database (${totalRevisionsCountForUserInSecondary})`,
|
||||
)
|
||||
}
|
||||
|
||||
const totalPages = Math.ceil(totalRevisionsCountForUserInPrimary / this.pageSize)
|
||||
for (let currentPage = initialPage; currentPage <= totalPages; currentPage++) {
|
||||
await (this.transitionStatusRepository as TransitionRepositoryInterface).setIntegrityProgress(
|
||||
userUuid.value,
|
||||
currentPage,
|
||||
)
|
||||
|
||||
const query = {
|
||||
userUuid: userUuid,
|
||||
offset: (currentPage - 1) * this.pageSize,
|
||||
limit: this.pageSize,
|
||||
}
|
||||
|
||||
const revisions = await (this.secondRevisionsRepository as RevisionRepositoryInterface).findByUserUuid(query)
|
||||
|
||||
for (const revision of revisions) {
|
||||
const revisionUuidOrError = Uuid.create(revision.id.toString())
|
||||
/* istanbul ignore if */
|
||||
if (revisionUuidOrError.isFailed()) {
|
||||
return Result.fail(revisionUuidOrError.getError())
|
||||
}
|
||||
const revisionUuid = revisionUuidOrError.getValue()
|
||||
|
||||
const revisionInPrimary = await this.primaryRevisionsRepository.findOneByUuid(revisionUuid, userUuid, [])
|
||||
if (!revisionInPrimary) {
|
||||
return Result.fail(`Revision ${revision.id.toString()} not found in primary database`)
|
||||
}
|
||||
|
||||
if (revisionInPrimary.props.dates.updatedAt > revision.props.dates.updatedAt) {
|
||||
this.logger.info(
|
||||
`[TRANSITION][${
|
||||
userUuid.value
|
||||
}] Integrity check of revision ${revision.id.toString()} - is older in secondary than revision in primary database`,
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (revision.isIdenticalTo(revisionInPrimary)) {
|
||||
continue
|
||||
}
|
||||
|
||||
return Result.fail(
|
||||
`Revision ${revision.id.toString()} is not identical in primary and secondary database. Revision in primary database: ${JSON.stringify(
|
||||
revisionInPrimary,
|
||||
)}, revision in secondary database: ${JSON.stringify(revision)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
} catch (error) {
|
||||
return Result.fail(
|
||||
`Errored when checking integrity between primary and secondary database: ${(error as Error).message}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private async updateTransitionStatus(userUuid: Uuid, status: string, timestamp: number): Promise<void> {
|
||||
await this.domainEventPublisher.publish(
|
||||
this.domainEventFactory.createTransitionStatusUpdatedEvent({
|
||||
userUuid: userUuid.value,
|
||||
status,
|
||||
transitionType: 'revisions',
|
||||
transitionTimestamp: timestamp,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
private async isAlreadyMigrated(userUuid: Uuid): Promise<boolean> {
|
||||
const totalRevisionsCountForUserInSecondary = await (
|
||||
this.secondRevisionsRepository as RevisionRepositoryInterface
|
||||
).countByUserUuid(userUuid)
|
||||
|
||||
if (totalRevisionsCountForUserInSecondary > 0) {
|
||||
this.logger.info(
|
||||
`[TRANSITION][${userUuid.value}] User has ${totalRevisionsCountForUserInSecondary} revisions in secondary database.`,
|
||||
)
|
||||
}
|
||||
|
||||
return totalRevisionsCountForUserInSecondary === 0
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export interface TransitionRevisionsFromPrimaryToSecondaryDatabaseForUserDTO {
|
||||
userUuid: string
|
||||
timestamp: number
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import { HttpStatusCode } from '@standardnotes/responses'
|
||||
import { Role } from '@standardnotes/security'
|
||||
import { BaseHttpController, results } from 'inversify-express-utils'
|
||||
import { Request, Response } from 'express'
|
||||
import { ControllerContainerInterface, MapperInterface } from '@standardnotes/domain-core'
|
||||
|
@ -34,7 +33,6 @@ export class BaseRevisionsController extends BaseHttpController {
|
|||
const revisionMetadataOrError = await this.getRevisionsMetadata.execute({
|
||||
itemUuid: request.params.itemUuid,
|
||||
userUuid: response.locals.user.uuid,
|
||||
roleNames: response.locals.roles.map((role: Role) => role.name),
|
||||
sharedVaultUuids: response.locals.belongsToSharedVaults.map(
|
||||
(association: { shared_vault_uuid: string; permission: string }) => association.shared_vault_uuid,
|
||||
),
|
||||
|
@ -61,7 +59,6 @@ export class BaseRevisionsController extends BaseHttpController {
|
|||
const revisionOrError = await this.doGetRevision.execute({
|
||||
revisionUuid: request.params.uuid,
|
||||
userUuid: response.locals.user.uuid,
|
||||
roleNames: response.locals.roles.map((role: Role) => role.name),
|
||||
sharedVaultUuids: response.locals.belongsToSharedVaults.map(
|
||||
(association: { shared_vault_uuid: string; permission: string }) => association.shared_vault_uuid,
|
||||
),
|
||||
|
@ -87,7 +84,6 @@ export class BaseRevisionsController extends BaseHttpController {
|
|||
const revisionOrError = await this.doDeleteRevision.execute({
|
||||
revisionUuid: request.params.uuid,
|
||||
userUuid: response.locals.user.uuid,
|
||||
roleNames: response.locals.roles.map((role: Role) => role.name),
|
||||
})
|
||||
|
||||
if (revisionOrError.isFailed()) {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import * as IORedis from 'ioredis'
|
||||
|
||||
import { TransitionRepositoryInterface } from '../../Domain/Transition/TransitionRepositoryInterface'
|
||||
|
||||
export class RedisTransitionRepository implements TransitionRepositoryInterface {
|
||||
private readonly PREFIX = 'transition-revisions-migration-progress'
|
||||
private readonly INTEGRITY_PREFIX = 'transition-revisions-integrity-progress'
|
||||
|
||||
constructor(private redisClient: IORedis.Redis) {}
|
||||
|
||||
async getIntegrityProgress(userUuid: string): Promise<number> {
|
||||
const progress = await this.redisClient.get(`${this.INTEGRITY_PREFIX}:${userUuid}`)
|
||||
|
||||
if (progress === null) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return parseInt(progress)
|
||||
}
|
||||
|
||||
async setIntegrityProgress(userUuid: string, progress: number): Promise<void> {
|
||||
await this.redisClient.setex(`${this.INTEGRITY_PREFIX}:${userUuid}`, 172_800, progress.toString())
|
||||
}
|
||||
|
||||
async getPagingProgress(userUuid: string): Promise<number> {
|
||||
const progress = await this.redisClient.get(`${this.PREFIX}:${userUuid}`)
|
||||
|
||||
if (progress === null) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return parseInt(progress)
|
||||
}
|
||||
|
||||
async setPagingProgress(userUuid: string, progress: number): Promise<void> {
|
||||
await this.redisClient.setex(`${this.PREFIX}:${userUuid}`, 172_800, progress.toString())
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
import { BSON } from 'mongodb'
|
||||
import { Column, Entity, Index, ObjectIdColumn } from 'typeorm'
|
||||
|
||||
@Entity({ name: 'revisions' })
|
||||
export class MongoDBRevision {
|
||||
@ObjectIdColumn()
|
||||
declare _id: BSON.UUID
|
||||
|
||||
@Column()
|
||||
@Index('item_uuid_on_revisions')
|
||||
declare itemUuid: string
|
||||
|
||||
@Column()
|
||||
@Index('user_uuid_on_revisions')
|
||||
declare userUuid: string | null
|
||||
|
||||
@Column()
|
||||
declare content: string | null
|
||||
|
||||
@Column()
|
||||
declare contentType: string | null
|
||||
|
||||
@Column()
|
||||
declare itemsKeyId: string | null
|
||||
|
||||
@Column()
|
||||
declare encItemKey: string | null
|
||||
|
||||
@Column()
|
||||
declare authHash: string | null
|
||||
|
||||
@Column()
|
||||
declare creationDate: Date
|
||||
|
||||
@Column()
|
||||
declare createdAt: Date
|
||||
|
||||
@Column()
|
||||
declare updatedAt: Date
|
||||
|
||||
@Column()
|
||||
declare editedBy: string | null
|
||||
|
||||
@Column()
|
||||
@Index('index_revisions_on_shared_vault_uuid')
|
||||
declare sharedVaultUuid: string | null
|
||||
|
||||
@Column()
|
||||
declare keySystemIdentifier: string | null
|
||||
}
|
|
@ -1,209 +0,0 @@
|
|||
import { MapperInterface, Uuid } from '@standardnotes/domain-core'
|
||||
import { MongoRepository, ObjectLiteral } from 'typeorm'
|
||||
import { BSON } from 'mongodb'
|
||||
import { Logger } from 'winston'
|
||||
|
||||
import { MongoDBRevision } from './MongoDBRevision'
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { RevisionRepositoryInterface } from '../../../Domain/Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class MongoDBRevisionRepository implements RevisionRepositoryInterface {
|
||||
constructor(
|
||||
private mongoRepository: MongoRepository<MongoDBRevision>,
|
||||
private revisionMetadataMapper: MapperInterface<RevisionMetadata, MongoDBRevision>,
|
||||
private revisionMapper: MapperInterface<Revision, MongoDBRevision>,
|
||||
private logger: Logger,
|
||||
) {}
|
||||
|
||||
async clearSharedVaultAndKeySystemAssociations(dto: { itemUuid?: Uuid; sharedVaultUuid: Uuid }): Promise<void> {
|
||||
let query: ObjectLiteral
|
||||
if (dto.itemUuid !== undefined) {
|
||||
query = {
|
||||
itemUuid: { $eq: dto.itemUuid.value },
|
||||
sharedVaultUuid: { $eq: dto.sharedVaultUuid.value },
|
||||
}
|
||||
} else {
|
||||
query = {
|
||||
sharedVaultUuid: { $eq: dto.sharedVaultUuid.value },
|
||||
}
|
||||
}
|
||||
|
||||
await this.mongoRepository.updateMany(query, {
|
||||
$set: {
|
||||
sharedVaultUuid: null,
|
||||
keySystemIdentifier: null,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async countByUserUuid(userUuid: Uuid): Promise<number> {
|
||||
return this.mongoRepository.count({ userUuid: { $eq: userUuid.value } })
|
||||
}
|
||||
|
||||
async findByUserUuid(dto: { userUuid: Uuid; offset?: number; limit?: number }): Promise<Revision[]> {
|
||||
const mongoRevisions = await this.mongoRepository.find({
|
||||
where: { userUuid: { $eq: dto.userUuid.value } },
|
||||
order: {
|
||||
createdAt: 'ASC',
|
||||
_id: 'ASC',
|
||||
},
|
||||
skip: dto.offset,
|
||||
take: dto.limit,
|
||||
})
|
||||
|
||||
const revisions = []
|
||||
for (const mongoRevision of mongoRevisions) {
|
||||
revisions.push(this.revisionMapper.toDomain(mongoRevision))
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async removeByUserUuid(userUuid: Uuid): Promise<void> {
|
||||
await this.mongoRepository.deleteMany({ userUuid: userUuid.value })
|
||||
}
|
||||
|
||||
async removeOneByUuid(revisionUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.mongoRepository.deleteOne({
|
||||
_id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) },
|
||||
userUuid: { $eq: userUuid.value },
|
||||
})
|
||||
}
|
||||
|
||||
async findOneByUuid(revisionUuid: Uuid, userUuid: Uuid, sharedVaultUuids: Uuid[]): Promise<Revision | null> {
|
||||
let persistence = null
|
||||
if (sharedVaultUuids.length > 0) {
|
||||
persistence = await this.mongoRepository.findOne({
|
||||
where: {
|
||||
$and: [
|
||||
{ _id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) } },
|
||||
{
|
||||
$or: [
|
||||
{ sharedVaultUuid: { $in: sharedVaultUuids.map((uuid) => uuid.value) } },
|
||||
{ userUuid: { $eq: userUuid.value } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
} else {
|
||||
persistence = await this.mongoRepository.findOne({
|
||||
where: {
|
||||
$and: [
|
||||
{ _id: { $eq: BSON.UUID.createFromHexString(revisionUuid.value) } },
|
||||
{ userUuid: { $eq: userUuid.value } },
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (persistence === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.revisionMapper.toDomain(persistence)
|
||||
}
|
||||
|
||||
async findByItemUuid(itemUuid: Uuid): Promise<Revision[]> {
|
||||
const persistence = await this.mongoRepository.find({
|
||||
where: {
|
||||
itemUuid: { $eq: itemUuid.value },
|
||||
},
|
||||
})
|
||||
|
||||
const revisions: Revision[] = []
|
||||
|
||||
for (const revision of persistence) {
|
||||
try {
|
||||
revisions.push(this.revisionMapper.toDomain(revision))
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to map revision ${revision._id.toHexString()} to domain: ${(error as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async findMetadataByItemId(
|
||||
itemUuid: Uuid,
|
||||
userUuid: Uuid,
|
||||
sharedVaultUuids: Uuid[],
|
||||
): Promise<Array<RevisionMetadata>> {
|
||||
let persistence = []
|
||||
if (sharedVaultUuids.length > 0) {
|
||||
persistence = await this.mongoRepository.find({
|
||||
select: ['_id', 'contentType', 'createdAt', 'updatedAt', 'sharedVaultUuid', 'itemUuid'],
|
||||
where: {
|
||||
$and: [
|
||||
{ itemUuid: { $eq: itemUuid.value } },
|
||||
{
|
||||
$or: [
|
||||
{ sharedVaultUuid: { $in: sharedVaultUuids.map((uuid) => uuid.value) } },
|
||||
{ userUuid: { $eq: userUuid.value } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
_id: 'DESC',
|
||||
},
|
||||
})
|
||||
} else {
|
||||
persistence = await this.mongoRepository.find({
|
||||
select: ['_id', 'contentType', 'createdAt', 'updatedAt', 'sharedVaultUuid', 'itemUuid'],
|
||||
where: {
|
||||
$and: [{ itemUuid: { $eq: itemUuid.value } }, { userUuid: { $eq: userUuid.value } }],
|
||||
},
|
||||
order: {
|
||||
createdAt: 'DESC',
|
||||
_id: 'DESC',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const revisions: RevisionMetadata[] = []
|
||||
|
||||
for (const revision of persistence) {
|
||||
try {
|
||||
revisions.push(this.revisionMetadataMapper.toDomain(revision))
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to map revision ${revision._id.toHexString()} to domain: ${(error as Error).message}`)
|
||||
}
|
||||
}
|
||||
|
||||
return revisions
|
||||
}
|
||||
|
||||
async updateUserUuid(itemUuid: Uuid, userUuid: Uuid): Promise<void> {
|
||||
await this.mongoRepository.updateMany(
|
||||
{
|
||||
itemUuid: { $eq: itemUuid.value },
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
userUuid: userUuid.value,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async insert(revision: Revision): Promise<boolean> {
|
||||
const persistence = this.revisionMapper.toProjection(revision)
|
||||
|
||||
const insertResult = await this.mongoRepository.insertOne(persistence)
|
||||
|
||||
return insertResult.acknowledged
|
||||
}
|
||||
|
||||
async update(revision: Revision): Promise<boolean> {
|
||||
const persistence = this.revisionMapper.toProjection(revision)
|
||||
|
||||
const { _id, ...rest } = persistence
|
||||
|
||||
const updateResult = await this.mongoRepository.updateOne({ _id: _id }, { $set: rest })
|
||||
|
||||
return updateResult.acknowledged
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
import { RoleName, RoleNameCollection } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionRepositoryResolverInterface } from '../../Domain/Revision/RevisionRepositoryResolverInterface'
|
||||
import { RevisionRepositoryInterface } from '../../Domain/Revision/RevisionRepositoryInterface'
|
||||
|
||||
export class TypeORMRevisionRepositoryResolver implements RevisionRepositoryResolverInterface {
|
||||
constructor(
|
||||
private sqlRevisionRepository: RevisionRepositoryInterface,
|
||||
private mongoDbRevisionRepository: RevisionRepositoryInterface | null,
|
||||
) {}
|
||||
|
||||
resolve(roleNames: RoleNameCollection): RevisionRepositoryInterface {
|
||||
if (!this.mongoDbRevisionRepository) {
|
||||
return this.sqlRevisionRepository
|
||||
}
|
||||
|
||||
const transitionRoleName = RoleName.create(RoleName.NAMES.TransitionUser).getValue()
|
||||
|
||||
if (roleNames.includes(transitionRoleName)) {
|
||||
return this.mongoDbRevisionRepository
|
||||
}
|
||||
|
||||
return this.sqlRevisionRepository
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import { MapperInterface, Dates, UniqueEntityId, ContentType, Uuid } from '@standardnotes/domain-core'
|
||||
|
||||
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
|
||||
import { MongoDBRevision } from '../../../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
|
||||
export class MongoDBRevisionMetadataPersistenceMapper implements MapperInterface<RevisionMetadata, MongoDBRevision> {
|
||||
toDomain(projection: MongoDBRevision): RevisionMetadata {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not create content type: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const createdAt = projection.createdAt instanceof Date ? projection.createdAt : new Date(projection.createdAt)
|
||||
const updatedAt = projection.updatedAt instanceof Date ? projection.updatedAt : new Date(projection.updatedAt)
|
||||
|
||||
const datesOrError = Dates.create(createdAt, updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not create dates: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
let sharedVaultUuid = null
|
||||
if (projection.sharedVaultUuid) {
|
||||
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not create shared vault uuid: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
}
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not create item uuid: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
const revisionMetadataOrError = RevisionMetadata.create(
|
||||
{
|
||||
contentType,
|
||||
dates,
|
||||
sharedVaultUuid,
|
||||
itemUuid,
|
||||
},
|
||||
new UniqueEntityId(projection._id.toHexString()),
|
||||
)
|
||||
|
||||
if (revisionMetadataOrError.isFailed()) {
|
||||
throw new Error(`Could not create revision metdata: ${revisionMetadataOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionMetadataOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(_domain: RevisionMetadata): MongoDBRevision {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
import { MapperInterface, Dates, UniqueEntityId, Uuid, ContentType } from '@standardnotes/domain-core'
|
||||
import { BSON } from 'mongodb'
|
||||
|
||||
import { MongoDBRevision } from '../../../Infra/TypeORM/MongoDB/MongoDBRevision'
|
||||
import { Revision } from '../../../Domain/Revision/Revision'
|
||||
import { SharedVaultAssociation } from '../../../Domain/SharedVault/SharedVaultAssociation'
|
||||
import { KeySystemAssociation } from '../../../Domain/KeySystem/KeySystemAssociation'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
export class MongoDBRevisionPersistenceMapper implements MapperInterface<Revision, MongoDBRevision> {
|
||||
constructor(private timer: TimerInterface) {}
|
||||
|
||||
toDomain(projection: MongoDBRevision): Revision {
|
||||
const contentTypeOrError = ContentType.create(projection.contentType)
|
||||
if (contentTypeOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${contentTypeOrError.getError()}`)
|
||||
}
|
||||
const contentType = contentTypeOrError.getValue()
|
||||
|
||||
const datesOrError = Dates.create(projection.createdAt, projection.updatedAt)
|
||||
if (datesOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${datesOrError.getError()}`)
|
||||
}
|
||||
const dates = datesOrError.getValue()
|
||||
|
||||
const itemUuidOrError = Uuid.create(projection.itemUuid)
|
||||
if (itemUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${itemUuidOrError.getError()}`)
|
||||
}
|
||||
const itemUuid = itemUuidOrError.getValue()
|
||||
|
||||
let userUuid = null
|
||||
if (projection.userUuid !== null) {
|
||||
const userUuidOrError = Uuid.create(projection.userUuid)
|
||||
if (userUuidOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${userUuidOrError.getError()}`)
|
||||
}
|
||||
userUuid = userUuidOrError.getValue()
|
||||
}
|
||||
|
||||
let sharedVaultAssociation: SharedVaultAssociation | undefined = undefined
|
||||
if (projection.sharedVaultUuid && projection.editedBy) {
|
||||
const sharedVaultUuidOrError = Uuid.create(projection.sharedVaultUuid)
|
||||
if (sharedVaultUuidOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultUuidOrError.getError()}`)
|
||||
}
|
||||
const sharedVaultUuid = sharedVaultUuidOrError.getValue()
|
||||
|
||||
const lastEditedByOrError = Uuid.create(projection.editedBy)
|
||||
if (lastEditedByOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${lastEditedByOrError.getError()}`)
|
||||
}
|
||||
const lastEditedBy = lastEditedByOrError.getValue()
|
||||
|
||||
const sharedVaultAssociationOrError = SharedVaultAssociation.create({
|
||||
sharedVaultUuid,
|
||||
editedBy: lastEditedBy,
|
||||
})
|
||||
if (sharedVaultAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${sharedVaultAssociationOrError.getError()}`)
|
||||
}
|
||||
sharedVaultAssociation = sharedVaultAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
let keySystemAssociation: KeySystemAssociation | undefined = undefined
|
||||
if (projection.keySystemIdentifier) {
|
||||
const keySystemAssociationOrError = KeySystemAssociation.create(projection.keySystemIdentifier)
|
||||
if (keySystemAssociationOrError.isFailed()) {
|
||||
throw new Error(`Failed to create revision from projection: ${keySystemAssociationOrError.getError()}`)
|
||||
}
|
||||
keySystemAssociation = keySystemAssociationOrError.getValue()
|
||||
}
|
||||
|
||||
const revisionOrError = Revision.create(
|
||||
{
|
||||
authHash: projection.authHash,
|
||||
content: projection.content,
|
||||
contentType,
|
||||
creationDate: new Date(this.timer.convertDateToFormattedString(projection.creationDate, 'YYYY-MM-DD')),
|
||||
encItemKey: projection.encItemKey,
|
||||
itemsKeyId: projection.itemsKeyId,
|
||||
itemUuid,
|
||||
userUuid,
|
||||
dates,
|
||||
sharedVaultAssociation,
|
||||
keySystemAssociation,
|
||||
},
|
||||
new UniqueEntityId(projection._id.toHexString()),
|
||||
)
|
||||
if (revisionOrError.isFailed()) {
|
||||
throw new Error(`Could not map typeorm revision to domain revision: ${revisionOrError.getError()}`)
|
||||
}
|
||||
|
||||
return revisionOrError.getValue()
|
||||
}
|
||||
|
||||
toProjection(domain: Revision): MongoDBRevision {
|
||||
const mongoDBRevision = new MongoDBRevision()
|
||||
|
||||
mongoDBRevision.authHash = domain.props.authHash
|
||||
mongoDBRevision.content = domain.props.content
|
||||
mongoDBRevision.contentType = domain.props.contentType.value
|
||||
mongoDBRevision.createdAt = domain.props.dates.createdAt
|
||||
mongoDBRevision.updatedAt = domain.props.dates.updatedAt
|
||||
mongoDBRevision.creationDate = new Date(
|
||||
this.timer.convertDateToFormattedString(domain.props.creationDate, 'YYYY-MM-DD'),
|
||||
)
|
||||
mongoDBRevision.encItemKey = domain.props.encItemKey
|
||||
mongoDBRevision.itemUuid = domain.props.itemUuid.value
|
||||
mongoDBRevision.itemsKeyId = domain.props.itemsKeyId
|
||||
mongoDBRevision.userUuid = domain.props.userUuid ? domain.props.userUuid.value : null
|
||||
mongoDBRevision.sharedVaultUuid = domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.sharedVaultUuid.value
|
||||
: null
|
||||
mongoDBRevision.editedBy = domain.props.sharedVaultAssociation
|
||||
? domain.props.sharedVaultAssociation.props.editedBy.value
|
||||
: null
|
||||
mongoDBRevision.keySystemIdentifier = domain.props.keySystemAssociation
|
||||
? domain.props.keySystemAssociation.props.keySystemIdentifier
|
||||
: null
|
||||
mongoDBRevision._id = BSON.UUID.createFromHexString(domain.id.toString())
|
||||
|
||||
return mongoDBRevision
|
||||
}
|
||||
}
|
|
@ -24,6 +24,4 @@ export type CrossServiceTokenData = {
|
|||
refresh_expiration: string
|
||||
}
|
||||
extensionKey?: string
|
||||
ongoing_transition?: boolean
|
||||
ongoing_revisions_transition?: boolean
|
||||
}
|
||||
|
|
|
@ -11,5 +11,4 @@ export type ValetTokenData = {
|
|||
}>
|
||||
uploadBytesUsed: number
|
||||
uploadBytesLimit: number
|
||||
ongoingTransition?: boolean
|
||||
}
|
||||
|
|
|
@ -17,9 +17,6 @@ DB_DEBUG_LEVEL=all # "all" | "query" | "schema" | "error" | "warn" | "info" | "l
|
|||
DB_MIGRATIONS_PATH=dist/migrations/*.js
|
||||
DB_TYPE=mysql
|
||||
|
||||
REDIS_URL=redis://cache
|
||||
CACHE_TYPE=redis
|
||||
|
||||
VALET_TOKEN_SECRET=change-me-!
|
||||
VALET_TOKEN_TTL=1000
|
||||
|
||||
|
@ -43,11 +40,3 @@ FILE_UPLOAD_PATH=
|
|||
|
||||
VALET_TOKEN_SECRET=change-me-!
|
||||
VALET_TOKEN_TTL=7200
|
||||
|
||||
# (Optional) Mongo Setup
|
||||
SECONDARY_DB_ENABLED=false
|
||||
MONGO_HOST=
|
||||
MONGO_PORT=
|
||||
MONGO_USERNAME=
|
||||
MONGO_PASSWORD=
|
||||
MONGO_DATABASE=
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
"inversify-express-utils": "^6.4.3",
|
||||
"ioredis": "^5.3.2",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"mongodb": "^6.0.0",
|
||||
"mysql2": "^3.0.1",
|
||||
"prettyjson": "^1.2.5",
|
||||
"reflect-metadata": "0.1.13",
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import * as winston from 'winston'
|
||||
import Redis from 'ioredis'
|
||||
import { Container, interfaces } from 'inversify'
|
||||
|
||||
import { Env } from './Env'
|
||||
|
@ -8,7 +7,7 @@ import { AppDataSource } from './DataSource'
|
|||
import { SNSClient, SNSClientConfig } from '@aws-sdk/client-sns'
|
||||
import { ItemRepositoryInterface } from '../Domain/Item/ItemRepositoryInterface'
|
||||
import { SQLLegacyItemRepository } from '../Infra/TypeORM/SQLLegacyItemRepository'
|
||||
import { MongoRepository, Repository } from 'typeorm'
|
||||
import { Repository } from 'typeorm'
|
||||
import { Item } from '../Domain/Item/Item'
|
||||
import {
|
||||
DirectCallDomainEventPublisher,
|
||||
|
@ -151,27 +150,18 @@ import { UpdateStorageQuotaUsedInSharedVault } from '../Domain/UseCase/SharedVau
|
|||
import { SharedVaultFileUploadedEventHandler } from '../Domain/Handler/SharedVaultFileUploadedEventHandler'
|
||||
import { SharedVaultFileRemovedEventHandler } from '../Domain/Handler/SharedVaultFileRemovedEventHandler'
|
||||
import { AddNotificationsForUsers } from '../Domain/UseCase/Messaging/AddNotificationsForUsers/AddNotificationsForUsers'
|
||||
import { MongoDBItem } from '../Infra/TypeORM/MongoDBItem'
|
||||
import { MongoDBItemRepository } from '../Infra/TypeORM/MongoDBItemRepository'
|
||||
import { MongoDBItemPersistenceMapper } from '../Mapping/Persistence/MongoDB/MongoDBItemPersistenceMapper'
|
||||
import { Logger } from 'winston'
|
||||
import { ItemRepositoryResolverInterface } from '../Domain/Item/ItemRepositoryResolverInterface'
|
||||
import { TypeORMItemRepositoryResolver } from '../Infra/TypeORM/TypeORMItemRepositoryResolver'
|
||||
import { TransitionItemsFromPrimaryToSecondaryDatabaseForUser } from '../Domain/UseCase/Transition/TransitionItemsFromPrimaryToSecondaryDatabaseForUser/TransitionItemsFromPrimaryToSecondaryDatabaseForUser'
|
||||
import { SharedVaultFileMovedEventHandler } from '../Domain/Handler/SharedVaultFileMovedEventHandler'
|
||||
import { SQLItem } from '../Infra/TypeORM/SQLItem'
|
||||
import { SQLItemPersistenceMapper } from '../Mapping/Persistence/SQLItemPersistenceMapper'
|
||||
import { SQLItemRepository } from '../Infra/TypeORM/SQLItemRepository'
|
||||
import { SendEventToClient } from '../Domain/UseCase/Syncing/SendEventToClient/SendEventToClient'
|
||||
import { TransitionRequestedEventHandler } from '../Domain/Handler/TransitionRequestedEventHandler'
|
||||
import { DeleteSharedVaults } from '../Domain/UseCase/SharedVaults/DeleteSharedVaults/DeleteSharedVaults'
|
||||
import { RemoveItemsFromSharedVault } from '../Domain/UseCase/SharedVaults/RemoveItemsFromSharedVault/RemoveItemsFromSharedVault'
|
||||
import { SharedVaultRemovedEventHandler } from '../Domain/Handler/SharedVaultRemovedEventHandler'
|
||||
import { DesignateSurvivor } from '../Domain/UseCase/SharedVaults/DesignateSurvivor/DesignateSurvivor'
|
||||
import { RemoveUserFromSharedVaults } from '../Domain/UseCase/SharedVaults/RemoveUserFromSharedVaults/RemoveUserFromSharedVaults'
|
||||
import { TransferSharedVault } from '../Domain/UseCase/SharedVaults/TransferSharedVault/TransferSharedVault'
|
||||
import { TransitionRepositoryInterface } from '../Domain/Transition/TransitionRepositoryInterface'
|
||||
import { RedisTransitionRepository } from '../Infra/Redis/RedisTransitionRepository'
|
||||
import { TransferSharedVaultItems } from '../Domain/UseCase/SharedVaults/TransferSharedVaultItems/TransferSharedVaultItems'
|
||||
import { DumpItem } from '../Domain/UseCase/Syncing/DumpItem/DumpItem'
|
||||
|
||||
|
@ -223,29 +213,11 @@ export class ContainerConfigLoader {
|
|||
const isConfiguredForHomeServer = env.get('MODE', true) === 'home-server'
|
||||
const isConfiguredForSelfHosting = env.get('MODE', true) === 'self-hosted'
|
||||
const isConfiguredForHomeServerOrSelfHosting = isConfiguredForHomeServer || isConfiguredForSelfHosting
|
||||
const isSecondaryDatabaseEnabled = env.get('SECONDARY_DB_ENABLED', true) === 'true'
|
||||
const isConfiguredForInMemoryCache = env.get('CACHE_TYPE', true) === 'memory'
|
||||
|
||||
container
|
||||
.bind<boolean>(TYPES.Sync_IS_CONFIGURED_FOR_HOME_SERVER_OR_SELF_HOSTING)
|
||||
.toConstantValue(isConfiguredForHomeServerOrSelfHosting)
|
||||
|
||||
if (!isConfiguredForInMemoryCache) {
|
||||
const redisUrl = env.get('REDIS_URL')
|
||||
const isRedisInClusterMode = redisUrl.indexOf(',') > 0
|
||||
let redis
|
||||
if (isRedisInClusterMode) {
|
||||
redis = new Redis.Cluster(redisUrl.split(','))
|
||||
} else {
|
||||
redis = new Redis(redisUrl)
|
||||
}
|
||||
|
||||
container.bind(TYPES.Sync_Redis).toConstantValue(redis)
|
||||
container
|
||||
.bind<TransitionRepositoryInterface>(TYPES.Sync_TransitionStatusRepository)
|
||||
.toConstantValue(new RedisTransitionRepository(container.get<Redis>(TYPES.Sync_Redis)))
|
||||
}
|
||||
|
||||
container.bind<Env>(TYPES.Sync_Env).toConstantValue(env)
|
||||
|
||||
if (isConfiguredForHomeServer) {
|
||||
|
@ -413,27 +385,6 @@ export class ContainerConfigLoader {
|
|||
.bind<Repository<TypeORMMessage>>(TYPES.Sync_ORMMessageRepository)
|
||||
.toConstantValue(appDataSource.getRepository(TypeORMMessage))
|
||||
|
||||
// Mongo
|
||||
if (isSecondaryDatabaseEnabled) {
|
||||
container
|
||||
.bind<MapperInterface<Item, MongoDBItem>>(TYPES.Sync_MongoDBItemPersistenceMapper)
|
||||
.toConstantValue(new MongoDBItemPersistenceMapper())
|
||||
|
||||
container
|
||||
.bind<MongoRepository<MongoDBItem>>(TYPES.Sync_ORMMongoItemRepository)
|
||||
.toConstantValue(appDataSource.getMongoRepository(MongoDBItem))
|
||||
|
||||
container
|
||||
.bind<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
|
||||
.toConstantValue(
|
||||
new MongoDBItemRepository(
|
||||
container.get<MongoRepository<MongoDBItem>>(TYPES.Sync_ORMMongoItemRepository),
|
||||
container.get<MapperInterface<Item, MongoDBItem>>(TYPES.Sync_MongoDBItemPersistenceMapper),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// Repositories
|
||||
container
|
||||
.bind<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository)
|
||||
|
@ -450,14 +401,6 @@ export class ContainerConfigLoader {
|
|||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver)
|
||||
.toConstantValue(
|
||||
new TypeORMItemRepositoryResolver(
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository)
|
||||
.toConstantValue(
|
||||
|
@ -603,7 +546,7 @@ export class ContainerConfigLoader {
|
|||
.bind<GetItems>(TYPES.Sync_GetItems)
|
||||
.toConstantValue(
|
||||
new GetItems(
|
||||
container.get(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get(TYPES.Sync_SQLItemRepository),
|
||||
container.get(TYPES.Sync_SharedVaultUserRepository),
|
||||
container.get(TYPES.Sync_CONTENT_SIZE_TRANSFER_LIMIT),
|
||||
container.get(TYPES.Sync_ItemTransferCalculator),
|
||||
|
@ -615,7 +558,7 @@ export class ContainerConfigLoader {
|
|||
.bind<SaveNewItem>(TYPES.Sync_SaveNewItem)
|
||||
.toConstantValue(
|
||||
new SaveNewItem(
|
||||
container.get(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get(TYPES.Sync_SQLItemRepository),
|
||||
container.get(TYPES.Sync_Timer),
|
||||
container.get(TYPES.Sync_DomainEventPublisher),
|
||||
container.get(TYPES.Sync_DomainEventFactory),
|
||||
|
@ -655,7 +598,7 @@ export class ContainerConfigLoader {
|
|||
.bind<UpdateExistingItem>(TYPES.Sync_UpdateExistingItem)
|
||||
.toConstantValue(
|
||||
new UpdateExistingItem(
|
||||
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<TimerInterface>(TYPES.Sync_Timer),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
|
@ -670,7 +613,7 @@ export class ContainerConfigLoader {
|
|||
.toConstantValue(
|
||||
new SaveItems(
|
||||
container.get(TYPES.Sync_ItemSaveValidator),
|
||||
container.get(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get(TYPES.Sync_SQLItemRepository),
|
||||
container.get(TYPES.Sync_Timer),
|
||||
container.get(TYPES.Sync_SaveNewItem),
|
||||
container.get(TYPES.Sync_UpdateExistingItem),
|
||||
|
@ -699,7 +642,7 @@ export class ContainerConfigLoader {
|
|||
.bind<SyncItems>(TYPES.Sync_SyncItems)
|
||||
.toConstantValue(
|
||||
new SyncItems(
|
||||
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<GetItems>(TYPES.Sync_GetItems),
|
||||
container.get<SaveItems>(TYPES.Sync_SaveItems),
|
||||
container.get<GetSharedVaults>(TYPES.Sync_GetSharedVaults),
|
||||
|
@ -710,10 +653,10 @@ export class ContainerConfigLoader {
|
|||
),
|
||||
)
|
||||
container.bind<CheckIntegrity>(TYPES.Sync_CheckIntegrity).toDynamicValue((context: interfaces.Context) => {
|
||||
return new CheckIntegrity(context.container.get(TYPES.Sync_ItemRepositoryResolver))
|
||||
return new CheckIntegrity(context.container.get(TYPES.Sync_SQLItemRepository))
|
||||
})
|
||||
container.bind<GetItem>(TYPES.Sync_GetItem).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GetItem(context.container.get(TYPES.Sync_ItemRepositoryResolver))
|
||||
return new GetItem(context.container.get(TYPES.Sync_SQLItemRepository))
|
||||
})
|
||||
container
|
||||
.bind<InviteUserToSharedVault>(TYPES.Sync_InviteUserToSharedVault)
|
||||
|
@ -854,32 +797,10 @@ export class ContainerConfigLoader {
|
|||
container.get<SharedVaultRepositoryInterface>(TYPES.Sync_SharedVaultRepository),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionItemsFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Sync_TransitionItemsFromPrimaryToSecondaryDatabaseForUser,
|
||||
)
|
||||
.toConstantValue(
|
||||
new TransitionItemsFromPrimaryToSecondaryDatabaseForUser(
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
|
||||
isConfiguredForInMemoryCache
|
||||
? null
|
||||
: container.get<TransitionRepositoryInterface>(TYPES.Sync_TransitionStatusRepository),
|
||||
container.get<TimerInterface>(TYPES.Sync_Timer),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
env.get('MIGRATION_BATCH_SIZE', true) ? +env.get('MIGRATION_BATCH_SIZE', true) : 100,
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<RemoveItemsFromSharedVault>(TYPES.Sync_RemoveItemsFromSharedVault)
|
||||
.toConstantValue(
|
||||
new RemoveItemsFromSharedVault(
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
|
||||
: container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
),
|
||||
new RemoveItemsFromSharedVault(container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository)),
|
||||
)
|
||||
container
|
||||
.bind<DesignateSurvivor>(TYPES.Sync_DesignateSurvivor)
|
||||
|
@ -905,11 +826,7 @@ export class ContainerConfigLoader {
|
|||
container
|
||||
.bind<TransferSharedVaultItems>(TYPES.Sync_TransferSharedVaultItems)
|
||||
.toConstantValue(
|
||||
new TransferSharedVaultItems(
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
|
||||
: container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
),
|
||||
new TransferSharedVaultItems(container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository)),
|
||||
)
|
||||
container
|
||||
.bind<TransferSharedVault>(TYPES.Sync_TransferSharedVault)
|
||||
|
@ -947,7 +864,7 @@ export class ContainerConfigLoader {
|
|||
.bind<DumpItem>(TYPES.Sync_DumpItem)
|
||||
.toConstantValue(
|
||||
new DumpItem(
|
||||
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
|
@ -985,7 +902,7 @@ export class ContainerConfigLoader {
|
|||
.bind<DuplicateItemSyncedEventHandler>(TYPES.Sync_DuplicateItemSyncedEventHandler)
|
||||
.toConstantValue(
|
||||
new DuplicateItemSyncedEventHandler(
|
||||
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
|
@ -995,7 +912,7 @@ export class ContainerConfigLoader {
|
|||
.bind<AccountDeletionRequestedEventHandler>(TYPES.Sync_AccountDeletionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new AccountDeletionRequestedEventHandler(
|
||||
container.get<ItemRepositoryResolverInterface>(TYPES.Sync_ItemRepositoryResolver),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
container.get<DeleteSharedVaults>(TYPES.Sync_DeleteSharedVaults),
|
||||
container.get<RemoveUserFromSharedVaults>(TYPES.Sync_RemoveUserFromSharedVaults),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
|
@ -1036,17 +953,6 @@ export class ContainerConfigLoader {
|
|||
container.get<winston.Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<TransitionRequestedEventHandler>(TYPES.Sync_TransitionRequestedEventHandler)
|
||||
.toConstantValue(
|
||||
new TransitionRequestedEventHandler(
|
||||
false,
|
||||
container.get<TransitionItemsFromPrimaryToSecondaryDatabaseForUser>(
|
||||
TYPES.Sync_TransitionItemsFromPrimaryToSecondaryDatabaseForUser,
|
||||
),
|
||||
container.get<Logger>(TYPES.Sync_Logger),
|
||||
),
|
||||
)
|
||||
container
|
||||
.bind<SharedVaultRemovedEventHandler>(TYPES.Sync_SharedVaultRemovedEventHandler)
|
||||
.toConstantValue(
|
||||
|
@ -1065,7 +971,6 @@ export class ContainerConfigLoader {
|
|||
new ExtensionsHttpService(
|
||||
container.get<AxiosInstance>(TYPES.Sync_HTTPClient),
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
isSecondaryDatabaseEnabled ? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository) : null,
|
||||
container.get<ContentDecoderInterface>(TYPES.Sync_ContentDecoder),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
|
@ -1089,10 +994,6 @@ export class ContainerConfigLoader {
|
|||
'SHARED_VAULT_FILE_MOVED',
|
||||
container.get<SharedVaultFileMovedEventHandler>(TYPES.Sync_SharedVaultFileMovedEventHandler),
|
||||
],
|
||||
[
|
||||
'TRANSITION_REQUESTED',
|
||||
container.get<TransitionRequestedEventHandler>(TYPES.Sync_TransitionRequestedEventHandler),
|
||||
],
|
||||
[
|
||||
'SHARED_VAULT_REMOVED',
|
||||
container.get<SharedVaultRemovedEventHandler>(TYPES.Sync_SharedVaultRemovedEventHandler),
|
||||
|
@ -1104,9 +1005,6 @@ export class ContainerConfigLoader {
|
|||
.toConstantValue(
|
||||
new EmailBackupRequestedEventHandler(
|
||||
container.get<ItemRepositoryInterface>(TYPES.Sync_SQLItemRepository),
|
||||
isSecondaryDatabaseEnabled
|
||||
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
|
||||
: null,
|
||||
container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
|
||||
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
|
||||
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { DataSource, EntityTarget, LoggerOptions, MongoRepository, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { DataSource, EntityTarget, LoggerOptions, ObjectLiteral, Repository } from 'typeorm'
|
||||
import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions'
|
||||
import { Env } from './Env'
|
||||
import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'
|
||||
|
@ -8,12 +8,10 @@ import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault'
|
|||
import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser'
|
||||
import { TypeORMSharedVaultInvite } from '../Infra/TypeORM/TypeORMSharedVaultInvite'
|
||||
import { TypeORMMessage } from '../Infra/TypeORM/TypeORMMessage'
|
||||
import { MongoDBItem } from '../Infra/TypeORM/MongoDBItem'
|
||||
import { SQLItem } from '../Infra/TypeORM/SQLItem'
|
||||
|
||||
export class AppDataSource {
|
||||
private _dataSource: DataSource | undefined
|
||||
private _secondaryDataSource: DataSource | undefined
|
||||
|
||||
constructor(
|
||||
private configuration: {
|
||||
|
@ -30,43 +28,8 @@ export class AppDataSource {
|
|||
return this._dataSource.getRepository(target)
|
||||
}
|
||||
|
||||
getMongoRepository<Entity extends ObjectLiteral>(target: EntityTarget<Entity>): MongoRepository<Entity> {
|
||||
if (!this._secondaryDataSource) {
|
||||
throw new Error('Secondary DataSource not initialized')
|
||||
}
|
||||
|
||||
return this._secondaryDataSource.getMongoRepository(target)
|
||||
}
|
||||
|
||||
async initialize(): Promise<void> {
|
||||
await this.dataSource.initialize()
|
||||
const secondaryDataSource = this.secondaryDataSource
|
||||
if (secondaryDataSource) {
|
||||
await secondaryDataSource.initialize()
|
||||
}
|
||||
}
|
||||
|
||||
get secondaryDataSource(): DataSource | undefined {
|
||||
this.configuration.env.load()
|
||||
|
||||
if (this.configuration.env.get('SECONDARY_DB_ENABLED', true) !== 'true') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
this._secondaryDataSource = new DataSource({
|
||||
type: 'mongodb',
|
||||
host: this.configuration.env.get('MONGO_HOST'),
|
||||
authSource: 'admin',
|
||||
port: parseInt(this.configuration.env.get('MONGO_PORT')),
|
||||
username: this.configuration.env.get('MONGO_USERNAME'),
|
||||
password: this.configuration.env.get('MONGO_PASSWORD', true),
|
||||
database: this.configuration.env.get('MONGO_DATABASE'),
|
||||
entities: [MongoDBItem],
|
||||
retryWrites: false,
|
||||
synchronize: true,
|
||||
})
|
||||
|
||||
return this._secondaryDataSource
|
||||
}
|
||||
|
||||
get dataSource(): DataSource {
|
||||
|
|
|
@ -7,15 +7,12 @@ const TYPES = {
|
|||
Sync_S3: Symbol.for('Sync_S3'),
|
||||
Sync_Env: Symbol.for('Sync_Env'),
|
||||
// Repositories
|
||||
Sync_ItemRepositoryResolver: Symbol.for('Sync_ItemRepositoryResolver'),
|
||||
Sync_SQLItemRepository: Symbol.for('Sync_SQLItemRepository'),
|
||||
Sync_MongoDBItemRepository: Symbol.for('Sync_MongoDBItemRepository'),
|
||||
Sync_SharedVaultRepository: Symbol.for('Sync_SharedVaultRepository'),
|
||||
Sync_SharedVaultInviteRepository: Symbol.for('Sync_SharedVaultInviteRepository'),
|
||||
Sync_SharedVaultUserRepository: Symbol.for('Sync_SharedVaultUserRepository'),
|
||||
Sync_NotificationRepository: Symbol.for('Sync_NotificationRepository'),
|
||||
Sync_MessageRepository: Symbol.for('Sync_MessageRepository'),
|
||||
Sync_TransitionStatusRepository: Symbol.for('Sync_TransitionStatusRepository'),
|
||||
// ORM
|
||||
Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'),
|
||||
Sync_ORMLegacyItemRepository: Symbol.for('Sync_ORMLegacyItemRepository'),
|
||||
|
@ -24,8 +21,6 @@ const TYPES = {
|
|||
Sync_ORMSharedVaultUserRepository: Symbol.for('Sync_ORMSharedVaultUserRepository'),
|
||||
Sync_ORMNotificationRepository: Symbol.for('Sync_ORMNotificationRepository'),
|
||||
Sync_ORMMessageRepository: Symbol.for('Sync_ORMMessageRepository'),
|
||||
// Mongo
|
||||
Sync_ORMMongoItemRepository: Symbol.for('Sync_ORMMongoItemRepository'),
|
||||
// Middleware
|
||||
Sync_AuthMiddleware: Symbol.for('Sync_AuthMiddleware'),
|
||||
// env vars
|
||||
|
@ -82,9 +77,6 @@ const TYPES = {
|
|||
Sync_DetermineSharedVaultOperationOnItem: Symbol.for('Sync_DetermineSharedVaultOperationOnItem'),
|
||||
Sync_UpdateStorageQuotaUsedInSharedVault: Symbol.for('Sync_UpdateStorageQuotaUsedInSharedVault'),
|
||||
Sync_AddNotificationsForUsers: Symbol.for('Sync_AddNotificationsForUsers'),
|
||||
Sync_TransitionItemsFromPrimaryToSecondaryDatabaseForUser: Symbol.for(
|
||||
'Sync_TransitionItemsFromPrimaryToSecondaryDatabaseForUser',
|
||||
),
|
||||
Sync_SendEventToClient: Symbol.for('Sync_SendEventToClient'),
|
||||
Sync_RemoveItemsFromSharedVault: Symbol.for('Sync_RemoveItemsFromSharedVault'),
|
||||
Sync_DesignateSurvivor: Symbol.for('Sync_DesignateSurvivor'),
|
||||
|
@ -100,7 +92,6 @@ const TYPES = {
|
|||
Sync_SharedVaultFileRemovedEventHandler: Symbol.for('Sync_SharedVaultFileRemovedEventHandler'),
|
||||
Sync_SharedVaultFileUploadedEventHandler: Symbol.for('Sync_SharedVaultFileUploadedEventHandler'),
|
||||
Sync_SharedVaultFileMovedEventHandler: Symbol.for('Sync_SharedVaultFileMovedEventHandler'),
|
||||
Sync_TransitionRequestedEventHandler: Symbol.for('Sync_TransitionRequestedEventHandler'),
|
||||
Sync_SharedVaultRemovedEventHandler: Symbol.for('Sync_SharedVaultRemovedEventHandler'),
|
||||
// Services
|
||||
Sync_ContentDecoder: Symbol.for('Sync_ContentDecoder'),
|
||||
|
@ -139,7 +130,6 @@ const TYPES = {
|
|||
Sync_NotificationHttpMapper: Symbol.for('Sync_NotificationHttpMapper'),
|
||||
Sync_SQLLegacyItemPersistenceMapper: Symbol.for('Sync_SQLLegacyItemPersistenceMapper'),
|
||||
Sync_SQLItemPersistenceMapper: Symbol.for('Sync_SQLItemPersistenceMapper'),
|
||||
Sync_MongoDBItemPersistenceMapper: Symbol.for('Sync_MongoDBItemPersistenceMapper'),
|
||||
Sync_ItemHttpMapper: Symbol.for('Sync_ItemHttpMapper'),
|
||||
Sync_ItemHashHttpMapper: Symbol.for('Sync_ItemHashHttpMapper'),
|
||||
Sync_SavedItemHttpMapper: Symbol.for('Sync_SavedItemHttpMapper'),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue