新增构建OpenSSL镜像相关文件
This commit is contained in:
@@ -0,0 +1 @@
|
||||
dist
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../.eslintrc",
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./linter.tsconfig.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.41.0...@standardnotes/domain-core@1.41.1) (2023-11-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* repository config in package.json files ([ed1bf37](https://github.com/standardnotes/server/commit/ed1bf37287af23a25b8388ada95f0acdec8f71ea))
|
||||
|
||||
# [1.41.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.40.0...@standardnotes/domain-core@1.41.0) (2023-11-27)
|
||||
|
||||
### Features
|
||||
|
||||
* add npm provenance to published packages ([e836abd](https://github.com/standardnotes/server/commit/e836abdef73d246940d8fffd9e65e17c64cd35c8))
|
||||
|
||||
# [1.40.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.39.0...@standardnotes/domain-core@1.40.0) (2023-10-26)
|
||||
|
||||
### Features
|
||||
|
||||
* extract setting name to domain-core package ([0e43bc0](https://github.com/standardnotes/server/commit/0e43bc00427113f421b0c4b67c067f0de96caf52))
|
||||
|
||||
# [1.39.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.38.0...@standardnotes/domain-core@1.39.0) (2023-10-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor settings ([#890](https://github.com/standardnotes/server/issues/890)) ([1b5078e](https://github.com/standardnotes/server/commit/1b5078eb9629397822f5403643c60fbf4182df92))
|
||||
|
||||
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.37.0...@standardnotes/domain-core@1.38.0) (2023-10-19)
|
||||
|
||||
### Features
|
||||
|
||||
* remove transition state ([#882](https://github.com/standardnotes/server/issues/882)) ([a2c1ebe](https://github.com/standardnotes/server/commit/a2c1ebe675cd5678c923715056a6966f465a15d6))
|
||||
|
||||
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.36.0...@standardnotes/domain-core@1.37.0) (2023-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add email scheduled task service identifier ([af5ebb2](https://github.com/standardnotes/server/commit/af5ebb25e7fd05390089e0f7c00af50648ad49d9))
|
||||
|
||||
# [1.36.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.35.0...@standardnotes/domain-core@1.36.0) (2023-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* add opentelemetry for scheduled tasks ([443235a](https://github.com/standardnotes/server/commit/443235a861181acf708d98fba25ce6d79f198b56))
|
||||
* **domain-core:** add email bounce processor service identifier ([6356fca](https://github.com/standardnotes/server/commit/6356fcaeed012e7ea5d174b47aaebb94a4040d29))
|
||||
|
||||
# [1.35.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.2...@standardnotes/domain-core@1.35.0) (2023-10-06)
|
||||
|
||||
### Features
|
||||
|
||||
* add xray to analytics scheduler and websockets ([f0531d6](https://github.com/standardnotes/server/commit/f0531d68cb77036222f2a34602819f11e6a2697d))
|
||||
|
||||
## [1.34.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.1...@standardnotes/domain-core@1.34.2) (2023-10-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* identifying services in workers ([eab78b3](https://github.com/standardnotes/server/commit/eab78b3a95ec82cb779d069d172169bfa92368c9))
|
||||
|
||||
## [1.34.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.34.0...@standardnotes/domain-core@1.34.1) (2023-09-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* removing items in a vault and notifying about designated survivor ([#855](https://github.com/standardnotes/server/issues/855)) ([1d06ffe](https://github.com/standardnotes/server/commit/1d06ffe9d51722ada7baa040e1d5ed351fc28f39))
|
||||
|
||||
# [1.34.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.2...@standardnotes/domain-core@1.34.0) (2023-09-26)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor handling revision creation from dump ([#854](https://github.com/standardnotes/server/issues/854)) ([ca6dbc0](https://github.com/standardnotes/server/commit/ca6dbc00537bb20f508f9310b1a838421f53a643))
|
||||
|
||||
## [1.33.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.1...@standardnotes/domain-core@1.33.2) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** notification paylod to string casting ([a02a287](https://github.com/standardnotes/server/commit/a02a28774b6d500200043faefb9ebac3719e7661))
|
||||
|
||||
## [1.33.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.33.0...@standardnotes/domain-core@1.33.1) (2023-09-25)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* refactor the structure of notifications ([#853](https://github.com/standardnotes/server/issues/853)) ([cebab59](https://github.com/standardnotes/server/commit/cebab59a026c6868886e0945787a8ddb0442fbc3))
|
||||
|
||||
# [1.33.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.32.0...@standardnotes/domain-core@1.33.0) (2023-09-21)
|
||||
|
||||
### Features
|
||||
|
||||
* add designating a survivor in shared vault ([#841](https://github.com/standardnotes/server/issues/841)) ([230c96d](https://github.com/standardnotes/server/commit/230c96dcf1d8faed9ce8fe288549226da70317e6))
|
||||
|
||||
# [1.32.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.31.0...@standardnotes/domain-core@1.32.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** distinct notifications upon user removal from shared vault ([#840](https://github.com/standardnotes/server/issues/840)) ([41e2136](https://github.com/standardnotes/server/commit/41e2136bc07312974701a70652528d304105e0f9))
|
||||
|
||||
# [1.31.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.1...@standardnotes/domain-core@1.31.0) (2023-09-20)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** add notification for user upon declined shared vault invitation ([#837](https://github.com/standardnotes/server/issues/837)) ([31e7aaf](https://github.com/standardnotes/server/commit/31e7aaf253029a951d8b943d6cffd655cd5ca765))
|
||||
|
||||
## [1.30.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.30.0...@standardnotes/domain-core@1.30.1) (2023-09-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** allow any version and variant of the UUID format ([de081fe](https://github.com/standardnotes/server/commit/de081fe78658bdd36c8c5d86b70a16a2880aa3d1))
|
||||
|
||||
# [1.30.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.29.0...@standardnotes/domain-core@1.30.0) (2023-09-18)
|
||||
|
||||
### Features
|
||||
|
||||
* add publishing notifications for users when a user is added to vault ([#834](https://github.com/standardnotes/server/issues/834)) ([547a79e](https://github.com/standardnotes/server/commit/547a79e23174dab0a756e4e5bee218e4859b3b42))
|
||||
|
||||
# [1.29.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.28.1...@standardnotes/domain-core@1.29.0) (2023-09-15)
|
||||
|
||||
### Features
|
||||
|
||||
* refactor transition to minimize status changes ([#828](https://github.com/standardnotes/server/issues/828)) ([36f07c6](https://github.com/standardnotes/server/commit/36f07c691afc213ecf817d6e98f885ddb19a6ed6))
|
||||
|
||||
## [1.28.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.28.0...@standardnotes/domain-core@1.28.1) (2023-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* comparing uuids ([0a1d162](https://github.com/standardnotes/server/commit/0a1d1624e818000f2e951f29323a88e6e233c755))
|
||||
|
||||
# [1.28.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.27.0...@standardnotes/domain-core@1.28.0) (2023-09-07)
|
||||
|
||||
### Features
|
||||
|
||||
* add VaultsUser role name ([#809](https://github.com/standardnotes/server/issues/809)) ([d4830de](https://github.com/standardnotes/server/commit/d4830dec018139ffaf93d0e91d6655c5dcc6be9c))
|
||||
|
||||
# [1.27.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.3...@standardnotes/domain-core@1.27.0) (2023-09-06)
|
||||
|
||||
### Features
|
||||
|
||||
* should be able to access shared item revisions as third party user ([#807](https://github.com/standardnotes/server/issues/807)) ([794cd87](https://github.com/standardnotes/server/commit/794cd8734acf89fb29f09dfb169a3f08f252bb6a))
|
||||
|
||||
## [1.26.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.2...@standardnotes/domain-core@1.26.3) (2023-09-04)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
## [1.26.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.1...@standardnotes/domain-core@1.26.2) (2023-09-01)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
## [1.26.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.26.0...@standardnotes/domain-core@1.26.1) (2023-08-30)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
# [1.26.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.2...@standardnotes/domain-core@1.26.0) (2023-08-18)
|
||||
|
||||
### Features
|
||||
|
||||
* add mechanism for determining if a user should use the primary or secondary items database ([#700](https://github.com/standardnotes/server/issues/700)) ([302b624](https://github.com/standardnotes/server/commit/302b624504f4c87fd7c3ddfee77cbdc14a61018b))
|
||||
|
||||
## [1.25.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.1...@standardnotes/domain-core@1.25.2) (2023-08-09)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "Revert "feat(syncing-server): notify shared vault users upon file uploads or removals (#692)"" ([1c3ff52](https://github.com/standardnotes/server/commit/1c3ff526b7c4885f71f019f6c01142f522a6f8ad)), closes [#692](https://github.com/standardnotes/server/issues/692)
|
||||
|
||||
## [1.25.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.25.0...@standardnotes/domain-core@1.25.1) (2023-08-09)
|
||||
|
||||
### Reverts
|
||||
|
||||
* Revert "feat(syncing-server): notify shared vault users upon file uploads or removals (#692)" ([d261c81](https://github.com/standardnotes/server/commit/d261c81cd0bdbb9001c8589224f007ed2d338903)), closes [#692](https://github.com/standardnotes/server/issues/692)
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.2...@standardnotes/domain-core@1.25.0) (2023-08-09)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** notify shared vault users upon file uploads or removals ([#692](https://github.com/standardnotes/server/issues/692)) ([46867c1](https://github.com/standardnotes/server/commit/46867c1a4dd310c1971ff37e1bdf380c10e478fd))
|
||||
|
||||
## [1.24.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.1...@standardnotes/domain-core@1.24.2) (2023-08-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** remove unused content types ([71624f1](https://github.com/standardnotes/server/commit/71624f18979ed9254fafeeced733e598cd66cbeb))
|
||||
|
||||
## [1.24.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.24.0...@standardnotes/domain-core@1.24.1) (2023-07-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* setting env vars on home server in e2e environment ([f87036e](https://github.com/standardnotes/server/commit/f87036e3a8dc6b7784e74e5f32ffd220033724f5))
|
||||
|
||||
# [1.24.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.4...@standardnotes/domain-core@1.24.0) (2023-07-26)
|
||||
|
||||
### Features
|
||||
|
||||
* extract shared vault user permission to domain-core ([e215ac4](https://github.com/standardnotes/server/commit/e215ac4343e9f8818f40004d31390d6ac23e369d))
|
||||
|
||||
## [1.23.4](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.3...@standardnotes/domain-core@1.23.4) (2023-07-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** persisting aggregate changes from root ([#674](https://github.com/standardnotes/server/issues/674)) ([c34f548](https://github.com/standardnotes/server/commit/c34f548e45bbd8defb8d490936e90755fd284e78))
|
||||
|
||||
## [1.23.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.2...@standardnotes/domain-core@1.23.3) (2023-07-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** notification payload creation from string ([1708c3f](https://github.com/standardnotes/server/commit/1708c3f8a00897369585e1ef8022950a7c3c610e))
|
||||
|
||||
## [1.23.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.1...@standardnotes/domain-core@1.23.2) (2023-07-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* user notifications structure ([#667](https://github.com/standardnotes/server/issues/667)) ([1bbb639](https://github.com/standardnotes/server/commit/1bbb639c83922ec09e3778f85419d76669d36ae3))
|
||||
|
||||
## [1.23.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.23.0...@standardnotes/domain-core@1.23.1) (2023-07-19)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** add missing messages and key system identifier sql representations ([#663](https://github.com/standardnotes/server/issues/663)) ([d026152](https://github.com/standardnotes/server/commit/d026152ac8cb2ecda2eee8d3f7385d655b210938))
|
||||
|
||||
# [1.23.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.22.0...@standardnotes/domain-core@1.23.0) (2023-07-17)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** refactor syncing to decouple getting and saving items ([#659](https://github.com/standardnotes/server/issues/659)) ([cb74b23](https://github.com/standardnotes/server/commit/cb74b23e45b207136e299ce8a3db2c04dc87e21e))
|
||||
|
||||
# [1.22.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.1...@standardnotes/domain-core@1.22.0) (2023-07-12)
|
||||
|
||||
### Features
|
||||
|
||||
* domain items ([#655](https://github.com/standardnotes/server/issues/655)) ([a0af8f0](https://github.com/standardnotes/server/commit/a0af8f00252e1219e58cb7e066c11a8e71692e9d))
|
||||
|
||||
## [1.21.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.21.0...@standardnotes/domain-core@1.21.1) (2023-07-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* transfer notifications from auth to syncing-server. ([#648](https://github.com/standardnotes/server/issues/648)) ([c288e5d](https://github.com/standardnotes/server/commit/c288e5d8dc54778a96a9fc33e3c9cae00583fade))
|
||||
|
||||
# [1.21.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.20.0...@standardnotes/domain-core@1.21.0) (2023-07-06)
|
||||
|
||||
### Features
|
||||
|
||||
* getting shared vault users and removing shared vault user ([#642](https://github.com/standardnotes/server/issues/642)) ([e905128](https://github.com/standardnotes/server/commit/e905128d45eaadb34d3465d4480dfb3a2c5f3f79))
|
||||
|
||||
# [1.20.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.19.0...@standardnotes/domain-core@1.20.0) (2023-07-05)
|
||||
|
||||
### Features
|
||||
|
||||
* deleting shared vaults. ([#640](https://github.com/standardnotes/server/issues/640)) ([f3161c2](https://github.com/standardnotes/server/commit/f3161c271296159331639814b2dbb2e566cc54c9))
|
||||
|
||||
# [1.19.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.18.0...@standardnotes/domain-core@1.19.0) (2023-06-30)
|
||||
|
||||
### Features
|
||||
|
||||
* add shared vaults model. ([#631](https://github.com/standardnotes/server/issues/631)) ([38e77f0](https://github.com/standardnotes/server/commit/38e77f04be441b7506c3390fb0d9894b34119c3e))
|
||||
|
||||
# [1.18.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.17.0...@standardnotes/domain-core@1.18.0) (2023-06-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add overriding environment variables in underlying services ([#621](https://github.com/standardnotes/server/issues/621)) ([f0cbec0](https://github.com/standardnotes/server/commit/f0cbec07b87d60dfad92072944553f76e0bea164))
|
||||
|
||||
# [1.17.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.16.2...@standardnotes/domain-core@1.17.0) (2023-05-31)
|
||||
|
||||
### Features
|
||||
|
||||
* **home-server:** add custom home server logs ([#619](https://github.com/standardnotes/server/issues/619)) ([bc63d0a](https://github.com/standardnotes/server/commit/bc63d0aeea86abbb4a144b2682b7070d7bdfe878))
|
||||
|
||||
## [1.16.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.16.0...@standardnotes/domain-core@1.16.2) (2023-05-30)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bump version manually to publish packages ([b0d01df](https://github.com/standardnotes/server/commit/b0d01dffd91557c67eac2940d9270bca208c1128))
|
||||
|
||||
# [1.16.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.15.0...@standardnotes/domain-core@1.16.0) (2023-05-29)
|
||||
|
||||
### Features
|
||||
|
||||
* add files server as a service to home-server ([#614](https://github.com/standardnotes/server/issues/614)) ([c7d575a](https://github.com/standardnotes/server/commit/c7d575a0ffc7eb3e8799c3835da5727584f4f67b))
|
||||
|
||||
# [1.15.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.2...@standardnotes/domain-core@1.15.0) (2023-05-16)
|
||||
|
||||
### Features
|
||||
|
||||
* home-server package initial setup with Api Gateway and Auth services ([#605](https://github.com/standardnotes/server/issues/605)) ([dc71e67](https://github.com/standardnotes/server/commit/dc71e6777fc4c51234b79f6fb409f9f3111cc6a5))
|
||||
|
||||
## [1.14.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.1...@standardnotes/domain-core@1.14.2) (2023-05-09)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* node engine version requirement in package.json files ([62a0e89](https://github.com/standardnotes/server/commit/62a0e89748ab306566c4aa10b9dc0385fb0f1684))
|
||||
|
||||
## [1.14.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.14.0...@standardnotes/domain-core@1.14.1) (2023-05-05)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
# [1.14.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.13.0...@standardnotes/domain-core@1.14.0) (2023-05-02)
|
||||
|
||||
### Features
|
||||
|
||||
* extract cache entry model to domain-core ([#581](https://github.com/standardnotes/server/issues/581)) ([c71f7ff](https://github.com/standardnotes/server/commit/c71f7ff8ad4ffbd7151e8397b5816e383b178eb4))
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.12.0...@standardnotes/domain-core@1.13.0) (2023-04-27)
|
||||
|
||||
### Features
|
||||
|
||||
* sqlite driver for auth service ([#572](https://github.com/standardnotes/server/issues/572)) ([3aef599](https://github.com/standardnotes/server/commit/3aef5998df2b4cf96c597ffa11a47dfc250d1647))
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.3...@standardnotes/domain-core@1.12.0) (2023-03-08)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add internal team user role ([#473](https://github.com/standardnotes/server/issues/473)) ([979a320](https://github.com/standardnotes/server/commit/979a320ca666991ad2b023436f58c59ae168c768))
|
||||
|
||||
## [1.11.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.2...@standardnotes/domain-core@1.11.3) (2023-02-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** remove unnecessary dependencies ([16043a7](https://github.com/standardnotes/server/commit/16043a7d6881378ed3286e08dc9e21e5e6b89171))
|
||||
|
||||
## [1.11.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.1...@standardnotes/domain-core@1.11.2) (2023-01-20)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
## [1.11.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.11.0...@standardnotes/domain-core@1.11.1) (2023-01-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **revisions:** add required role to revisions list response ([e7beee2](https://github.com/standardnotes/server/commit/e7beee278871d2939b058d842404fd6980d7f48a))
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.10.0...@standardnotes/domain-core@1.11.0) (2022-12-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add legacy session model ([4084f2f](https://github.com/standardnotes/server/commit/4084f2f5ecf8379ff69d619d3d12495c010c3980))
|
||||
|
||||
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.9.0...@standardnotes/domain-core@1.10.0) (2022-12-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add session model ([1c4d4c5](https://github.com/standardnotes/server/commit/1c4d4c57dea1187dc130f1ae8b7dc8ede332320f))
|
||||
|
||||
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.8.0...@standardnotes/domain-core@1.9.0) (2022-12-07)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** rename email subscription rejection level to email level ([c87561f](https://github.com/standardnotes/server/commit/c87561fca782883b84f58b4f0b9f85ecc279ca50))
|
||||
|
||||
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.7.0...@standardnotes/domain-core@1.8.0) (2022-12-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add email subscription rejection levels ([02f3c85](https://github.com/standardnotes/server/commit/02f3c85796ade7cb69edbdda2367c0d91ac1bdf0))
|
||||
|
||||
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.6.0...@standardnotes/domain-core@1.7.0) (2022-12-05)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** distinguish between username and email ([06fd404](https://github.com/standardnotes/server/commit/06fd404d44b44a53733f889aabd4da63f21e2f36))
|
||||
|
||||
# [1.6.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.2...@standardnotes/domain-core@1.6.0) (2022-12-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add subscription plan name value object ([800fe9e](https://github.com/standardnotes/server/commit/800fe9e4c80c33f2da8097b5a153f470a23b7984))
|
||||
|
||||
## [1.5.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.1...@standardnotes/domain-core@1.5.2) (2022-12-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** rename timestamps to dates ([dd86c5b](https://github.com/standardnotes/server/commit/dd86c5bcdf3a1a37d684f6416d4cc6f24497fe5e))
|
||||
|
||||
## [1.5.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.5.0...@standardnotes/domain-core@1.5.1) (2022-11-25)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
# [1.5.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.4.0...@standardnotes/domain-core@1.5.0) (2022-11-24)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add methods to check role power ([9d90f27](https://github.com/standardnotes/server/commit/9d90f276de8915d91d009909154036ba128687e0))
|
||||
|
||||
# [1.4.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.3.0...@standardnotes/domain-core@1.4.0) (2022-11-24)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add role name collection value object ([ae2f8f0](https://github.com/standardnotes/server/commit/ae2f8f086b9f647bb98c59f32375b45243cb0af9))
|
||||
|
||||
# [1.3.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.2...@standardnotes/domain-core@1.3.0) (2022-11-24)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add role name value object ([748630e](https://github.com/standardnotes/server/commit/748630e1f1ed1dfae2e743cd2b3d3fd91967088c))
|
||||
|
||||
## [1.2.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.1...@standardnotes/domain-core@1.2.2) (2022-11-22)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-core
|
||||
|
||||
## [1.2.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.2.0...@standardnotes/domain-core@1.2.1) (2022-11-21)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **domain-core:** remove revisions related models to revisions microservice ([a6542dd](https://github.com/standardnotes/server/commit/a6542dd63870a8ada5fd8143d8e2133a570d9329))
|
||||
|
||||
# [1.2.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.1.1...@standardnotes/domain-core@1.2.0) (2022-11-18)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add revision definition to domain core ([c8f3a0c](https://github.com/standardnotes/server/commit/c8f3a0ce7b589a6fbc47941fc5d1a44b6cf04fe3))
|
||||
* **revisions:** add revisions microservice ([d5c06bf](https://github.com/standardnotes/server/commit/d5c06bfa58a987685fbd8fbab0d22df3fcff3377))
|
||||
|
||||
## [1.1.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.1.0...@standardnotes/domain-core@1.1.1) (2022-11-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** retrieving revisions ([50f7ae3](https://github.com/standardnotes/server/commit/50f7ae338ad66d3465fa16c31e7c47c57b1e0c3c))
|
||||
|
||||
# 1.1.0 (2022-11-14)
|
||||
|
||||
### Features
|
||||
|
||||
* **analytics:** extract domain core into a separate package ([0f94e2a](https://github.com/standardnotes/server/commit/0f94e2ad0c8927733eac31f130cbe649dce765f9))
|
||||
@@ -0,0 +1,10 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const base = require('../../jest.config')
|
||||
const { defaults: tsjPreset } = require('ts-jest/presets')
|
||||
|
||||
module.exports = {
|
||||
...base,
|
||||
transform: {
|
||||
...tsjPreset.transform,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.41.1",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <21.0.0"
|
||||
},
|
||||
"description": "Domain Core SDK used in SN projects",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
"files": [
|
||||
"dist/src/**/*.js",
|
||||
"dist/src/**/*.d.ts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"provenance": true
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:standardnotes/server.git",
|
||||
"directory": "packages/domain-core"
|
||||
},
|
||||
"author": "Standard Notes",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"clean": "rm -fr dist",
|
||||
"build": "tsc --build",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"lint:fix": "eslint . --ext .ts --fix",
|
||||
"test": "jest --coverage --no-cache --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^20.5.7",
|
||||
"@types/uuid": "^9.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
||||
"@typescript-eslint/parser": "^6.5.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"prettier": "^3.0.3",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.0.4"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { LegacySession } from './LegacySession'
|
||||
|
||||
describe('LegacySession', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = LegacySession.create('foobar')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().accessToken).toEqual('foobar')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = LegacySession.create('')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { LegacySessionProps } from './LegacySessionProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class LegacySession extends ValueObject<LegacySessionProps> {
|
||||
get accessToken(): string {
|
||||
return this.props.token
|
||||
}
|
||||
|
||||
private constructor(props: LegacySessionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(token: string): Result<LegacySession> {
|
||||
if (Validator.isNotEmpty(token).isFailed()) {
|
||||
return Result.fail<LegacySession>('Could not create legacy session. Token value is empty')
|
||||
}
|
||||
|
||||
return Result.ok<LegacySession>(new LegacySession({ token }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface LegacySessionProps {
|
||||
token: string
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Session } from './Session'
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
describe('Session', () => {
|
||||
it('should create a session value object', () => {
|
||||
const accessToken = SessionToken.create('foobar1', 1234567890).getValue()
|
||||
const refreshToken = SessionToken.create('foobar2', 1234567890).getValue()
|
||||
|
||||
const valueOrError = Session.create(accessToken, refreshToken)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().accessToken.value).toEqual('foobar1')
|
||||
expect(valueOrError.getValue().refreshToken.value).toEqual('foobar2')
|
||||
expect(valueOrError.getValue().isReadOnly()).toEqual(false)
|
||||
})
|
||||
|
||||
it('should create a session reado-only value object', () => {
|
||||
const accessToken = SessionToken.create('foobar', 1234567890).getValue()
|
||||
const refreshToken = SessionToken.create('foobar', 1234567890).getValue()
|
||||
|
||||
const valueOrError = Session.create(accessToken, refreshToken, true)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().isReadOnly()).toEqual(true)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { SessionProps } from './SessionProps'
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
export class Session extends ValueObject<SessionProps> {
|
||||
get accessToken(): SessionToken {
|
||||
return this.props.accessToken
|
||||
}
|
||||
|
||||
get refreshToken(): SessionToken {
|
||||
return this.props.refreshToken
|
||||
}
|
||||
|
||||
isReadOnly(): boolean {
|
||||
return this.props.readonlyAccess || false
|
||||
}
|
||||
|
||||
private constructor(props: SessionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(accessToken: SessionToken, refreshToken: SessionToken, readonlyAccess?: boolean): Result<Session> {
|
||||
return Result.ok<Session>(new Session({ accessToken, refreshToken, readonlyAccess }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
export interface SessionProps {
|
||||
accessToken: SessionToken
|
||||
refreshToken: SessionToken
|
||||
readonlyAccess?: boolean
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
describe('SessionToken', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = SessionToken.create('foobar', 1234567890)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('foobar')
|
||||
expect(valueOrError.getValue().expiresAt).toEqual(1234567890)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
let valueOrError = SessionToken.create('', 1234567890)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = SessionToken.create('foobar', undefined as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { SessionTokenProps } from './SessionTokenProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class SessionToken extends ValueObject<SessionTokenProps> {
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
get expiresAt(): number {
|
||||
return this.props.expiresAt
|
||||
}
|
||||
|
||||
private constructor(props: SessionTokenProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(value: string, expiresAt: number): Result<SessionToken> {
|
||||
if (Validator.isNotEmpty(value).isFailed()) {
|
||||
return Result.fail<SessionToken>('Could not create session token. Token value is empty')
|
||||
}
|
||||
if (Validator.isNotEmpty(expiresAt).isFailed()) {
|
||||
return Result.fail<SessionToken>('Could not create session token. Token expiration is empty')
|
||||
}
|
||||
|
||||
return Result.ok<SessionToken>(new SessionToken({ value, expiresAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SessionTokenProps {
|
||||
value: string
|
||||
expiresAt: number
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Entity } from '../Core/Entity'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { CacheEntryProps } from './CacheEntryProps'
|
||||
|
||||
export class CacheEntry extends Entity<CacheEntryProps> {
|
||||
private constructor(props: CacheEntryProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: CacheEntryProps, id?: UniqueEntityId): Result<CacheEntry> {
|
||||
return Result.ok<CacheEntry>(new CacheEntry(props, id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface CacheEntryProps {
|
||||
key: string
|
||||
value: string
|
||||
expiresAt: Date | null
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { CacheEntry } from './CacheEntry'
|
||||
|
||||
export interface CacheEntryRepositoryInterface {
|
||||
save(cacheEntry: CacheEntry): Promise<void>
|
||||
findUnexpiredOneByKey(key: string): Promise<CacheEntry | null>
|
||||
removeByKey(key: string): Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { ContentType } from './ContentType'
|
||||
|
||||
describe('ContentType', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.Component)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('SN|Component')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, 0, 'FOOBAR']) {
|
||||
const valueOrError = ContentType.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should return a display name', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.FilesafeFileMetadata)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('FileSafe file')
|
||||
})
|
||||
|
||||
it('should return null for a display name if the value is null', () => {
|
||||
const valueOrError = ContentType.create(null)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toBeNull()
|
||||
})
|
||||
|
||||
it('should fallback to the value if the display name is not found', () => {
|
||||
const valueOrError = ContentType.create(ContentType.TYPES.EncryptedStorage)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().getDisplayName()).toEqual('SN|EncryptedStorage')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,77 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { ContentTypeProps } from './ContentTypeProps'
|
||||
|
||||
export class ContentType extends ValueObject<ContentTypeProps> {
|
||||
static readonly TYPES = {
|
||||
Any: '*',
|
||||
Item: 'SF|Item',
|
||||
KeySystemItemsKey: 'SN|KeySystemItemsKey',
|
||||
KeySystemRootKey: 'SN|KeySystemRootKey',
|
||||
TrustedContact: 'SN|TrustedContact',
|
||||
VaultListing: 'SN|VaultListing',
|
||||
RootKey: 'SN|RootKey|NoSync',
|
||||
ItemsKey: 'SN|ItemsKey',
|
||||
EncryptedStorage: 'SN|EncryptedStorage',
|
||||
Note: 'Note',
|
||||
Tag: 'Tag',
|
||||
SmartView: 'SN|SmartTag',
|
||||
Component: 'SN|Component',
|
||||
Editor: 'SN|Editor',
|
||||
ActionsExtension: 'Extension',
|
||||
UserPrefs: 'SN|UserPreferences',
|
||||
HistorySession: 'SN|HistorySession',
|
||||
Theme: 'SN|Theme',
|
||||
File: 'SN|File',
|
||||
FilesafeCredentials: 'SN|FileSafe|Credentials',
|
||||
FilesafeFileMetadata: 'SN|FileSafe|FileMetadata',
|
||||
FilesafeIntegration: 'SN|FileSafe|Integration',
|
||||
ExtensionRepo: 'SN|ExtensionRepo',
|
||||
}
|
||||
|
||||
private readonly displayNamesMap: Partial<Record<string, string>> = {
|
||||
[ContentType.TYPES.ActionsExtension]: 'action-based extension',
|
||||
[ContentType.TYPES.Component]: 'component',
|
||||
[ContentType.TYPES.Editor]: 'editor',
|
||||
[ContentType.TYPES.File]: 'file',
|
||||
[ContentType.TYPES.FilesafeCredentials]: 'FileSafe credential',
|
||||
[ContentType.TYPES.FilesafeFileMetadata]: 'FileSafe file',
|
||||
[ContentType.TYPES.FilesafeIntegration]: 'FileSafe integration',
|
||||
[ContentType.TYPES.ItemsKey]: 'encryption key',
|
||||
[ContentType.TYPES.Note]: 'note',
|
||||
[ContentType.TYPES.SmartView]: 'smart view',
|
||||
[ContentType.TYPES.Tag]: 'tag',
|
||||
[ContentType.TYPES.Theme]: 'theme',
|
||||
[ContentType.TYPES.UserPrefs]: 'user preferences',
|
||||
}
|
||||
|
||||
get value(): string | null {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ContentTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(type: string | null): Result<ContentType> {
|
||||
if (type === null) {
|
||||
return Result.ok<ContentType>(new ContentType({ value: null }))
|
||||
}
|
||||
|
||||
const isValidType = Object.values(this.TYPES).includes(type)
|
||||
if (!isValidType) {
|
||||
return Result.fail<ContentType>(`Invalid content type: ${type}`)
|
||||
} else {
|
||||
return Result.ok<ContentType>(new ContentType({ value: type }))
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayName(): string | null {
|
||||
if (!this.value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.displayNamesMap[this.value] || this.value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ContentTypeProps {
|
||||
value: string | null
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Dates } from './Dates'
|
||||
|
||||
describe('Dates', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Dates.create(new Date(1), new Date(2))
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().createdAt).toEqual(new Date(1))
|
||||
expect(valueOrError.getValue().updatedAt).toEqual(new Date(2))
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
let valueOrError = Dates.create(null as unknown as Date, '2' as unknown as Date)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = Dates.create(new Date(2), '2' as unknown as Date)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { DatesProps } from './DatesProps'
|
||||
|
||||
export class Dates extends ValueObject<DatesProps> {
|
||||
get createdAt(): Date {
|
||||
return this.props.createdAt
|
||||
}
|
||||
|
||||
get updatedAt(): Date {
|
||||
return this.props.updatedAt
|
||||
}
|
||||
|
||||
private constructor(props: DatesProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(createdAt: Date, updatedAt: Date): Result<Dates> {
|
||||
if (!(createdAt instanceof Date)) {
|
||||
return Result.fail<Dates>(`Could not create Dates. Creation date should be a date object, given: ${createdAt}`)
|
||||
}
|
||||
if (!(updatedAt instanceof Date)) {
|
||||
return Result.fail<Dates>(`Could not create Dates. Update date should be a date object, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Dates>(new Dates({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface DatesProps {
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Email } from './Email'
|
||||
|
||||
describe('Email', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Email.create('test@test.te')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('test@test.te')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Email.create('foobar')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not create an invalid type object', () => {
|
||||
const valueOrError = Email.create(undefined as unknown as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { EmailProps } from './EmailProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class Email extends ValueObject<EmailProps> {
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: EmailProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(email: string): Result<Email> {
|
||||
if (Validator.isString(email).isFailed()) {
|
||||
return Result.fail<Email>('Email must be a string')
|
||||
}
|
||||
|
||||
const trimmedAndLowerCasedEmail = email.trim().toLowerCase()
|
||||
|
||||
const emailValidation = Validator.isValidEmail(trimmedAndLowerCasedEmail)
|
||||
if (emailValidation.isFailed()) {
|
||||
return Result.fail<Email>(emailValidation.getError())
|
||||
}
|
||||
|
||||
return Result.ok<Email>(new Email({ value: trimmedAndLowerCasedEmail }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface EmailProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { RoleName } from './RoleName'
|
||||
|
||||
describe('RoleName', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = RoleName.create(RoleName.NAMES.ProUser)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('PRO_USER')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, null, 0, 'SOME_USER']) {
|
||||
const valueOrError = RoleName.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should say if a role has more power or equal power to another role', () => {
|
||||
const proUserRole = RoleName.create(RoleName.NAMES.ProUser).getValue()
|
||||
const plusUserRole = RoleName.create(RoleName.NAMES.PlusUser).getValue()
|
||||
const coreUser = RoleName.create(RoleName.NAMES.CoreUser).getValue()
|
||||
const internalTeamUser = RoleName.create(RoleName.NAMES.InternalTeamUser).getValue()
|
||||
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(internalTeamUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeTruthy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(proUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(plusUserRole)).toBeTruthy()
|
||||
expect(plusUserRole.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(internalTeamUser)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(proUserRole)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(plusUserRole)).toBeFalsy()
|
||||
expect(coreUser.hasMoreOrEqualPowerTo(coreUser)).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RoleNameProps } from './RoleNameProps'
|
||||
|
||||
export class RoleName extends ValueObject<RoleNameProps> {
|
||||
static readonly NAMES = {
|
||||
CoreUser: 'CORE_USER',
|
||||
PlusUser: 'PLUS_USER',
|
||||
ProUser: 'PRO_USER',
|
||||
InternalTeamUser: 'INTERNAL_TEAM_USER',
|
||||
VaultsUser: 'VAULTS_USER',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
hasMoreOrEqualPowerTo(roleName: RoleName): boolean {
|
||||
switch (this.value) {
|
||||
case RoleName.NAMES.InternalTeamUser:
|
||||
return true
|
||||
case RoleName.NAMES.ProUser:
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser, RoleName.NAMES.ProUser].includes(roleName.value)
|
||||
case RoleName.NAMES.PlusUser:
|
||||
return [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser].includes(roleName.value)
|
||||
case RoleName.NAMES.CoreUser:
|
||||
case RoleName.NAMES.VaultsUser:
|
||||
return [RoleName.NAMES.CoreUser].includes(roleName.value)
|
||||
/*istanbul ignore next*/
|
||||
default:
|
||||
throw new Error(`Invalid role name: ${this.value}`)
|
||||
}
|
||||
}
|
||||
|
||||
private constructor(props: RoleNameProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<RoleName> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<RoleName>(`Invalid role name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<RoleName>(new RoleName({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import { RoleName } from './RoleName'
|
||||
import { RoleNameCollection } from './RoleNameCollection'
|
||||
|
||||
describe('RoleNameCollection', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = RoleNameCollection.create([RoleName.NAMES.ProUser])
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value[0].value).toEqual('PRO_USER')
|
||||
})
|
||||
|
||||
it('should tell if collections are not equal', () => {
|
||||
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
|
||||
|
||||
let roles2 = RoleNameCollection.create([RoleName.NAMES.ProUser, RoleName.NAMES.CoreUser]).getValue()
|
||||
|
||||
let valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
|
||||
|
||||
roles2 = RoleNameCollection.create([
|
||||
RoleName.NAMES.ProUser,
|
||||
RoleName.NAMES.PlusUser,
|
||||
RoleName.NAMES.CoreUser,
|
||||
]).getValue()
|
||||
|
||||
valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().equals(roles2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should tell if collections are equal', () => {
|
||||
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
|
||||
|
||||
const roles2 = RoleNameCollection.create([RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]).getValue()
|
||||
|
||||
const valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().equals(roles2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should tell if collection includes element', () => {
|
||||
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
|
||||
|
||||
const valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should tell if collection does not includes element', () => {
|
||||
const roles1 = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
|
||||
|
||||
const valueOrError = RoleNameCollection.create(roles1)
|
||||
expect(valueOrError.getValue().includes(RoleName.create(RoleName.NAMES.CoreUser).getValue())).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should tell if collection has a role with more or equal power to', () => {
|
||||
let roles = [RoleName.NAMES.VaultsUser]
|
||||
let valueOrError = RoleNameCollection.create(roles)
|
||||
let roleNames = valueOrError.getValue()
|
||||
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
|
||||
).toBeFalsy()
|
||||
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
|
||||
).toBeTruthy()
|
||||
|
||||
roles = [RoleName.NAMES.CoreUser]
|
||||
valueOrError = RoleNameCollection.create(roles)
|
||||
roleNames = valueOrError.getValue()
|
||||
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
|
||||
).toBeFalsy()
|
||||
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
|
||||
).toBeTruthy()
|
||||
|
||||
roles = [RoleName.NAMES.CoreUser, RoleName.NAMES.PlusUser]
|
||||
valueOrError = RoleNameCollection.create(roles)
|
||||
roleNames = valueOrError.getValue()
|
||||
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
|
||||
).toBeTruthy()
|
||||
expect(roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue())).toBeFalsy()
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
|
||||
).toBeTruthy()
|
||||
|
||||
roles = [RoleName.NAMES.ProUser, RoleName.NAMES.PlusUser]
|
||||
valueOrError = RoleNameCollection.create(roles)
|
||||
roleNames = valueOrError.getValue()
|
||||
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.PlusUser).getValue()),
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.ProUser).getValue()),
|
||||
).toBeTruthy()
|
||||
expect(
|
||||
roleNames.hasARoleNameWithMoreOrEqualPowerTo(RoleName.create(RoleName.NAMES.CoreUser).getValue()),
|
||||
).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should fail to create a collection if a role name is invalid', () => {
|
||||
const valueOrError = RoleNameCollection.create(['invalid-role-name'])
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
expect(valueOrError.getError()).toEqual('Invalid role name: invalid-role-name')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,61 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { RoleNameCollectionProps } from './RoleNameCollectionProps'
|
||||
import { RoleName } from './RoleName'
|
||||
|
||||
export class RoleNameCollection extends ValueObject<RoleNameCollectionProps> {
|
||||
get value(): RoleName[] {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
includes(roleName: RoleName): boolean {
|
||||
for (const existingRoleName of this.props.value) {
|
||||
if (existingRoleName.value === roleName.value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
hasARoleNameWithMoreOrEqualPowerTo(roleName: RoleName): boolean {
|
||||
for (const existingRoleName of this.props.value) {
|
||||
if (existingRoleName.hasMoreOrEqualPowerTo(roleName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override equals(roleNameCollection: RoleNameCollection): boolean {
|
||||
if (this.props.value.length !== roleNameCollection.value.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const roleName of roleNameCollection.value) {
|
||||
if (!this.includes(roleName)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private constructor(props: RoleNameCollectionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(roleNameStrings: string[]): Result<RoleNameCollection> {
|
||||
const roleNames: RoleName[] = []
|
||||
for (const roleNameString of roleNameStrings) {
|
||||
const roleNameOrError = RoleName.create(roleNameString)
|
||||
if (roleNameOrError.isFailed()) {
|
||||
return Result.fail<RoleNameCollection>(roleNameOrError.getError())
|
||||
}
|
||||
roleNames.push(roleNameOrError.getValue())
|
||||
}
|
||||
|
||||
return Result.ok<RoleNameCollection>(new RoleNameCollection({ value: roleNames }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { RoleName } from './RoleName'
|
||||
|
||||
export interface RoleNameCollectionProps {
|
||||
value: RoleName[]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface RoleNameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Timestamps } from './Timestamps'
|
||||
|
||||
describe('Timestamps', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Timestamps.create(123, 234)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().createdAt).toEqual(123)
|
||||
expect(valueOrError.getValue().updatedAt).toEqual(234)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Timestamps.create('' as unknown as number, 123)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Timestamps.create(123, '' as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { TimestampsProps } from './TimestampsProps'
|
||||
|
||||
export class Timestamps extends ValueObject<TimestampsProps> {
|
||||
get createdAt(): number {
|
||||
return this.props.createdAt
|
||||
}
|
||||
|
||||
get updatedAt(): number {
|
||||
return this.props.updatedAt
|
||||
}
|
||||
|
||||
private constructor(props: TimestampsProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(createdAt: number, updatedAt: number): Result<Timestamps> {
|
||||
if (isNaN(createdAt) || typeof createdAt !== 'number') {
|
||||
return Result.fail<Timestamps>(
|
||||
`Could not create Timestamps. Creation date should be a number, given: ${createdAt}`,
|
||||
)
|
||||
}
|
||||
if (isNaN(updatedAt) || typeof updatedAt !== 'number') {
|
||||
return Result.fail<Timestamps>(`Could not create Timestamps. Update date should be a number, given: ${updatedAt}`)
|
||||
}
|
||||
|
||||
return Result.ok<Timestamps>(new Timestamps({ createdAt, updatedAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface TimestampsProps {
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import { Username } from './Username'
|
||||
|
||||
describe('Username', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Username.create('test@test.te')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('test@test.te')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Username.create('')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not create an invalid type object', () => {
|
||||
const valueOrError = Username.create(undefined as unknown as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should indicate if the username is potentially a vault account', () => {
|
||||
const value = Username.create('a75a31ce95365904ef0e0a8e6cefc1f5e99adfef81bbdb6d4499eeb10ae0ff67').getValue()
|
||||
|
||||
expect(value.isPotentiallyAPrivateUsernameAccount()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should indicate if the user is not a vault account', () => {
|
||||
const value = Username.create('test@test.te').getValue()
|
||||
|
||||
expect(value.isPotentiallyAPrivateUsernameAccount()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UsernameProps } from './UsernameProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class Username extends ValueObject<UsernameProps> {
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: UsernameProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(username: string): Result<Username> {
|
||||
if (Validator.isString(username).isFailed()) {
|
||||
return Result.fail<Username>('Username must be a string')
|
||||
}
|
||||
|
||||
const trimmedAndLowerCasedUsername = username.trim().toLowerCase()
|
||||
|
||||
if (Validator.isNotEmpty(trimmedAndLowerCasedUsername).isFailed()) {
|
||||
return Result.fail<Username>('Username cannot be empty')
|
||||
}
|
||||
|
||||
return Result.ok<Username>(new Username({ value: trimmedAndLowerCasedUsername }))
|
||||
}
|
||||
|
||||
isPotentiallyAPrivateUsernameAccount(): boolean {
|
||||
return this.value.length === 64 && !this.value.includes('@')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface UsernameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Uuid } from './Uuid'
|
||||
|
||||
describe('Uuid', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('84c0f8e8-544a-4c7e-9adf-26209303bc1d')
|
||||
})
|
||||
|
||||
it('should create a value object on upper case', () => {
|
||||
const valueOrError = Uuid.create('00B57455-B563-4B50-A2AA-B19762102219')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('00B57455-B563-4B50-A2AA-B19762102219')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = Uuid.create('1-2-3')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check equality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
const uuid2 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1e').getValue()
|
||||
|
||||
expect(uuid1.equals(uuid2)).toBeFalsy()
|
||||
})
|
||||
|
||||
it('should check inequality between two value objects of different types', () => {
|
||||
const uuid1 = Uuid.create('84c0f8e8-544a-4c7e-9adf-26209303bc1d').getValue()
|
||||
|
||||
expect(uuid1.equals(null as unknown as Uuid)).toBeFalsy()
|
||||
expect(uuid1.equals(undefined as unknown as Uuid)).toBeFalsy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UuidProps } from './UuidProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class Uuid extends ValueObject<UuidProps> {
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: UuidProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(uuid: string): Result<Uuid> {
|
||||
const validUuidOrError = Validator.isValidUuid(uuid)
|
||||
if (validUuidOrError.isFailed()) {
|
||||
return Result.fail<Uuid>(validUuidOrError.getError())
|
||||
} else {
|
||||
return Result.ok<Uuid>(new Uuid({ value: uuid }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface UuidProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { Change } from './Change'
|
||||
import { Entity } from './Entity'
|
||||
|
||||
export abstract class Aggregate<T> extends Entity<T> {
|
||||
private changesOnAggregateRoot: Change[] = []
|
||||
|
||||
addChange(change: Change): void {
|
||||
this.changesOnAggregateRoot.push(change)
|
||||
}
|
||||
|
||||
flushChanges(): void {
|
||||
this.changesOnAggregateRoot = []
|
||||
}
|
||||
|
||||
getChanges(): Change[] {
|
||||
return this.changesOnAggregateRoot
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { ChangeProps } from './ChangeProps'
|
||||
import { Result } from './Result'
|
||||
|
||||
export class Change {
|
||||
static readonly TYPES = {
|
||||
Add: 'add',
|
||||
Remove: 'remove',
|
||||
Modify: 'modify',
|
||||
}
|
||||
|
||||
public readonly props: ChangeProps
|
||||
|
||||
constructor(props: ChangeProps) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
static create(props: ChangeProps): Result<Change> {
|
||||
if (!Object.values(Change.TYPES).includes(props.changeType)) {
|
||||
return Result.fail('Invalid change type')
|
||||
}
|
||||
|
||||
return Result.ok(new Change(props))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { Entity } from './Entity'
|
||||
|
||||
export interface ChangeProps {
|
||||
aggregateRootUuid: string
|
||||
changeType: string
|
||||
changeData: Entity<unknown>
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { UniqueEntityId } from './UniqueEntityId'
|
||||
|
||||
export abstract class Entity<T> {
|
||||
protected readonly _id: UniqueEntityId
|
||||
|
||||
constructor(
|
||||
public readonly props: T,
|
||||
id?: UniqueEntityId,
|
||||
) {
|
||||
this._id = id ? id : new UniqueEntityId()
|
||||
}
|
||||
|
||||
get id(): UniqueEntityId {
|
||||
return this._id
|
||||
}
|
||||
|
||||
public equals(object?: Entity<T>): boolean {
|
||||
if (object == null || object == undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (this === object) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!(object instanceof Entity)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this._id.equals(object._id)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export class Id<T> {
|
||||
constructor(private value: T) {}
|
||||
|
||||
equals(id?: Id<T>): boolean {
|
||||
if (id === null || id === undefined) {
|
||||
return false
|
||||
}
|
||||
if (!(id instanceof this.constructor)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return id.toValue() === this.value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return String(this.value)
|
||||
}
|
||||
|
||||
toValue(): T {
|
||||
return this.value
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
export class Result<T> {
|
||||
constructor(
|
||||
private isSuccess: boolean,
|
||||
private error?: string,
|
||||
private value?: T,
|
||||
) {
|
||||
Object.freeze(this)
|
||||
}
|
||||
|
||||
isFailed(): boolean {
|
||||
return !this.isSuccess
|
||||
}
|
||||
|
||||
getValue(): T {
|
||||
if (!this.isSuccess) {
|
||||
throw new Error(`Cannot get value of an unsuccessfull result: ${this.error}`)
|
||||
}
|
||||
|
||||
return this.value as T
|
||||
}
|
||||
|
||||
getError(): string {
|
||||
if (this.isSuccess || this.error === undefined) {
|
||||
throw new Error('Cannot get an error of a successfull result')
|
||||
}
|
||||
|
||||
return this.error
|
||||
}
|
||||
|
||||
static ok<U>(value?: U): Result<U> {
|
||||
return new Result<U>(true, undefined, value)
|
||||
}
|
||||
|
||||
static fail<U>(error: string): Result<U> {
|
||||
return new Result<U>(false, error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/* istanbul ignore file */
|
||||
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { Id } from './Id'
|
||||
|
||||
export class UniqueEntityId extends Id<string | number> {
|
||||
constructor(id?: string | number) {
|
||||
super(id ? id : uuid())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
import { Validator } from './Validator'
|
||||
|
||||
describe('Validator', () => {
|
||||
const validUuids = [
|
||||
'2221101c-1da9-4d2b-9b32-b8be2a8d1c82',
|
||||
'c08f2f29-a74b-42b4-aefd-98af9832391c',
|
||||
'b453fa64-1493-443b-b5bb-bca7b9c696c7',
|
||||
'fa7350b3-77cf-8c0c-40b2-6046b13254fe',
|
||||
]
|
||||
|
||||
const invalidUuids = [
|
||||
123,
|
||||
'someone@127.0.0.1',
|
||||
'',
|
||||
null,
|
||||
'b453fa64-1493-443b-b5bb-ca7b9c696c7',
|
||||
'c08f*f29-a74b-42b4-aefd-98af9832391c',
|
||||
'c08f*f29-a74b-42b4-aefd-98af9832391c',
|
||||
'../../escaped.sh',
|
||||
]
|
||||
|
||||
const validEmails = [
|
||||
'something@something.com',
|
||||
'someone@localhost.localdomain',
|
||||
'a/b@domain.com',
|
||||
'{}@domain.com',
|
||||
'karol+test@standardnotes.com',
|
||||
"m*'!%@something.sa",
|
||||
'tu!!7n7.ad##0!!!@company.ca',
|
||||
'%@com.com',
|
||||
"!#$%&'*+/=?^_`{|}~.-@com.com",
|
||||
'someone@do-ma-in.com',
|
||||
'""testlah""@example.com',
|
||||
]
|
||||
|
||||
const invalidEmails = [
|
||||
'someone@127.0.0.1',
|
||||
'a@b.b',
|
||||
'',
|
||||
null,
|
||||
'.wooly@example.com',
|
||||
'wo..oly@example.com',
|
||||
'somebody@example',
|
||||
'a@p.com',
|
||||
]
|
||||
|
||||
it('should validate proper uuids', () => {
|
||||
for (const validUuid of validUuids) {
|
||||
expect(Validator.isValidUuid(validUuid).isFailed()).toBeFalsy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should not validate invalid uuids', () => {
|
||||
for (const invalidUuid of invalidUuids) {
|
||||
expect(Validator.isValidUuid(invalidUuid as string).isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should validate proper emails', () => {
|
||||
for (const validEmail of validEmails) {
|
||||
expect(Validator.isValidEmail(validEmail).isFailed()).toBeFalsy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should not validate invalid emails', () => {
|
||||
for (const invalidEmail of invalidEmails) {
|
||||
expect(Validator.isValidEmail(invalidEmail as string).isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should validate value if not empty', () => {
|
||||
for (const value of [1, 'foobar', {}, 0]) {
|
||||
expect(Validator.isNotEmpty(value).isFailed()).toBeFalsy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should not validate value if empty', () => {
|
||||
for (const value of [null, undefined, '', []]) {
|
||||
expect(Validator.isNotEmpty(value).isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
describe('is not empty string', () => {
|
||||
it('should not validate invalid string', () => {
|
||||
expect(Validator.isNotEmptyString(123 as unknown as string).isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should not validate an empty string', () => {
|
||||
expect(Validator.isNotEmptyString('').isFailed()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should validate a string', () => {
|
||||
expect(Validator.isNotEmptyString('foo').isFailed()).toBeFalsy()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Result } from './Result'
|
||||
|
||||
export class Validator {
|
||||
private static readonly UUID_ANY_VERSION_AND_VARIANT_REGEX =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
||||
private static readonly EMAIL_REGEX =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
static isValidUuid(value: string): Result<string> {
|
||||
const matchesUuidRegex = String(value).toLowerCase().match(Validator.UUID_ANY_VERSION_AND_VARIANT_REGEX) !== null
|
||||
if (matchesUuidRegex) {
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
return Result.fail(`Given value is not a valid uuid: ${value}`)
|
||||
}
|
||||
|
||||
static isValidEmail(value: string): Result<string> {
|
||||
const matchesUuidRegex = String(value).toLowerCase().match(Validator.EMAIL_REGEX) !== null
|
||||
if (matchesUuidRegex) {
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
return Result.fail(`Given value is not a valid email address: ${value}`)
|
||||
}
|
||||
|
||||
static isString(value: unknown): Result<string> {
|
||||
if (typeof value === 'string') {
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
return Result.fail(`Given value is not a string: ${typeof value}`)
|
||||
}
|
||||
|
||||
static isNotEmpty(value: unknown): Result<string> {
|
||||
if (value instanceof Array && value.length === 0) {
|
||||
return Result.fail(`Given value is empty: ${value}`)
|
||||
}
|
||||
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return Result.fail(`Given value is empty: ${value}`)
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
static isNotEmptyString(value: unknown): Result<string> {
|
||||
const isStringResult = Validator.isString(value)
|
||||
if (isStringResult.isFailed()) {
|
||||
return isStringResult
|
||||
}
|
||||
|
||||
const isNotEmptyResult = Validator.isNotEmpty(value)
|
||||
if (isNotEmptyResult.isFailed()) {
|
||||
return isNotEmptyResult
|
||||
}
|
||||
|
||||
return Result.ok()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* istanbul ignore file */
|
||||
import { ValueObjectProps } from './ValueObjectProps'
|
||||
|
||||
export abstract class ValueObject<T extends ValueObjectProps> {
|
||||
public readonly props: T
|
||||
|
||||
constructor(props: T) {
|
||||
this.props = Object.freeze(props)
|
||||
}
|
||||
|
||||
public equals(vo?: ValueObject<T>): boolean {
|
||||
if (vo === null || vo === undefined) {
|
||||
return false
|
||||
}
|
||||
|
||||
return JSON.stringify(this.props) === JSON.stringify(vo.props)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ValueObjectProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[index: string]: any
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ControllerContainerInterface } from './ControllerContainerInterface'
|
||||
|
||||
export class ControllerContainer implements ControllerContainerInterface {
|
||||
private controllerMethodsMap: Map<string, (request: never, response: never) => Promise<unknown>> = new Map()
|
||||
|
||||
register(methodIdentifier: string, binding: (request: never, response: never) => Promise<unknown>): void {
|
||||
this.controllerMethodsMap.set(methodIdentifier, binding)
|
||||
}
|
||||
|
||||
get(methodIdentifier: string): ((request: never, response: never) => Promise<unknown>) | undefined {
|
||||
return this.controllerMethodsMap.get(methodIdentifier)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface ControllerContainerInterface {
|
||||
register(methodIdentifier: string, binding: (request: never, response: never) => Promise<unknown>): void
|
||||
get(methodIdentifier: string): ((request: never, response: never) => Promise<unknown>) | undefined
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { EmailLevel } from './EmailLevel'
|
||||
|
||||
describe('EmailLevel', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = EmailLevel.create(EmailLevel.LEVELS.SignIn)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('SIGN_IN')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, null, 0, 'FOOBAR']) {
|
||||
const valueOrError = EmailLevel.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { EmailLevelProps } from './EmailLevelProps'
|
||||
|
||||
export class EmailLevel extends ValueObject<EmailLevelProps> {
|
||||
static readonly LEVELS = {
|
||||
System: 'SYSTEM',
|
||||
SignIn: 'SIGN_IN',
|
||||
Marketing: 'MARKETING',
|
||||
FailedCloudBackup: 'FAILED_CLOUD_BACKUP',
|
||||
FailedEmailBackup: 'FAILED_EMAIL_BACKUP',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: EmailLevelProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<EmailLevel> {
|
||||
const isValidName = Object.values(this.LEVELS).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<EmailLevel>(`Invalid subscription rejection level: ${name}`)
|
||||
} else {
|
||||
return Result.ok<EmailLevel>(new EmailLevel({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface EmailLevelProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
export abstract class AbstractEnv {
|
||||
protected env?: { [key: string]: string } = {}
|
||||
protected overrides: { [key: string]: string }
|
||||
|
||||
constructor(overrides: { [key: string]: string } = {}) {
|
||||
this.overrides = overrides
|
||||
}
|
||||
|
||||
abstract load(): void
|
||||
|
||||
get(key: string, optional = false): string {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
if (this.overrides[key]) {
|
||||
return this.overrides[key]
|
||||
}
|
||||
|
||||
if (!process.env[key] && !optional) {
|
||||
throw new Error(`Environment variable ${key} not set`)
|
||||
}
|
||||
|
||||
return <string>process.env[key]
|
||||
}
|
||||
|
||||
getAll(): { [key: string]: string } {
|
||||
if (!this.env) {
|
||||
this.load()
|
||||
}
|
||||
|
||||
return this.env as { [key: string]: string }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface MapperInterface<T, U> {
|
||||
toDomain(projection: U): T
|
||||
toProjection(domain: T): U
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
|
||||
import { NotificationPayloadProps } from './NotificationPayloadProps'
|
||||
import { NotificationType } from './NotificationType'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
|
||||
|
||||
export class NotificationPayload extends ValueObject<NotificationPayloadProps> {
|
||||
private constructor(props: NotificationPayloadProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
override toString(): string {
|
||||
return JSON.stringify({
|
||||
version: this.props.version,
|
||||
type: this.props.type.value,
|
||||
primaryIdentifier: this.props.primaryIdentifier.value,
|
||||
primaryIndentifierType: this.props.primaryIndentifierType.value,
|
||||
secondaryIdentifier: this.props.secondaryIdentifier?.value,
|
||||
secondaryIdentifierType: this.props.secondaryIdentifierType?.value,
|
||||
})
|
||||
}
|
||||
|
||||
static createFromString(jsonPayload: string): Result<NotificationPayload> {
|
||||
try {
|
||||
const props = JSON.parse(jsonPayload)
|
||||
|
||||
const typeOrError = NotificationType.create(props.type)
|
||||
if (typeOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(typeOrError.getError())
|
||||
}
|
||||
const type = typeOrError.getValue()
|
||||
|
||||
const primaryIdentifierOrError = Uuid.create(props.primaryIdentifier)
|
||||
if (primaryIdentifierOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(primaryIdentifierOrError.getError())
|
||||
}
|
||||
const primaryIdentifier = primaryIdentifierOrError.getValue()
|
||||
|
||||
const primaryIndentifierTypeOrError = NotificationPayloadIdentifierType.create(props.primaryIndentifierType)
|
||||
if (primaryIndentifierTypeOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(primaryIndentifierTypeOrError.getError())
|
||||
}
|
||||
const primaryIndentifierType = primaryIndentifierTypeOrError.getValue()
|
||||
|
||||
let secondaryIdentifier: Uuid | undefined
|
||||
if (props.secondaryIdentifier) {
|
||||
const secondaryIdentifierOrError = Uuid.create(props.secondaryIdentifier)
|
||||
if (secondaryIdentifierOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(secondaryIdentifierOrError.getError())
|
||||
}
|
||||
secondaryIdentifier = secondaryIdentifierOrError.getValue()
|
||||
}
|
||||
|
||||
let secondaryIdentifierType: NotificationPayloadIdentifierType | undefined
|
||||
if (props.secondaryIdentifierType) {
|
||||
const secondaryIdentifierTypeOrError = NotificationPayloadIdentifierType.create(props.secondaryIdentifierType)
|
||||
if (secondaryIdentifierTypeOrError.isFailed()) {
|
||||
return Result.fail<NotificationPayload>(secondaryIdentifierTypeOrError.getError())
|
||||
}
|
||||
secondaryIdentifierType = secondaryIdentifierTypeOrError.getValue()
|
||||
}
|
||||
|
||||
return NotificationPayload.create({
|
||||
version: props.version,
|
||||
type,
|
||||
primaryIdentifier,
|
||||
primaryIndentifierType,
|
||||
secondaryIdentifier,
|
||||
secondaryIdentifierType,
|
||||
})
|
||||
} catch (error) {
|
||||
return Result.fail<NotificationPayload>((error as Error).message)
|
||||
}
|
||||
}
|
||||
|
||||
static create(props: NotificationPayloadProps): Result<NotificationPayload> {
|
||||
if (
|
||||
props.secondaryIdentifier === undefined &&
|
||||
props.type.equals(NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved).getValue())
|
||||
) {
|
||||
return Result.fail<NotificationPayload>(
|
||||
`Item uuid is required for ${NotificationType.TYPES.SharedVaultItemRemoved} notification type`,
|
||||
)
|
||||
}
|
||||
|
||||
return Result.ok<NotificationPayload>(new NotificationPayload(props))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { NotificationPayloadIdentifierTypeProps } from './NotificationPayloadIdentifierTypeProps'
|
||||
|
||||
export class NotificationPayloadIdentifierType extends ValueObject<NotificationPayloadIdentifierTypeProps> {
|
||||
static readonly TYPES = {
|
||||
SharedVaultUuid: 'shared_vault_uuid',
|
||||
UserUuid: 'user_uuid',
|
||||
SharedVaultInviteUuid: 'shared_vault_invite_uuid',
|
||||
ItemUuid: 'item_uuid',
|
||||
}
|
||||
|
||||
private constructor(props: NotificationPayloadIdentifierTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
static create(type: string): Result<NotificationPayloadIdentifierType> {
|
||||
if (!Object.values(this.TYPES).includes(type)) {
|
||||
return Result.fail<NotificationPayloadIdentifierType>(`Invalid notification payload identifier type: ${type}`)
|
||||
}
|
||||
|
||||
return Result.ok<NotificationPayloadIdentifierType>(new NotificationPayloadIdentifierType({ value: type }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NotificationPayloadIdentifierTypeProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { NotificationPayloadIdentifierType } from './NotificationPayloadIdentifierType'
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
export interface NotificationPayloadProps {
|
||||
type: NotificationType
|
||||
primaryIdentifier: Uuid
|
||||
primaryIndentifierType: NotificationPayloadIdentifierType
|
||||
secondaryIdentifier?: Uuid
|
||||
secondaryIdentifierType?: NotificationPayloadIdentifierType
|
||||
version: string
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { NotificationType } from './NotificationType'
|
||||
|
||||
describe('NotificationType', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = NotificationType.create(NotificationType.TYPES.SharedVaultItemRemoved)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('shared_vault_item_removed')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = NotificationType.create('TEST')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { NotificationTypeProps } from './NotificationTypeProps'
|
||||
|
||||
export class NotificationType extends ValueObject<NotificationTypeProps> {
|
||||
static readonly TYPES = {
|
||||
SharedVaultItemRemoved: 'shared_vault_item_removed',
|
||||
SelfRemovedFromSharedVault: 'self_removed_from_shared_vault',
|
||||
UserRemovedFromSharedVault: 'user_removed_from_shared_vault',
|
||||
UserDesignatedAsSurvivor: 'user_designated_as_survivor',
|
||||
UserAddedToSharedVault: 'user_added_to_shared_vault',
|
||||
SharedVaultInviteCanceled: 'shared_vault_invite_canceled',
|
||||
SharedVaultFileUploaded: 'shared_vault_file_uploaded',
|
||||
SharedVaultFileRemoved: 'shared_vault_file_removed',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: NotificationTypeProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(notificationType: string): Result<NotificationType> {
|
||||
const isValidType = Object.values(this.TYPES).includes(notificationType)
|
||||
if (!isValidType) {
|
||||
return Result.fail<NotificationType>(`Invalid notification type: ${notificationType}`)
|
||||
} else {
|
||||
return Result.ok<NotificationType>(new NotificationType({ value: notificationType }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface NotificationTypeProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Transform } from 'stream'
|
||||
|
||||
export interface ServiceConfiguration {
|
||||
logger?: Transform
|
||||
environmentOverrides?: { [name: string]: string }
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { ServiceContainerInterface } from './ServiceContainerInterface'
|
||||
import { ServiceIdentifier } from './ServiceIdentifier'
|
||||
import { ServiceInterface } from './ServiceInterface'
|
||||
|
||||
export class ServiceContainer implements ServiceContainerInterface {
|
||||
private serviceMap: Map<string, unknown> = new Map()
|
||||
|
||||
register(serviceIdentifer: ServiceIdentifier, service: ServiceInterface): void {
|
||||
this.serviceMap.set(serviceIdentifer.value, service)
|
||||
}
|
||||
|
||||
get(serviceIdentifer: ServiceIdentifier): ServiceInterface | undefined {
|
||||
return this.serviceMap.get(serviceIdentifer.value) as ServiceInterface
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { ServiceIdentifier } from './ServiceIdentifier'
|
||||
import { ServiceInterface } from './ServiceInterface'
|
||||
|
||||
export interface ServiceContainerInterface {
|
||||
register(serviceIdentifer: ServiceIdentifier, service: ServiceInterface): void
|
||||
get(serviceIdentifer: ServiceIdentifier): ServiceInterface | undefined
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ServiceIdentifier } from './ServiceIdentifier'
|
||||
|
||||
describe('ServiceIdentifier', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('Auth')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, null, 0, 'SOME_SERVICE']) {
|
||||
const valueOrError = ServiceIdentifier.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { ServiceIdentifierProps } from './ServiceIdentifierProps'
|
||||
|
||||
export class ServiceIdentifier extends ValueObject<ServiceIdentifierProps> {
|
||||
static readonly NAMES = {
|
||||
AnalyticsWorker: 'AnalyticsWorker',
|
||||
AnalyticsScheduledTask: 'AnalyticsScheduledTask',
|
||||
ApiGateway: 'ApiGateway',
|
||||
Auth: 'Auth',
|
||||
AuthWorker: 'AuthWorker',
|
||||
AuthScheduledTask: 'AuthScheduledTask',
|
||||
SyncingServer: 'SyncingServer',
|
||||
SyncingServerWorker: 'SyncingServerWorker',
|
||||
Revisions: 'Revisions',
|
||||
RevisionsWorker: 'RevisionsWorker',
|
||||
Files: 'Files',
|
||||
FilesWorker: 'FilesWorker',
|
||||
SchedulerWorker: 'SchedulerWorker',
|
||||
SchedulerScheduledTask: 'SchedulerScheduledTask',
|
||||
Email: 'Email',
|
||||
EmailWorker: 'EmailWorker',
|
||||
EmailBounceProcessor: 'EmailBounceProcessor',
|
||||
EmailScheduledTask: 'EmailScheduledTask',
|
||||
Websockets: 'Websockets',
|
||||
WebsocketsWorker: 'WebsocketsWorker',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: ServiceIdentifierProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<ServiceIdentifier> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<ServiceIdentifier>(`Invalid subscription plan name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<ServiceIdentifier>(new ServiceIdentifier({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ServiceIdentifierProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { ServiceConfiguration } from './ServiceConfiguration'
|
||||
import { ServiceIdentifier } from './ServiceIdentifier'
|
||||
|
||||
export interface ServiceInterface {
|
||||
getContainer(configuration?: ServiceConfiguration): Promise<unknown>
|
||||
getId(): ServiceIdentifier
|
||||
handleRequest(request: never, response: never, endpointOrMethodIdentifier: string): Promise<unknown>
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { SettingNameProps } from './SettingNameProps'
|
||||
|
||||
export class SettingName extends ValueObject<SettingNameProps> {
|
||||
static readonly NAMES = {
|
||||
MfaSecret: 'MFA_SECRET',
|
||||
ExtensionKey: 'EXTENSION_KEY',
|
||||
EmailBackupFrequency: 'EMAIL_BACKUP_FREQUENCY',
|
||||
DropboxBackupFrequency: 'DROPBOX_BACKUP_FREQUENCY',
|
||||
DropboxBackupToken: 'DROPBOX_BACKUP_TOKEN',
|
||||
OneDriveBackupFrequency: 'ONE_DRIVE_BACKUP_FREQUENCY',
|
||||
OneDriveBackupToken: 'ONE_DRIVE_BACKUP_TOKEN',
|
||||
GoogleDriveBackupFrequency: 'GOOGLE_DRIVE_BACKUP_FREQUENCY',
|
||||
GoogleDriveBackupToken: 'GOOGLE_DRIVE_BACKUP_TOKEN',
|
||||
MuteFailedBackupsEmails: 'MUTE_FAILED_BACKUPS_EMAILS',
|
||||
MuteFailedCloudBackupsEmails: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
|
||||
MuteSignInEmails: 'MUTE_SIGN_IN_EMAILS',
|
||||
MuteMarketingEmails: 'MUTE_MARKETING_EMAILS',
|
||||
ListedAuthorSecrets: 'LISTED_AUTHOR_SECRETS',
|
||||
LogSessionUserAgent: 'LOG_SESSION_USER_AGENT',
|
||||
RecoveryCodes: 'RECOVERY_CODES',
|
||||
FileUploadBytesLimit: 'FILE_UPLOAD_BYTES_LIMIT',
|
||||
FileUploadBytesUsed: 'FILE_UPLOAD_BYTES_USED',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
isSensitive(): boolean {
|
||||
return [SettingName.NAMES.MfaSecret, SettingName.NAMES.ExtensionKey].includes(this.props.value)
|
||||
}
|
||||
|
||||
isASubscriptionSetting(): boolean {
|
||||
return [
|
||||
SettingName.NAMES.FileUploadBytesLimit,
|
||||
SettingName.NAMES.FileUploadBytesUsed,
|
||||
SettingName.NAMES.MuteSignInEmails,
|
||||
].includes(this.props.value)
|
||||
}
|
||||
|
||||
isARegularOnlySubscriptionSetting(): boolean {
|
||||
return [SettingName.NAMES.FileUploadBytesLimit, SettingName.NAMES.FileUploadBytesUsed].includes(this.props.value)
|
||||
}
|
||||
|
||||
isASharedAndRegularOnlySubscriptionSetting(): boolean {
|
||||
return [SettingName.NAMES.MuteSignInEmails].includes(this.props.value)
|
||||
}
|
||||
|
||||
private constructor(props: SettingNameProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<SettingName> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<SettingName>(`Invalid setting name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<SettingName>(new SettingName({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface SettingNameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
|
||||
import { SharedVaultUser } from './SharedVaultUser'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { Timestamps } from '../Common/Timestamps'
|
||||
|
||||
describe('SharedVaultUser', () => {
|
||||
it('should create an entity', () => {
|
||||
const entityOrError = SharedVaultUser.create({
|
||||
permission: SharedVaultUserPermission.create('read').getValue(),
|
||||
sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
timestamps: Timestamps.create(123456789, 123456789).getValue(),
|
||||
userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(),
|
||||
isDesignatedSurvivor: false,
|
||||
})
|
||||
|
||||
expect(entityOrError.isFailed()).toBeFalsy()
|
||||
expect(entityOrError.getValue().id).not.toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Entity } from '../Core/Entity'
|
||||
import { Result } from '../Core/Result'
|
||||
import { UniqueEntityId } from '../Core/UniqueEntityId'
|
||||
import { SharedVaultUserProps } from './SharedVaultUserProps'
|
||||
|
||||
export class SharedVaultUser extends Entity<SharedVaultUserProps> {
|
||||
private constructor(props: SharedVaultUserProps, id?: UniqueEntityId) {
|
||||
super(props, id)
|
||||
}
|
||||
|
||||
static create(props: SharedVaultUserProps, id?: UniqueEntityId): Result<SharedVaultUser> {
|
||||
return Result.ok<SharedVaultUser>(new SharedVaultUser(props, id))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
|
||||
|
||||
describe('SharedVaultUserPermission', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = SharedVaultUserPermission.create('read')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('read')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = SharedVaultUserPermission.create('TEST')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Result } from '../Core/Result'
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
|
||||
import { SharedVaultUserPermissionProps } from './SharedVaultUserPermissionProps'
|
||||
|
||||
export class SharedVaultUserPermission extends ValueObject<SharedVaultUserPermissionProps> {
|
||||
static readonly PERMISSIONS = {
|
||||
Read: 'read',
|
||||
Write: 'write',
|
||||
Admin: 'admin',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: SharedVaultUserPermissionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(sharedVaultUserPermission: string): Result<SharedVaultUserPermission> {
|
||||
const isValidPermission = Object.values(this.PERMISSIONS).includes(sharedVaultUserPermission)
|
||||
if (!isValidPermission) {
|
||||
return Result.fail<SharedVaultUserPermission>(`Invalid shared vault user permission ${sharedVaultUserPermission}`)
|
||||
} else {
|
||||
return Result.ok<SharedVaultUserPermission>(new SharedVaultUserPermission({ value: sharedVaultUserPermission }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface SharedVaultUserPermissionProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Timestamps } from '../Common/Timestamps'
|
||||
import { Uuid } from '../Common/Uuid'
|
||||
import { SharedVaultUserPermission } from './SharedVaultUserPermission'
|
||||
|
||||
export interface SharedVaultUserProps {
|
||||
sharedVaultUuid: Uuid
|
||||
userUuid: Uuid
|
||||
permission: SharedVaultUserPermission
|
||||
isDesignatedSurvivor: boolean
|
||||
timestamps: Timestamps
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { SubscriptionPlanName } from './SubscriptionPlanName'
|
||||
|
||||
describe('SubscriptionPlanName', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = SubscriptionPlanName.create('PRO_PLAN')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('PRO_PLAN')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
for (const value of ['', undefined, null, 0, 'SOME_PLAN']) {
|
||||
const valueOrError = SubscriptionPlanName.create(value as string)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { SubscriptionPlanNameProps } from './SubscriptionPlanNameProps'
|
||||
|
||||
export class SubscriptionPlanName extends ValueObject<SubscriptionPlanNameProps> {
|
||||
static readonly NAMES = {
|
||||
PlusPlan: 'PLUS_PLAN',
|
||||
ProPlan: 'PRO_PLAN',
|
||||
}
|
||||
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
private constructor(props: SubscriptionPlanNameProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(name: string): Result<SubscriptionPlanName> {
|
||||
const isValidName = Object.values(this.NAMES).includes(name)
|
||||
if (!isValidName) {
|
||||
return Result.fail<SubscriptionPlanName>(`Invalid subscription plan name: ${name}`)
|
||||
} else {
|
||||
return Result.ok<SubscriptionPlanName>(new SubscriptionPlanName({ value: name }))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface SubscriptionPlanNameProps {
|
||||
value: string
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Result } from '../Core/Result'
|
||||
|
||||
export interface SyncUseCaseInterface<T> {
|
||||
execute(...args: any[]): Result<T>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Result } from '../Core/Result'
|
||||
|
||||
export interface UseCaseInterface<T> {
|
||||
execute(...args: any[]): Promise<Result<T>>
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
export * from './Auth/LegacySession'
|
||||
export * from './Auth/LegacySessionProps'
|
||||
export * from './Auth/Session'
|
||||
export * from './Auth/SessionProps'
|
||||
export * from './Auth/SessionToken'
|
||||
export * from './Auth/SessionTokenProps'
|
||||
|
||||
export * from './Cache/CacheEntry'
|
||||
export * from './Cache/CacheEntryProps'
|
||||
export * from './Cache/CacheEntryRepositoryInterface'
|
||||
|
||||
export * from './Common/ContentType'
|
||||
export * from './Common/ContentTypeProps'
|
||||
export * from './Common/Dates'
|
||||
export * from './Common/DatesProps'
|
||||
export * from './Common/Email'
|
||||
export * from './Common/EmailProps'
|
||||
export * from './Common/RoleName'
|
||||
export * from './Common/RoleNameProps'
|
||||
export * from './Common/RoleNameCollection'
|
||||
export * from './Common/RoleNameCollectionProps'
|
||||
export * from './Common/Timestamps'
|
||||
export * from './Common/TimestampsProps'
|
||||
export * from './Common/Username'
|
||||
export * from './Common/UsernameProps'
|
||||
export * from './Common/Uuid'
|
||||
export * from './Common/UuidProps'
|
||||
|
||||
export * from './Core/Aggregate'
|
||||
export * from './Core/Change'
|
||||
export * from './Core/ChangeProps'
|
||||
export * from './Core/Entity'
|
||||
export * from './Core/Id'
|
||||
export * from './Core/Result'
|
||||
export * from './Core/UniqueEntityId'
|
||||
export * from './Core/Validator'
|
||||
export * from './Core/ValueObject'
|
||||
export * from './Core/ValueObjectProps'
|
||||
|
||||
export * from './DI/ControllerContainer'
|
||||
export * from './DI/ControllerContainerInterface'
|
||||
|
||||
export * from './Email/EmailLevel'
|
||||
export * from './Email/EmailLevelProps'
|
||||
|
||||
export * from './Env/AbstractEnv'
|
||||
|
||||
export * from './Mapping/MapperInterface'
|
||||
|
||||
export * from './Notification/NotificationPayload'
|
||||
export * from './Notification/NotificationPayloadIdentifierType'
|
||||
export * from './Notification/NotificationPayloadIdentifierTypeProps'
|
||||
export * from './Notification/NotificationPayloadProps'
|
||||
export * from './Notification/NotificationType'
|
||||
export * from './Notification/NotificationTypeProps'
|
||||
|
||||
export * from './Service/ServiceConfiguration'
|
||||
export * from './Service/ServiceContainer'
|
||||
export * from './Service/ServiceContainerInterface'
|
||||
export * from './Service/ServiceIdentifier'
|
||||
export * from './Service/ServiceIdentifierProps'
|
||||
export * from './Service/ServiceInterface'
|
||||
|
||||
export * from './Setting/SettingName'
|
||||
export * from './Setting/SettingNameProps'
|
||||
|
||||
export * from './SharedVault/SharedVaultUser'
|
||||
export * from './SharedVault/SharedVaultUserPermission'
|
||||
export * from './SharedVault/SharedVaultUserPermissionProps'
|
||||
export * from './SharedVault/SharedVaultUserProps'
|
||||
|
||||
export * from './Subscription/SubscriptionPlanName'
|
||||
export * from './Subscription/SubscriptionPlanNameProps'
|
||||
|
||||
export * from './UseCase/SyncUseCaseInterface'
|
||||
export * from './UseCase/UseCaseInterface'
|
||||
@@ -0,0 +1 @@
|
||||
export * from './Domain'
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"outDir": "./dist",
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"references": []
|
||||
}
|
||||
Reference in New Issue
Block a user