mirror of
https://github.com/standardnotes/server
synced 2026-02-04 14:01:16 -05:00
Compare commits
19 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
937ce5a157 | ||
|
|
0c1a779ef0 | ||
|
|
e01d1f44d0 | ||
|
|
cea9021c16 | ||
|
|
3039f58b5a | ||
|
|
e2326190d4 | ||
|
|
095d13f8bb | ||
|
|
1292d1d898 | ||
|
|
8bc92616d2 | ||
|
|
ae45fafaee | ||
|
|
f74227067b | ||
|
|
5f76d25ec3 | ||
|
|
ba9d3bfe46 | ||
|
|
3dc6babfca | ||
|
|
ace2b6936a | ||
|
|
712e874bfe | ||
|
|
266adda45b | ||
|
|
f5ebe4a69e | ||
|
|
15d960d47b |
137
.pnp.cjs
generated
137
.pnp.cjs
generated
@@ -2521,30 +2521,16 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/api", [\
|
||||
["npm:1.11.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.11.0-ce72fb3e14-f1134efb44.zip/node_modules/@standardnotes/api/",\
|
||||
["npm:1.16.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.16.0-efccf518ba-465f76dd29.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.11.0"],\
|
||||
["@standardnotes/api", "npm:1.16.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/encryption", "npm:1.16.0"],\
|
||||
["@standardnotes/models", "npm:1.24.0"],\
|
||||
["@standardnotes/responses", "npm:1.10.4"],\
|
||||
["@standardnotes/encryption", "npm:1.17.1"],\
|
||||
["@standardnotes/models", "npm:1.27.0"],\
|
||||
["@standardnotes/responses", "npm:1.11.0"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.9.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-api-npm-1.9.0-507434ff00-cc3feac393.zip/node_modules/@standardnotes/api/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/api", "npm:1.9.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/encryption", "npm:1.15.9"],\
|
||||
["@standardnotes/models", "npm:1.22.0"],\
|
||||
["@standardnotes/responses", "npm:1.10.3"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["@standardnotes/utils", "npm:1.10.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -2614,7 +2600,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.5.0"],\
|
||||
["@standardnotes/analytics", "workspace:packages/analytics"],\
|
||||
["@standardnotes/api", "npm:1.9.0"],\
|
||||
["@standardnotes/api", "npm:1.16.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
@@ -2739,28 +2725,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/encryption", [\
|
||||
["npm:1.15.9", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.15.9-00c7fac9f6-7595ac08ce.zip/node_modules/@standardnotes/encryption/",\
|
||||
["npm:1.17.1", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.17.1-f4d1330273-2b2408ffbd.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.15.9"],\
|
||||
["@standardnotes/encryption", "npm:1.17.1"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.22.0"],\
|
||||
["@standardnotes/responses", "npm:1.10.3"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.12.0"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.16.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-encryption-npm-1.16.0-df46ea19bc-9971b9afcc.zip/node_modules/@standardnotes/encryption/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/encryption", "npm:1.16.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/models", "npm:1.24.0"],\
|
||||
["@standardnotes/responses", "npm:1.10.4"],\
|
||||
["@standardnotes/models", "npm:1.27.0"],\
|
||||
["@standardnotes/responses", "npm:1.11.0"],\
|
||||
["@standardnotes/sncrypto-common", "npm:1.13.0"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["@standardnotes/utils", "npm:1.10.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
@@ -2818,10 +2791,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.52.2", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.52.2-076ab9f511-ab345f8dc1.zip/node_modules/@standardnotes/features/",\
|
||||
["npm:1.53.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-features-npm-1.53.0-8ea4a2d559-a856e815a3.zip/node_modules/@standardnotes/features/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/features", "npm:1.52.2"],\
|
||||
["@standardnotes/features", "npm:1.53.0"],\
|
||||
["@standardnotes/auth", "npm:3.19.4"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
@@ -2883,27 +2856,27 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/models", [\
|
||||
["npm:1.22.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.22.0-2cc72f987b-9928246368.zip/node_modules/@standardnotes/models/",\
|
||||
["npm:1.26.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.26.0-dade8919ab-f595a3de88.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.22.0"],\
|
||||
["@standardnotes/models", "npm:1.26.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.52.1"],\
|
||||
["@standardnotes/responses", "npm:1.10.3"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["@standardnotes/features", "npm:1.53.0"],\
|
||||
["@standardnotes/responses", "npm:1.11.0"],\
|
||||
["@standardnotes/utils", "npm:1.10.0"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.24.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.24.0-bf039594ac-2acbbbc062.zip/node_modules/@standardnotes/models/",\
|
||||
["npm:1.27.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-models-npm-1.27.0-831bd645c6-263fd9e923.zip/node_modules/@standardnotes/models/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/models", "npm:1.24.0"],\
|
||||
["@standardnotes/models", "npm:1.27.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.52.2"],\
|
||||
["@standardnotes/responses", "npm:1.10.4"],\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["@standardnotes/features", "npm:1.53.0"],\
|
||||
["@standardnotes/responses", "npm:1.11.0"],\
|
||||
["@standardnotes/utils", "npm:1.10.0"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
@@ -2939,23 +2912,12 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/responses", [\
|
||||
["npm:1.10.3", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.10.3-7cdb15f83a-4a1e31eb89.zip/node_modules/@standardnotes/responses/",\
|
||||
["npm:1.11.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.11.0-d066ddbbb6-46d6a47980.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.10.3"],\
|
||||
["@standardnotes/responses", "npm:1.11.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.52.1"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.10.4", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-responses-npm-1.10.4-3af0d3ab54-41e4971144.zip/node_modules/@standardnotes/responses/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/responses", "npm:1.10.4"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.52.2"],\
|
||||
["@standardnotes/features", "npm:1.53.0"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
@@ -3066,14 +3028,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/sncrypto-common", [\
|
||||
["npm:1.12.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.12.0-1a093ff006-b89a14bd23.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/sncrypto-common", "npm:1.12.0"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.13.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-sncrypto-common-npm-1.13.0-18cb5f8eb9-e58258f525.zip/node_modules/@standardnotes/sncrypto-common/",\
|
||||
"packageDependencies": [\
|
||||
@@ -3184,6 +3138,17 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/utils", [\
|
||||
["npm:1.10.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.10.0-0dc2ade40b-c02d54ca8a.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.10.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.0"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.6.12", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.6.12-8fa8d7d09b-e177b1fa51.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
@@ -3193,17 +3158,6 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["lodash", "npm:4.17.21"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}],\
|
||||
["npm:1.9.0", {\
|
||||
"packageLocation": "./.yarn/cache/@standardnotes-utils-npm-1.9.0-da939553f6-4591aff48d.zip/node_modules/@standardnotes/utils/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/utils", "npm:1.9.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["dompurify", "npm:2.4.0"],\
|
||||
["lodash", "npm:4.17.21"],\
|
||||
["reflect-metadata", "npm:0.1.13"]\
|
||||
],\
|
||||
"linkType": "HARD"\
|
||||
}]\
|
||||
]],\
|
||||
["@standardnotes/workspace-server", [\
|
||||
@@ -3213,10 +3167,11 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["@standardnotes/workspace-server", "workspace:packages/workspace"],\
|
||||
["@newrelic/winston-enricher", "virtual:04783e12400851b8a3d76e71495851cc94959db6e62f04cb0a31190080629440b182d8c8eb4d7f2b04e281912f2783a5fd4d2c3c6ab68d38b7097246c93f4c19#npm:4.0.0"],\
|
||||
["@sentry/node", "npm:7.5.0"],\
|
||||
["@standardnotes/api", "npm:1.11.0"],\
|
||||
["@standardnotes/api", "npm:1.16.0"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/domain-events", "workspace:packages/domain-events"],\
|
||||
["@standardnotes/domain-events-infra", "workspace:packages/domain-events-infra"],\
|
||||
["@standardnotes/models", "npm:1.26.0"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@standardnotes/time", "workspace:packages/time"],\
|
||||
["@types/cors", "npm:2.8.12"],\
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.30.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.29.0...@standardnotes/api-gateway@1.30.0) (2022-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add endpoints for initiating keyshare in a workspace ([0c1a779](https://github.com/standardnotes/api-gateway/commit/0c1a779ef03819928e7e791a6843d90eb9fed964))
|
||||
|
||||
# [1.29.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.2...@standardnotes/api-gateway@1.29.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* add listin worspaces and workspace users ([095d13f](https://github.com/standardnotes/api-gateway/commit/095d13f8bbfe543fcf086840e1a985447a6c51ef))
|
||||
|
||||
## [1.28.2](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.1...@standardnotes/api-gateway@1.28.2) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.28.1](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.28.0...@standardnotes/api-gateway@1.28.1) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
# [1.28.0](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.4...@standardnotes/api-gateway@1.28.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add invite to workspace endpoints ([266adda](https://github.com/standardnotes/api-gateway/commit/266adda45bd3ad84bc6605824b6be1dd912f3f9a))
|
||||
|
||||
## [1.27.4](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.3...@standardnotes/api-gateway@1.27.4) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
## [1.27.3](https://github.com/standardnotes/api-gateway/compare/@standardnotes/api-gateway@1.27.2...@standardnotes/api-gateway@1.27.3) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/api-gateway
|
||||
|
||||
@@ -20,6 +20,7 @@ import '../src/Controller/v1/OfflineController'
|
||||
import '../src/Controller/v1/FilesController'
|
||||
import '../src/Controller/v1/SubscriptionInvitesController'
|
||||
import '../src/Controller/v1/WorkspacesController'
|
||||
import '../src/Controller/v1/InvitesController'
|
||||
|
||||
import '../src/Controller/v2/PaymentsControllerV2'
|
||||
import '../src/Controller/v2/ActionsControllerV2'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/api-gateway",
|
||||
"version": "1.27.3",
|
||||
"version": "1.30.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
23
packages/api-gateway/src/Controller/v1/InvitesController.ts
Normal file
23
packages/api-gateway/src/Controller/v1/InvitesController.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { inject } from 'inversify'
|
||||
import { Request, Response } from 'express'
|
||||
import { controller, BaseHttpController, httpPost } from 'inversify-express-utils'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/invites', TYPES.AuthMiddleware)
|
||||
export class InvitesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/:inviteUuid/accept')
|
||||
async accept(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`invites/${request.params.inviteUuid}/accept`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,53 @@
|
||||
import { inject } from 'inversify'
|
||||
import { Request, Response } from 'express'
|
||||
import { controller, BaseHttpController, httpPost } from 'inversify-express-utils'
|
||||
import { controller, BaseHttpController, httpPost, httpGet } from 'inversify-express-utils'
|
||||
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface'
|
||||
|
||||
@controller('/v1/workspaces')
|
||||
@controller('/v1/workspaces', TYPES.AuthMiddleware)
|
||||
export class WorkspacesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.AuthMiddleware)
|
||||
@httpPost('/')
|
||||
async create(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
|
||||
}
|
||||
|
||||
@httpGet('/:workspaceUuid/users')
|
||||
async listWorkspaceUsers(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/users`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/users/:userUuid/keyshare')
|
||||
async initiateKeyshare(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/users/${request.params.userUuid}/keyshare`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
async listWorkspaces(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(request, response, 'workspaces', request.body)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/invites')
|
||||
async invite(request: Request, response: Response): Promise<void> {
|
||||
await this.httpService.callWorkspaceServer(
|
||||
request,
|
||||
response,
|
||||
`workspaces/${request.params.workspaceUuid}/invites`,
|
||||
request.body,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.43.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.42.0...@standardnotes/auth-server@1.43.0) (2022-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add endpoints for initiating keyshare in a workspace ([0c1a779](https://github.com/standardnotes/server/commit/0c1a779ef03819928e7e791a6843d90eb9fed964))
|
||||
|
||||
# [1.42.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.2...@standardnotes/auth-server@1.42.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* add listin worspaces and workspace users ([095d13f](https://github.com/standardnotes/server/commit/095d13f8bbfe543fcf086840e1a985447a6c51ef))
|
||||
|
||||
## [1.41.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.1...@standardnotes/auth-server@1.41.2) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.41.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.41.0...@standardnotes/auth-server@1.41.1) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
# [1.41.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.4...@standardnotes/auth-server@1.41.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add invite to workspace endpoints ([266adda](https://github.com/standardnotes/server/commit/266adda45bd3ad84bc6605824b6be1dd912f3f9a))
|
||||
|
||||
## [1.40.4](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.3...@standardnotes/auth-server@1.40.4) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.40.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.40.2...@standardnotes/auth-server@1.40.3) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.40.3",
|
||||
"version": "1.43.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -34,7 +34,7 @@
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/analytics": "workspace:*",
|
||||
"@standardnotes/api": "^1.9.0",
|
||||
"@standardnotes/api": "^1.16.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-events": "workspace:*",
|
||||
"@standardnotes/domain-events-infra": "workspace:*",
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.39.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.38.0...@standardnotes/common@1.39.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** extract workspace user status to common ([8bc9261](https://github.com/standardnotes/server/commit/8bc92616d2fbeb833c3fcbef6b87538745fc7f3e))
|
||||
|
||||
# [1.38.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.37.0...@standardnotes/common@1.38.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add invite access level ([f742270](https://github.com/standardnotes/server/commit/f74227067b7151cb63a54e815e57f81984467bfe))
|
||||
|
||||
# [1.37.0](https://github.com/standardnotes/server/compare/@standardnotes/common@1.36.1...@standardnotes/common@1.37.0) (2022-10-10)
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** add WORKSPACE_INVITE_CREATED email message identifier ([15d960d](https://github.com/standardnotes/server/commit/15d960d47b0bcf5aeddf869ac939eafb08166db7))
|
||||
|
||||
## [1.36.1](https://github.com/standardnotes/server/compare/@standardnotes/common@1.36.0...@standardnotes/common@1.36.1) (2022-10-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/common",
|
||||
"version": "1.36.1",
|
||||
"version": "1.39.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -24,4 +24,5 @@ export enum EmailMessageIdentifier {
|
||||
REFUND_NOTICE = 'REFUND_NOTICE',
|
||||
REFUND_REQUESTED = 'REFUND_REQUESTED',
|
||||
RATE_ADJUSTMENT_NOTICE = 'RATE_ADJUSTMENT_NOTICE',
|
||||
WORKSPACE_INVITE_CREATED = 'WORKSPACE_INVITE_CREATED',
|
||||
}
|
||||
|
||||
@@ -24,4 +24,6 @@ export * from './Type/Either'
|
||||
export * from './Type/Only'
|
||||
export * from './Validator/UuidValidator'
|
||||
export * from './Validator/ValidatorInterface'
|
||||
export * from './Workspace/WorkspaceAccessLevel'
|
||||
export * from './Workspace/WorkspaceType'
|
||||
export * from './Workspace/WorkspaceUserStatus'
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.8.25](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.24...@standardnotes/domain-events-infra@1.8.25) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.8.24](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.23...@standardnotes/domain-events-infra@1.8.24) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.8.23](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.22...@standardnotes/domain-events-infra@1.8.23) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
## [1.8.22](https://github.com/standardnotes/server/compare/@standardnotes/domain-events-infra@1.8.21...@standardnotes/domain-events-infra@1.8.22) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events-infra
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events-infra",
|
||||
"version": "1.8.22",
|
||||
"version": "1.8.25",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.66.3](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.2...@standardnotes/domain-events@2.66.3) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
## [2.66.2](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.1...@standardnotes/domain-events@2.66.2) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
## [2.66.1](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.66.0...@standardnotes/domain-events@2.66.1) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/domain-events
|
||||
|
||||
# [2.66.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-events@2.65.0...@standardnotes/domain-events@2.66.0) (2022-10-10)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-events",
|
||||
"version": "2.66.0",
|
||||
"version": "2.66.3",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.4](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.3...@standardnotes/event-store@1.4.4) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.4.3](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.2...@standardnotes/event-store@1.4.3) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.4.2](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.1...@standardnotes/event-store@1.4.2) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
## [1.4.1](https://github.com/standardnotes/server/compare/@standardnotes/event-store@1.4.0...@standardnotes/event-store@1.4.1) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/event-store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/event-store",
|
||||
"version": "1.4.1",
|
||||
"version": "1.4.4",
|
||||
"description": "Event Store Service",
|
||||
"private": true,
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.6.16](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.15...@standardnotes/files-server@1.6.16) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.6.15](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.14...@standardnotes/files-server@1.6.15) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.6.14](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.13...@standardnotes/files-server@1.6.14) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
## [1.6.13](https://github.com/standardnotes/files/compare/@standardnotes/files-server@1.6.12...@standardnotes/files-server@1.6.13) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/files-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/files-server",
|
||||
"version": "1.6.13",
|
||||
"version": "1.6.16",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.10](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.9...@standardnotes/predicates@1.4.10) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
## [1.4.9](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.8...@standardnotes/predicates@1.4.9) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
## [1.4.8](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.7...@standardnotes/predicates@1.4.8) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
## [1.4.7](https://github.com/standardnotes/server/compare/@standardnotes/predicates@1.4.6...@standardnotes/predicates@1.4.7) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/predicates
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/predicates",
|
||||
"version": "1.4.7",
|
||||
"version": "1.4.10",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.10.44](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.43...@standardnotes/scheduler-server@1.10.44) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.10.43](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.42...@standardnotes/scheduler-server@1.10.43) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.10.42](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.41...@standardnotes/scheduler-server@1.10.42) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.10.41](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.10.40...@standardnotes/scheduler-server@1.10.41) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.10.41",
|
||||
"version": "1.10.44",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.4.8](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.7...@standardnotes/security@1.4.8) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/security
|
||||
|
||||
## [1.4.7](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.6...@standardnotes/security@1.4.7) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/security
|
||||
|
||||
## [1.4.6](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.5...@standardnotes/security@1.4.6) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/security
|
||||
|
||||
## [1.4.5](https://github.com/standardnotes/server/compare/@standardnotes/security@1.4.4...@standardnotes/security@1.4.5) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/security",
|
||||
"version": "1.4.5",
|
||||
"version": "1.4.8",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.5...@standardnotes/syncing-server@1.9.6) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.9.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.4...@standardnotes/syncing-server@1.9.5) (2022-10-11)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.9.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.3...@standardnotes/syncing-server@1.9.4) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.9.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.9.2...@standardnotes/syncing-server@1.9.3) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.9.3",
|
||||
"version": "1.9.6",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,58 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.13.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.12.0...@standardnotes/workspace-server@1.13.0) (2022-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add endpoints for initiating keyshare in a workspace ([0c1a779](https://github.com/standardnotes/server/commit/0c1a779ef03819928e7e791a6843d90eb9fed964))
|
||||
|
||||
# [1.12.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.11.0...@standardnotes/workspace-server@1.12.0) (2022-10-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add initiating key share ([cea9021](https://github.com/standardnotes/server/commit/cea9021c164588969890370a2332f11749ac820e))
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.10.0...@standardnotes/workspace-server@1.11.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* add listin worspaces and workspace users ([095d13f](https://github.com/standardnotes/server/commit/095d13f8bbfe543fcf086840e1a985447a6c51ef))
|
||||
|
||||
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.9.0...@standardnotes/workspace-server@1.10.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** extract workspace user status to common ([8bc9261](https://github.com/standardnotes/server/commit/8bc92616d2fbeb833c3fcbef6b87538745fc7f3e))
|
||||
|
||||
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.8.0...@standardnotes/workspace-server@1.9.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add invite access level ([f742270](https://github.com/standardnotes/server/commit/f74227067b7151cb63a54e815e57f81984467bfe))
|
||||
|
||||
# [1.8.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.7.0...@standardnotes/workspace-server@1.8.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add workspace user display name ([ba9d3bf](https://github.com/standardnotes/server/commit/ba9d3bfe4632d5001b8c967860df086f103e2e35))
|
||||
|
||||
# [1.7.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.6.0...@standardnotes/workspace-server@1.7.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** accepting invitation ([ace2b69](https://github.com/standardnotes/server/commit/ace2b6936a104f3cfcad8f15d846e845917aa678))
|
||||
|
||||
# [1.6.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.5.1...@standardnotes/workspace-server@1.6.0) (2022-10-11)
|
||||
|
||||
### Features
|
||||
|
||||
* **workspace:** add invite to workspace endpoints ([266adda](https://github.com/standardnotes/server/commit/266adda45bd3ad84bc6605824b6be1dd912f3f9a))
|
||||
|
||||
## [1.5.1](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.5.0...@standardnotes/workspace-server@1.5.1) (2022-10-10)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
# [1.5.0](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.4.1...@standardnotes/workspace-server@1.5.0) (2022-10-10)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'newrelic'
|
||||
import * as Sentry from '@sentry/node'
|
||||
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressHealthCheckController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressInvitesController'
|
||||
import '../src/Infra/InversifyExpressUtils/InversifyExpressWorkspacesController'
|
||||
|
||||
import * as cors from 'cors'
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class addUserDisplayName1665480537103 implements MigrationInterface {
|
||||
name = 'addUserDisplayName1665480537103'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `workspace_users` ADD `user_display_name` varchar(255) NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `workspace_users` DROP COLUMN `user_display_name`')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm'
|
||||
|
||||
export class addInviteAccessLevel1665481699781 implements MigrationInterface {
|
||||
name = 'addInviteAccessLevel1665481699781'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `workspace_invites` ADD `access_level` varchar(64) NOT NULL')
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query('ALTER TABLE `workspace_invites` DROP COLUMN `access_level`')
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/workspace-server",
|
||||
"version": "1.5.0",
|
||||
"version": "1.13.0",
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <17.0.0"
|
||||
},
|
||||
@@ -25,10 +25,11 @@
|
||||
"dependencies": {
|
||||
"@newrelic/winston-enricher": "^4.0.0",
|
||||
"@sentry/node": "^7.3.0",
|
||||
"@standardnotes/api": "^1.11.0",
|
||||
"@standardnotes/api": "^1.16.0",
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/domain-events": "workspace:^",
|
||||
"@standardnotes/domain-events-infra": "workspace:^",
|
||||
"@standardnotes/models": "^1.26.0",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"@standardnotes/time": "workspace:^",
|
||||
"aws-sdk": "^2.1159.0",
|
||||
|
||||
@@ -38,6 +38,15 @@ import { WorkspaceInvite } from '../Domain/Invite/WorkspaceInvite'
|
||||
import { InviteToWorkspace } from '../Domain/UseCase/InviteToWorkspace/InviteToWorkspace'
|
||||
import { DomainEventFactory } from '../Domain/Event/DomainEventFactory'
|
||||
import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface'
|
||||
import { WorkspaceProjection } from '../Domain/Projection/WorkspaceProjection'
|
||||
import { WorkspaceProjector } from '../Domain/Projection/WorkspaceProjector'
|
||||
import { ProjectorInterface } from '../Domain/Projection/ProjectorInterface'
|
||||
import { WorkspaceUserProjection } from '../Domain/Projection/WorkspaceUserProjection'
|
||||
import { WorkspaceUserProjector } from '../Domain/Projection/WorkspaceUserProjector'
|
||||
import { AcceptInvitation } from '../Domain/UseCase/AcceptInvitation/AcceptInvitation'
|
||||
import { ListWorkspaces } from '../Domain/UseCase/ListWorkspaces/ListWorkspaces'
|
||||
import { ListWorkspaceUsers } from '../Domain/UseCase/ListWorkspaceUsers/ListWorkspaceUsers'
|
||||
import { InitiateKeyShare } from '../Domain/UseCase/InitiateKeyShare/InitiateKeyShare'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const newrelicFormatter = require('@newrelic/winston-enricher')
|
||||
@@ -131,8 +140,17 @@ export class ContainerConfigLoader {
|
||||
// use cases
|
||||
container.bind<CreateWorkspace>(TYPES.CreateWorkspace).to(CreateWorkspace)
|
||||
container.bind<InviteToWorkspace>(TYPES.InviteToWorkspace).to(InviteToWorkspace)
|
||||
container.bind<AcceptInvitation>(TYPES.AcceptInvitation).to(AcceptInvitation)
|
||||
container.bind<ListWorkspaces>(TYPES.ListWorkspaces).to(ListWorkspaces)
|
||||
container.bind<ListWorkspaceUsers>(TYPES.ListWorkspaceUsers).to(ListWorkspaceUsers)
|
||||
container.bind<InitiateKeyShare>(TYPES.InitiateKeyShare).to(InitiateKeyShare)
|
||||
// Handlers
|
||||
container.bind<UserRegisteredEventHandler>(TYPES.UserRegisteredEventHandler).to(UserRegisteredEventHandler)
|
||||
// Projection
|
||||
container.bind<ProjectorInterface<Workspace, WorkspaceProjection>>(TYPES.WorkspaceProjector).to(WorkspaceProjector)
|
||||
container
|
||||
.bind<ProjectorInterface<WorkspaceUser, WorkspaceUserProjection>>(TYPES.WorkspaceUserProjector)
|
||||
.to(WorkspaceUserProjector)
|
||||
// Services
|
||||
container.bind<DomainEventFactoryInterface>(TYPES.DomainEventFactory).to(DomainEventFactory)
|
||||
container.bind<TimerInterface>(TYPES.Timer).toConstantValue(new Timer())
|
||||
|
||||
@@ -28,8 +28,15 @@ const TYPES = {
|
||||
// use cases
|
||||
CreateWorkspace: Symbol.for('CreateWorkspace'),
|
||||
InviteToWorkspace: Symbol.for('InviteToWorkspace'),
|
||||
AcceptInvitation: Symbol.for('AcceptInvitation'),
|
||||
ListWorkspaces: Symbol.for('ListWorkspaces'),
|
||||
ListWorkspaceUsers: Symbol.for('ListWorkspaceUsers'),
|
||||
InitiateKeyShare: Symbol.for('InitiateKeyShare'),
|
||||
// Handlers
|
||||
UserRegisteredEventHandler: Symbol.for('UserRegisteredEventHandler'),
|
||||
// Projection
|
||||
WorkspaceProjector: Symbol.for('WorkspaceProjector'),
|
||||
WorkspaceUserProjector: Symbol.for('WorkspaceUserProjector'),
|
||||
// Services
|
||||
Timer: Symbol.for('Timer'),
|
||||
CrossServiceTokenDecoder: Symbol.for('CrossServiceTokenDecoder'),
|
||||
|
||||
@@ -1,17 +1,72 @@
|
||||
import { WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
import 'reflect-metadata'
|
||||
import { ProjectorInterface } from '../Domain/Projection/ProjectorInterface'
|
||||
import { WorkspaceProjection } from '../Domain/Projection/WorkspaceProjection'
|
||||
import { WorkspaceUserProjection } from '../Domain/Projection/WorkspaceUserProjection'
|
||||
import { AcceptInvitation } from '../Domain/UseCase/AcceptInvitation/AcceptInvitation'
|
||||
|
||||
import { CreateWorkspace } from '../Domain/UseCase/CreateWorkspace/CreateWorkspace'
|
||||
import { InitiateKeyShare } from '../Domain/UseCase/InitiateKeyShare/InitiateKeyShare'
|
||||
import { InviteToWorkspace } from '../Domain/UseCase/InviteToWorkspace/InviteToWorkspace'
|
||||
import { ListWorkspaces } from '../Domain/UseCase/ListWorkspaces/ListWorkspaces'
|
||||
import { ListWorkspaceUsers } from '../Domain/UseCase/ListWorkspaceUsers/ListWorkspaceUsers'
|
||||
import { Workspace } from '../Domain/Workspace/Workspace'
|
||||
import { WorkspaceUser } from '../Domain/Workspace/WorkspaceUser'
|
||||
|
||||
import { WorkspacesController } from './WorkspacesController'
|
||||
|
||||
describe('WorkspacesController', () => {
|
||||
let doCreateWorkspace: CreateWorkspace
|
||||
let doInviteToWorkspace: InviteToWorkspace
|
||||
let doAcceptInvitation: AcceptInvitation
|
||||
let doListWorkspaces: ListWorkspaces
|
||||
let doListWorkspaceUsers: ListWorkspaceUsers
|
||||
let doInitiateKeyshare: InitiateKeyShare
|
||||
let workspacesProject: ProjectorInterface<Workspace, WorkspaceProjection>
|
||||
let workspaceUsersProjector: ProjectorInterface<WorkspaceUser, WorkspaceUserProjection>
|
||||
let workspace1: Workspace
|
||||
let workspace2: Workspace
|
||||
let workspaceUser1: WorkspaceUser
|
||||
let workspaceUser2: WorkspaceUser
|
||||
|
||||
const createController = () => new WorkspacesController(doCreateWorkspace)
|
||||
const createController = () =>
|
||||
new WorkspacesController(
|
||||
doCreateWorkspace,
|
||||
doInviteToWorkspace,
|
||||
doListWorkspaces,
|
||||
doListWorkspaceUsers,
|
||||
doAcceptInvitation,
|
||||
doInitiateKeyshare,
|
||||
workspacesProject,
|
||||
workspaceUsersProjector,
|
||||
)
|
||||
|
||||
beforeEach(() => {
|
||||
doCreateWorkspace = {} as jest.Mocked<CreateWorkspace>
|
||||
doCreateWorkspace.execute = jest.fn().mockReturnValue({ workspace: { uuid: 'w-1-2-3' } })
|
||||
|
||||
doInviteToWorkspace = {} as jest.Mocked<InviteToWorkspace>
|
||||
doInviteToWorkspace.execute = jest.fn().mockReturnValue({ invite: { uuid: 'i-1-2-3' } })
|
||||
|
||||
doListWorkspaces = {} as jest.Mocked<ListWorkspaces>
|
||||
doListWorkspaces.execute = jest
|
||||
.fn()
|
||||
.mockReturnValue({ ownedWorkspaces: [workspace1], joinedWorkspaces: [workspace2] })
|
||||
|
||||
doListWorkspaceUsers = {} as jest.Mocked<ListWorkspaceUsers>
|
||||
doListWorkspaceUsers.execute = jest.fn().mockReturnValue({ workspaceUsers: [workspaceUser1, workspaceUser2] })
|
||||
|
||||
doAcceptInvitation = {} as jest.Mocked<AcceptInvitation>
|
||||
doAcceptInvitation.execute = jest.fn().mockReturnValue({ success: true })
|
||||
|
||||
doInitiateKeyshare = {} as jest.Mocked<InitiateKeyShare>
|
||||
doInitiateKeyshare.execute = jest.fn().mockReturnValue({ success: true })
|
||||
|
||||
workspacesProject = {} as jest.Mocked<ProjectorInterface<Workspace, WorkspaceProjection>>
|
||||
workspacesProject.project = jest.fn().mockReturnValue({ foo: 'bar' })
|
||||
|
||||
workspaceUsersProjector = {} as jest.Mocked<ProjectorInterface<WorkspaceUser, WorkspaceUserProjection>>
|
||||
workspaceUsersProjector.project = jest.fn().mockReturnValue({ bar: 'buzz' })
|
||||
})
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
@@ -21,6 +76,7 @@ describe('WorkspacesController', () => {
|
||||
publicKey: 'buzz',
|
||||
workspaceName: 'A Team',
|
||||
ownerUuid: 'u-1-2-3',
|
||||
workspaceType: WorkspaceType.Private,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
@@ -30,4 +86,118 @@ describe('WorkspacesController', () => {
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should invite to a workspace', async () => {
|
||||
const result = await createController().inviteToWorkspace({
|
||||
inviteeEmail: 'test@test.te',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
accessLevel: WorkspaceAccessLevel.ReadOnly,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
uuid: 'i-1-2-3',
|
||||
},
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should accept an invite', async () => {
|
||||
const result = await createController().acceptInvite({
|
||||
userUuid: '1-2-3',
|
||||
encryptedPrivateKey: 'foo',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'bar',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not accept an invite if it fails', async () => {
|
||||
doAcceptInvitation.execute = jest.fn().mockReturnValue({ success: false })
|
||||
const result = await createController().acceptInvite({
|
||||
userUuid: '1-2-3',
|
||||
encryptedPrivateKey: 'foo',
|
||||
inviteUuid: 'i-1-2-3',
|
||||
publicKey: 'bar',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
error: {
|
||||
message: 'Could not accept invite',
|
||||
},
|
||||
},
|
||||
status: 400,
|
||||
})
|
||||
})
|
||||
|
||||
it('should list workspaces', async () => {
|
||||
const result = await createController().listWorkspaces({
|
||||
userUuid: '1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
ownedWorkspaces: [{ foo: 'bar' }],
|
||||
joinedWorkspaces: [{ foo: 'bar' }],
|
||||
},
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should list workspace users', async () => {
|
||||
const result = await createController().listWorkspaceUsers({
|
||||
userUuid: '1-2-3',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
users: [{ bar: 'buzz' }, { bar: 'buzz' }],
|
||||
},
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should initiate keyshare', async () => {
|
||||
const result = await createController().initiateKeyshare({
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foo',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
performingUserUuid: 'p-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
status: 200,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not initiate keyshare if it fails', async () => {
|
||||
doInitiateKeyshare.execute = jest.fn().mockReturnValue({ success: false })
|
||||
|
||||
const result = await createController().initiateKeyshare({
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foo',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
performingUserUuid: 'p-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
data: {
|
||||
error: {
|
||||
message: 'Could not initiate keyshare.',
|
||||
},
|
||||
},
|
||||
status: 400,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,16 +3,89 @@ import {
|
||||
HttpStatusCode,
|
||||
WorkspaceCreationRequestParams,
|
||||
WorkspaceCreationResponse,
|
||||
WorkspaceInvitationRequestParams,
|
||||
WorkspaceInvitationResponse,
|
||||
WorkspaceServerInterface,
|
||||
WorkspaceListRequestParams,
|
||||
WorkspaceListResponse,
|
||||
WorkspaceInvitationAcceptingRequestParams,
|
||||
WorkspaceInvitationAcceptingResponse,
|
||||
WorkspaceUserListRequestParams,
|
||||
WorkspaceKeyshareInitiatingRequestParams,
|
||||
WorkspaceKeyshareInitiatingResponse,
|
||||
} from '@standardnotes/api'
|
||||
import { WorkspaceType } from '@standardnotes/common'
|
||||
import { Uuid, WorkspaceAccessLevel, WorkspaceType } from '@standardnotes/common'
|
||||
|
||||
import TYPES from '../Bootstrap/Types'
|
||||
import { CreateWorkspace } from '../Domain/UseCase/CreateWorkspace/CreateWorkspace'
|
||||
import { InviteToWorkspace } from '../Domain/UseCase/InviteToWorkspace/InviteToWorkspace'
|
||||
import { ProjectorInterface } from '../Domain/Projection/ProjectorInterface'
|
||||
import { WorkspaceProjection } from '../Domain/Projection/WorkspaceProjection'
|
||||
import { Workspace } from '../Domain/Workspace/Workspace'
|
||||
import { ListWorkspaces } from '../Domain/UseCase/ListWorkspaces/ListWorkspaces'
|
||||
import { WorkspaceUserListResponse } from '@standardnotes/api/dist/Domain/Response/Workspace/WorkspaceUserListResponse'
|
||||
import { AcceptInvitation } from '../Domain/UseCase/AcceptInvitation/AcceptInvitation'
|
||||
import { WorkspaceUser } from '../Domain/Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserProjection } from '../Domain/Projection/WorkspaceUserProjection'
|
||||
import { ListWorkspaceUsers } from '../Domain/UseCase/ListWorkspaceUsers/ListWorkspaceUsers'
|
||||
import { InitiateKeyShare } from '../Domain/UseCase/InitiateKeyShare/InitiateKeyShare'
|
||||
|
||||
@injectable()
|
||||
export class WorkspacesController implements WorkspaceServerInterface {
|
||||
constructor(@inject(TYPES.CreateWorkspace) private doCreateWorkspace: CreateWorkspace) {}
|
||||
constructor(
|
||||
@inject(TYPES.CreateWorkspace) private doCreateWorkspace: CreateWorkspace,
|
||||
@inject(TYPES.InviteToWorkspace) private doInviteToWorkspace: InviteToWorkspace,
|
||||
@inject(TYPES.ListWorkspaces) private doListWorkspaces: ListWorkspaces,
|
||||
@inject(TYPES.ListWorkspaceUsers) private doListWorkspaceUsers: ListWorkspaceUsers,
|
||||
@inject(TYPES.AcceptInvitation) private doAcceptInvite: AcceptInvitation,
|
||||
@inject(TYPES.InitiateKeyShare) private doInitiateKeyshare: InitiateKeyShare,
|
||||
@inject(TYPES.WorkspaceProjector) private workspaceProjector: ProjectorInterface<Workspace, WorkspaceProjection>,
|
||||
@inject(TYPES.WorkspaceUserProjector)
|
||||
private workspaceUserProjector: ProjectorInterface<WorkspaceUser, WorkspaceUserProjection>,
|
||||
) {}
|
||||
|
||||
async initiateKeyshare(
|
||||
params: WorkspaceKeyshareInitiatingRequestParams,
|
||||
): Promise<WorkspaceKeyshareInitiatingResponse> {
|
||||
const result = await this.doInitiateKeyshare.execute({
|
||||
userUuid: params.userUuid,
|
||||
workspaceUuid: params.workspaceUuid,
|
||||
encryptedWorkspaceKey: params.encryptedWorkspaceKey,
|
||||
performingUserUuid: params.performingUserUuid as Uuid,
|
||||
})
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Could not initiate keyshare.',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async inviteToWorkspace(params: WorkspaceInvitationRequestParams): Promise<WorkspaceInvitationResponse> {
|
||||
const { invite } = await this.doInviteToWorkspace.execute({
|
||||
inviteeEmail: params.inviteeEmail,
|
||||
workspaceUuid: params.workspaceUuid,
|
||||
inviterUuid: params.inviterUuid as Uuid,
|
||||
accessLevel: params.accessLevel as WorkspaceAccessLevel,
|
||||
})
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: { uuid: invite.uuid },
|
||||
}
|
||||
}
|
||||
|
||||
async createWorkspace(params: WorkspaceCreationRequestParams): Promise<WorkspaceCreationResponse> {
|
||||
const { workspace } = await this.doCreateWorkspace.execute({
|
||||
@@ -29,4 +102,69 @@ export class WorkspacesController implements WorkspaceServerInterface {
|
||||
data: { uuid: workspace.uuid },
|
||||
}
|
||||
}
|
||||
|
||||
async listWorkspaces(params: WorkspaceListRequestParams): Promise<WorkspaceListResponse> {
|
||||
const { ownedWorkspaces, joinedWorkspaces } = await this.doListWorkspaces.execute({
|
||||
userUuid: params.userUuid as Uuid,
|
||||
})
|
||||
|
||||
const ownedWorkspacesProjections = []
|
||||
for (const ownedWorkspace of ownedWorkspaces) {
|
||||
ownedWorkspacesProjections.push(await this.workspaceProjector.project(ownedWorkspace))
|
||||
}
|
||||
|
||||
const joinedWorkspacesProjections = []
|
||||
for (const joinedWorkspace of joinedWorkspaces) {
|
||||
joinedWorkspacesProjections.push(await this.workspaceProjector.project(joinedWorkspace))
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: { ownedWorkspaces: ownedWorkspacesProjections, joinedWorkspaces: joinedWorkspacesProjections },
|
||||
}
|
||||
}
|
||||
|
||||
async listWorkspaceUsers(params: WorkspaceUserListRequestParams): Promise<WorkspaceUserListResponse> {
|
||||
const { workspaceUsers } = await this.doListWorkspaceUsers.execute({
|
||||
userUuid: params.userUuid as Uuid,
|
||||
workspaceUuid: params.workspaceUuid,
|
||||
})
|
||||
|
||||
const workspaceUserProjections = []
|
||||
for (const workspaceUser of workspaceUsers) {
|
||||
workspaceUserProjections.push(await this.workspaceUserProjector.project(workspaceUser))
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: { users: workspaceUserProjections },
|
||||
}
|
||||
}
|
||||
|
||||
async acceptInvite(params: WorkspaceInvitationAcceptingRequestParams): Promise<WorkspaceInvitationAcceptingResponse> {
|
||||
const result = await this.doAcceptInvite.execute({
|
||||
acceptingUserUuid: params.userUuid,
|
||||
encryptedPrivateKey: params.encryptedPrivateKey,
|
||||
invitationUuid: params.inviteUuid,
|
||||
publicKey: params.publicKey,
|
||||
})
|
||||
|
||||
if (!result.success) {
|
||||
return {
|
||||
status: HttpStatusCode.BadRequest,
|
||||
data: {
|
||||
error: {
|
||||
message: 'Could not accept invite',
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status: HttpStatusCode.Success,
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'
|
||||
|
||||
import { Workspace } from '../Workspace/Workspace'
|
||||
import { WorkspaceInviteStatus } from './WorkspaceInviteStatus'
|
||||
|
||||
@@ -40,6 +42,12 @@ export class WorkspaceInvite {
|
||||
})
|
||||
declare workspaceUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'access_level',
|
||||
length: 64,
|
||||
})
|
||||
declare accessLevel: WorkspaceAccessLevel
|
||||
|
||||
@Column({
|
||||
name: 'created_at',
|
||||
type: 'bigint',
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
import { WorkspaceInvite } from './WorkspaceInvite'
|
||||
|
||||
export interface WorkspaceInviteRepositoryInterface {
|
||||
findOneByUuid(uuid: Uuid): Promise<WorkspaceInvite | null>
|
||||
save(workspace: WorkspaceInvite): Promise<WorkspaceInvite>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface ProjectorInterface<T, E> {
|
||||
project(object: T): Promise<E>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { Workspace } from '@standardnotes/models'
|
||||
|
||||
export type WorkspaceProjection = Workspace
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { WorkspaceType } from '@standardnotes/common'
|
||||
import { Workspace } from '../Workspace/Workspace'
|
||||
|
||||
import { WorkspaceProjector } from './WorkspaceProjector'
|
||||
|
||||
describe('WorkspaceProjector', () => {
|
||||
const createProjector = () => new WorkspaceProjector()
|
||||
|
||||
it('should project a workspace', async () => {
|
||||
expect(
|
||||
await createProjector().project({
|
||||
uuid: 'w-1-2-3',
|
||||
type: WorkspaceType.Private,
|
||||
name: 'test',
|
||||
keyRotationIndex: 0,
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
} as jest.Mocked<Workspace>),
|
||||
).toEqual({
|
||||
uuid: 'w-1-2-3',
|
||||
type: 'private',
|
||||
name: 'test',
|
||||
keyRotationIndex: 0,
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { injectable } from 'inversify'
|
||||
import { ProjectorInterface } from './ProjectorInterface'
|
||||
|
||||
import { WorkspaceProjection } from './WorkspaceProjection'
|
||||
import { Workspace } from '../Workspace/Workspace'
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceProjector implements ProjectorInterface<Workspace, WorkspaceProjection> {
|
||||
async project(workspace: Workspace): Promise<WorkspaceProjection> {
|
||||
return {
|
||||
uuid: workspace.uuid,
|
||||
type: workspace.type,
|
||||
name: workspace.name,
|
||||
keyRotationIndex: workspace.keyRotationIndex,
|
||||
createdAt: workspace.createdAt,
|
||||
updatedAt: workspace.updatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { WorkspaceUser } from '@standardnotes/models'
|
||||
|
||||
export type WorkspaceUserProjection = WorkspaceUser
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
|
||||
import { WorkspaceUser } from '../Workspace/WorkspaceUser'
|
||||
|
||||
import { WorkspaceUserProjector } from './WorkspaceUserProjector'
|
||||
|
||||
describe('WorkspaceUserProjector', () => {
|
||||
const createProjector = () => new WorkspaceUserProjector()
|
||||
|
||||
it('should project a workspace user', async () => {
|
||||
expect(
|
||||
await createProjector().project({
|
||||
uuid: '1-2-3',
|
||||
accessLevel: WorkspaceAccessLevel.Owner,
|
||||
userUuid: 'u-1-2-3',
|
||||
userDisplayName: 'foobar',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
encryptedWorkspaceKey: 'foo',
|
||||
publicKey: 'bar',
|
||||
encryptedPrivateKey: 'buzz',
|
||||
status: WorkspaceUserStatus.PendingKeyshare,
|
||||
keyRotationIndex: 0,
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
} as jest.Mocked<WorkspaceUser>),
|
||||
).toEqual({
|
||||
uuid: '1-2-3',
|
||||
accessLevel: 'owner',
|
||||
userUuid: 'u-1-2-3',
|
||||
userDisplayName: 'foobar',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
encryptedWorkspaceKey: 'foo',
|
||||
publicKey: 'bar',
|
||||
encryptedPrivateKey: 'buzz',
|
||||
status: 'pending-keyshare',
|
||||
keyRotationIndex: 0,
|
||||
createdAt: 1,
|
||||
updatedAt: 2,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,25 @@
|
||||
import { injectable } from 'inversify'
|
||||
import { ProjectorInterface } from './ProjectorInterface'
|
||||
|
||||
import { WorkspaceUserProjection } from './WorkspaceUserProjection'
|
||||
import { WorkspaceUser } from '../Workspace/WorkspaceUser'
|
||||
|
||||
@injectable()
|
||||
export class WorkspaceUserProjector implements ProjectorInterface<WorkspaceUser, WorkspaceUserProjection> {
|
||||
async project(workspaceUser: WorkspaceUser): Promise<WorkspaceUserProjection> {
|
||||
return {
|
||||
uuid: workspaceUser.uuid,
|
||||
accessLevel: workspaceUser.accessLevel,
|
||||
userUuid: workspaceUser.userUuid,
|
||||
userDisplayName: workspaceUser.userDisplayName,
|
||||
workspaceUuid: workspaceUser.workspaceUuid,
|
||||
encryptedWorkspaceKey: workspaceUser.encryptedWorkspaceKey,
|
||||
publicKey: workspaceUser.publicKey,
|
||||
encryptedPrivateKey: workspaceUser.encryptedPrivateKey,
|
||||
status: workspaceUser.status,
|
||||
keyRotationIndex: workspaceUser.keyRotationIndex,
|
||||
createdAt: workspaceUser.createdAt,
|
||||
updatedAt: workspaceUser.updatedAt,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import 'reflect-metadata'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { WorkspaceInvite } from '../../Invite/WorkspaceInvite'
|
||||
import { WorkspaceInviteRepositoryInterface } from '../../Invite/WorkspaceInviteRepositoryInterface'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
|
||||
import { AcceptInvitation } from './AcceptInvitation'
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
|
||||
describe('AcceptInvitation', () => {
|
||||
let workspaceInviteRepository: WorkspaceInviteRepositoryInterface
|
||||
let workspaceUserRepository: WorkspaceUserRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let invite: WorkspaceInvite
|
||||
|
||||
const createUseCase = () => new AcceptInvitation(workspaceInviteRepository, workspaceUserRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
invite = {
|
||||
uuid: 'i-1-2-3',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: 'test@test.te',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
} as jest.Mocked<WorkspaceInvite>
|
||||
workspaceInviteRepository = {} as jest.Mocked<WorkspaceInviteRepositoryInterface>
|
||||
workspaceInviteRepository.findOneByUuid = jest.fn().mockReturnValue(invite)
|
||||
workspaceInviteRepository.save = jest.fn()
|
||||
|
||||
workspaceUserRepository = {} as jest.Mocked<WorkspaceUserRepositoryInterface>
|
||||
workspaceUserRepository.save = jest.fn()
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
|
||||
})
|
||||
|
||||
it('should accept an invite and assign user to workspace', async () => {
|
||||
await createUseCase().execute({
|
||||
acceptingUserUuid: 'u-1-2-3',
|
||||
encryptedPrivateKey: 'foo',
|
||||
publicKey: 'bar',
|
||||
invitationUuid: 'i-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceInviteRepository.save).toHaveBeenCalledWith({
|
||||
acceptingUserUuid: 'u-1-2-3',
|
||||
status: 'accepted',
|
||||
updatedAt: 1,
|
||||
uuid: 'i-1-2-3',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
inviteeEmail: 'test@test.te',
|
||||
accessLevel: 'write-and-read',
|
||||
})
|
||||
expect(workspaceUserRepository.save).toHaveBeenCalledWith({
|
||||
encryptedPrivateKey: 'foo',
|
||||
publicKey: 'bar',
|
||||
status: 'pending-keyshare',
|
||||
userUuid: 'u-1-2-3',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
accessLevel: 'write-and-read',
|
||||
userDisplayName: 'test@test.te',
|
||||
})
|
||||
})
|
||||
|
||||
it('should not accept an invite if it does not exist', async () => {
|
||||
workspaceInviteRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createUseCase().execute({
|
||||
acceptingUserUuid: 'u-1-2-3',
|
||||
encryptedPrivateKey: 'foo',
|
||||
publicKey: 'bar',
|
||||
invitationUuid: 'i-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceInviteRepository.save).not.toHaveBeenCalled()
|
||||
expect(workspaceUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { WorkspaceUserStatus } from '@standardnotes/common'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { WorkspaceInviteRepositoryInterface } from '../../Invite/WorkspaceInviteRepositoryInterface'
|
||||
import { WorkspaceInviteStatus } from '../../Invite/WorkspaceInviteStatus'
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { AcceptInvitationDTO } from './AcceptInvitationDTO'
|
||||
import { AcceptInvitationResponse } from './AcceptInvitationResponse'
|
||||
|
||||
@injectable()
|
||||
export class AcceptInvitation implements UseCaseInterface {
|
||||
constructor(
|
||||
@inject(TYPES.WorkspaceInviteRepository) private workspaceInviteRepository: WorkspaceInviteRepositoryInterface,
|
||||
@inject(TYPES.WorkspaceUserRepository) private workspaceUserRepository: WorkspaceUserRepositoryInterface,
|
||||
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: AcceptInvitationDTO): Promise<AcceptInvitationResponse> {
|
||||
const invite = await this.workspaceInviteRepository.findOneByUuid(dto.invitationUuid)
|
||||
if (invite === null) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
invite.acceptingUserUuid = dto.acceptingUserUuid
|
||||
invite.updatedAt = this.timer.getTimestampInMicroseconds()
|
||||
invite.status = WorkspaceInviteStatus.Accepted
|
||||
|
||||
await this.workspaceInviteRepository.save(invite)
|
||||
|
||||
const workspaceUser = new WorkspaceUser()
|
||||
workspaceUser.userUuid = dto.acceptingUserUuid
|
||||
workspaceUser.userDisplayName = invite.inviteeEmail
|
||||
workspaceUser.workspaceUuid = invite.workspaceUuid
|
||||
workspaceUser.publicKey = dto.publicKey
|
||||
workspaceUser.encryptedPrivateKey = dto.encryptedPrivateKey
|
||||
workspaceUser.accessLevel = invite.accessLevel
|
||||
workspaceUser.status = WorkspaceUserStatus.PendingKeyshare
|
||||
|
||||
await this.workspaceUserRepository.save(workspaceUser)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type AcceptInvitationDTO = {
|
||||
invitationUuid: Uuid
|
||||
acceptingUserUuid: Uuid
|
||||
publicKey: string
|
||||
encryptedPrivateKey: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type AcceptInvitationResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { Workspace } from '../../Workspace/Workspace'
|
||||
import { WorkspaceAccessLevel } from '../../Workspace/WorkspaceAccessLevel'
|
||||
import { WorkspaceRepositoryInterface } from '../../Workspace/WorkspaceRepositoryInterface'
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
import { WorkspaceUserStatus } from '../../Workspace/WorkspaceUserStatus'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { CreateWorkspaceDTO } from './CreateWorkspaceDTO'
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
|
||||
import { InitiateKeyShare } from './InitiateKeyShare'
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
|
||||
describe('InitiateKeyShare', () => {
|
||||
let workspaceUserRepository: WorkspaceUserRepositoryInterface
|
||||
let timer: TimerInterface
|
||||
let workspaceUser: WorkspaceUser
|
||||
let workspaceOwner: WorkspaceUser
|
||||
|
||||
const createUseCase = () => new InitiateKeyShare(workspaceUserRepository, timer)
|
||||
|
||||
beforeEach(() => {
|
||||
workspaceOwner = {
|
||||
accessLevel: WorkspaceAccessLevel.Owner,
|
||||
} as jest.Mocked<WorkspaceUser>
|
||||
workspaceUser = {} as jest.Mocked<WorkspaceUser>
|
||||
|
||||
workspaceUserRepository = {} as jest.Mocked<WorkspaceUserRepositoryInterface>
|
||||
workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(workspaceOwner)
|
||||
.mockReturnValueOnce(workspaceUser)
|
||||
workspaceUserRepository.save = jest.fn().mockImplementation((user: WorkspaceUser) => user)
|
||||
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn().mockReturnValue(1)
|
||||
})
|
||||
|
||||
it('should update the workspace user with a workspace key and mark as active', async () => {
|
||||
await createUseCase().execute({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
performingUserUuid: 'o-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceUserRepository.save).toHaveBeenCalledWith({
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
status: 'active',
|
||||
updatedAt: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not initiate key share if workspace is not found', async () => {
|
||||
workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(workspaceOwner)
|
||||
.mockReturnValueOnce(null)
|
||||
|
||||
await createUseCase().execute({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
performingUserUuid: 'o-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not initiate key share if workspace performing user is not the owner or admin', async () => {
|
||||
workspaceOwner.accessLevel = WorkspaceAccessLevel.ReadOnly
|
||||
workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(workspaceOwner)
|
||||
.mockReturnValueOnce(workspaceUser)
|
||||
|
||||
await createUseCase().execute({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
performingUserUuid: 'o-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should not initiate key share if workspace performing user is found in workspace', async () => {
|
||||
workspaceOwner.accessLevel = WorkspaceAccessLevel.ReadOnly
|
||||
workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce(null)
|
||||
.mockReturnValueOnce(workspaceUser)
|
||||
|
||||
await createUseCase().execute({
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
encryptedWorkspaceKey: 'foobar',
|
||||
performingUserUuid: 'o-1-2-3',
|
||||
})
|
||||
|
||||
expect(workspaceUserRepository.save).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { InitiateKeyShareDTO } from './InitiateKeyShareDTO'
|
||||
import { InitiateKeyShareResponse } from './InitiateKeyShareResponse'
|
||||
|
||||
@injectable()
|
||||
export class InitiateKeyShare implements UseCaseInterface {
|
||||
constructor(
|
||||
@inject(TYPES.WorkspaceUserRepository) private workspaceUserRepository: WorkspaceUserRepositoryInterface,
|
||||
@inject(TYPES.Timer) private timer: TimerInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: InitiateKeyShareDTO): Promise<InitiateKeyShareResponse> {
|
||||
const workspaceOwner = await this.workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid({
|
||||
workspaceUuid: dto.workspaceUuid,
|
||||
userUuid: dto.performingUserUuid,
|
||||
})
|
||||
if (
|
||||
workspaceOwner === null ||
|
||||
![WorkspaceAccessLevel.Admin, WorkspaceAccessLevel.Owner].includes(workspaceOwner.accessLevel)
|
||||
) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceUser = await this.workspaceUserRepository.findOneByUserUuidAndWorkspaceUuid({
|
||||
workspaceUuid: dto.workspaceUuid,
|
||||
userUuid: dto.userUuid,
|
||||
})
|
||||
|
||||
if (workspaceUser === null) {
|
||||
return {
|
||||
success: false,
|
||||
}
|
||||
}
|
||||
|
||||
workspaceUser.encryptedWorkspaceKey = dto.encryptedWorkspaceKey
|
||||
workspaceUser.status = WorkspaceUserStatus.Active
|
||||
workspaceUser.updatedAt = this.timer.getTimestampInMicroseconds()
|
||||
|
||||
await this.workspaceUserRepository.save(workspaceUser)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type InitiateKeyShareDTO = {
|
||||
workspaceUuid: Uuid
|
||||
userUuid: Uuid
|
||||
performingUserUuid: Uuid
|
||||
encryptedWorkspaceKey: string
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export type InitiateKeyShareResponse = {
|
||||
success: boolean
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { WorkspaceInviteRepositoryInterface } from '../../Invite/WorkspaceInvite
|
||||
import { InviteToWorkspace } from './InviteToWorkspace'
|
||||
import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
|
||||
import { DomainEventPublisherInterface, WorkspaceInviteCreatedEvent } from '@standardnotes/domain-events'
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
|
||||
describe('InviteToWorkspace', () => {
|
||||
let workspaceInviteRepository: WorkspaceInviteRepositoryInterface
|
||||
@@ -42,11 +43,24 @@ describe('InviteToWorkspace', () => {
|
||||
inviteeEmail: 'test@test.te',
|
||||
inviterUuid: 'u-1-2-3',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
})
|
||||
|
||||
expect(result).toEqual({ uuid: 'i-1-2-3' })
|
||||
expect(result).toEqual({
|
||||
invite: {
|
||||
uuid: 'i-1-2-3',
|
||||
inviterUuid: 'u-1-2-3',
|
||||
inviteeEmail: 'test@test.te',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
status: 'created',
|
||||
accessLevel: 'write-and-read',
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
},
|
||||
})
|
||||
|
||||
expect(workspaceInviteRepository.save).toHaveBeenCalledWith({
|
||||
accessLevel: 'write-and-read',
|
||||
inviterUuid: 'u-1-2-3',
|
||||
inviteeEmail: 'test@test.te',
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
|
||||
@@ -26,6 +26,7 @@ export class InviteToWorkspace implements UseCaseInterface {
|
||||
invite.inviterUuid = dto.inviterUuid
|
||||
invite.inviteeEmail = dto.inviteeEmail
|
||||
invite.workspaceUuid = dto.workspaceUuid
|
||||
invite.accessLevel = dto.accessLevel
|
||||
invite.status = WorkspaceInviteStatus.Created
|
||||
|
||||
const timestamp = this.timer.getTimestampInMicroseconds()
|
||||
@@ -44,7 +45,7 @@ export class InviteToWorkspace implements UseCaseInterface {
|
||||
)
|
||||
|
||||
return {
|
||||
uuid: invite.uuid,
|
||||
invite,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { Uuid, WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
|
||||
export type InviteToWorkspaceDTO = {
|
||||
workspaceUuid: Uuid
|
||||
inviterUuid: Uuid
|
||||
inviteeEmail: string
|
||||
accessLevel: WorkspaceAccessLevel
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { WorkspaceInvite } from '../../Invite/WorkspaceInvite'
|
||||
|
||||
export type InviteToWorkspaceResponse = {
|
||||
uuid: string
|
||||
invite: WorkspaceInvite
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
import 'reflect-metadata'
|
||||
import { Workspace } from '../../Workspace/Workspace'
|
||||
import { WorkspaceRepositoryInterface } from '../../Workspace/WorkspaceRepositoryInterface'
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
|
||||
import { ListWorkspaceUsers } from './ListWorkspaceUsers'
|
||||
|
||||
describe('ListWorkspaceUsers', () => {
|
||||
let workspaceRepository: WorkspaceRepositoryInterface
|
||||
let workspaceUserRepository: WorkspaceUserRepositoryInterface
|
||||
let workspace: Workspace
|
||||
let workspaceUser1: WorkspaceUser
|
||||
let workspaceUser2: WorkspaceUser
|
||||
|
||||
const createUseCase = () => new ListWorkspaceUsers(workspaceRepository, workspaceUserRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
workspace = { uuid: 'j-1-2-3' } as jest.Mocked<Workspace>
|
||||
|
||||
workspaceUser1 = { userUuid: 'u-1-2-3', accessLevel: WorkspaceAccessLevel.Owner } as jest.Mocked<WorkspaceUser>
|
||||
workspaceUser2 = {
|
||||
userUuid: 'u-2-3-4',
|
||||
accessLevel: WorkspaceAccessLevel.WriteAndRead,
|
||||
} as jest.Mocked<WorkspaceUser>
|
||||
|
||||
workspaceRepository = {} as jest.Mocked<WorkspaceRepositoryInterface>
|
||||
workspaceRepository.findOneByUuid = jest.fn().mockReturnValue(workspace)
|
||||
|
||||
workspaceUserRepository = {} as jest.Mocked<WorkspaceUserRepositoryInterface>
|
||||
workspaceUserRepository.findByWorkspaceUuid = jest.fn().mockReturnValue([workspaceUser1, workspaceUser2])
|
||||
})
|
||||
|
||||
it('should list users in a workspace where the user is owner or admin', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'u-1-2-3',
|
||||
workspaceUuid: 'j-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
workspaceUsers: [workspaceUser1, workspaceUser2],
|
||||
userIsOwnerOrAdmin: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should list users in a workspace where the user is not the owner or admin with indiciation', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'u-2-3-4',
|
||||
workspaceUuid: 'j-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
workspaceUsers: [workspaceUser1, workspaceUser2],
|
||||
userIsOwnerOrAdmin: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not list users in a workspace where the user does not belong', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'z-1-2-3',
|
||||
workspaceUuid: 'j-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
workspaceUsers: [],
|
||||
userIsOwnerOrAdmin: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should not list users in a workspace that does not exist', async () => {
|
||||
workspaceRepository.findOneByUuid = jest.fn().mockReturnValue(null)
|
||||
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'u-1-2-3',
|
||||
workspaceUuid: 'j-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
workspaceUsers: [],
|
||||
userIsOwnerOrAdmin: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
import { inject, injectable } from 'inversify'
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { WorkspaceRepositoryInterface } from '../../Workspace/WorkspaceRepositoryInterface'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
import { ListWorkspaceUsersDTO } from './ListWorkspaceUsersDTO'
|
||||
import { ListWorkspaceUsersResponse } from './ListWorkspaceUsersResponse'
|
||||
|
||||
@injectable()
|
||||
export class ListWorkspaceUsers implements UseCaseInterface {
|
||||
constructor(
|
||||
@inject(TYPES.WorkspaceRepository) private workspaceRepository: WorkspaceRepositoryInterface,
|
||||
@inject(TYPES.WorkspaceUserRepository) private workspaceUserRepository: WorkspaceUserRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: ListWorkspaceUsersDTO): Promise<ListWorkspaceUsersResponse> {
|
||||
const workspace = await this.workspaceRepository.findOneByUuid(dto.workspaceUuid)
|
||||
if (workspace === null) {
|
||||
return {
|
||||
workspaceUsers: [],
|
||||
userIsOwnerOrAdmin: false,
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceUsers = await this.workspaceUserRepository.findByWorkspaceUuid(dto.workspaceUuid)
|
||||
let userIsOwnerOrAdmin = false
|
||||
let userIsInWorkspace = false
|
||||
for (const workspaceUser of workspaceUsers) {
|
||||
if (workspaceUser.userUuid === dto.userUuid) {
|
||||
userIsInWorkspace = true
|
||||
if ([WorkspaceAccessLevel.Admin, WorkspaceAccessLevel.Owner].includes(workspaceUser.accessLevel)) {
|
||||
userIsOwnerOrAdmin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIsInWorkspace) {
|
||||
return {
|
||||
workspaceUsers: [],
|
||||
userIsOwnerOrAdmin: false,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
workspaceUsers,
|
||||
userIsOwnerOrAdmin,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type ListWorkspaceUsersDTO = {
|
||||
workspaceUuid: Uuid
|
||||
userUuid: Uuid
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
|
||||
export type ListWorkspaceUsersResponse = {
|
||||
workspaceUsers: WorkspaceUser[]
|
||||
userIsOwnerOrAdmin: boolean
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
import 'reflect-metadata'
|
||||
import { Workspace } from '../../Workspace/Workspace'
|
||||
import { WorkspaceRepositoryInterface } from '../../Workspace/WorkspaceRepositoryInterface'
|
||||
import { WorkspaceUser } from '../../Workspace/WorkspaceUser'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
|
||||
import { ListWorkspaces } from './ListWorkspaces'
|
||||
|
||||
describe('ListWorkspaces', () => {
|
||||
let workspaceRepository: WorkspaceRepositoryInterface
|
||||
let workspaceUserRepository: WorkspaceUserRepositoryInterface
|
||||
let ownedWorkspace: Workspace
|
||||
let joinedWorkspace: Workspace
|
||||
let workspaceUser1: WorkspaceUser
|
||||
let workspaceUser2: WorkspaceUser
|
||||
|
||||
const createUseCase = () => new ListWorkspaces(workspaceRepository, workspaceUserRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
ownedWorkspace = { uuid: 'o-1-2-3' } as jest.Mocked<Workspace>
|
||||
joinedWorkspace = { uuid: 'j-1-2-3' } as jest.Mocked<Workspace>
|
||||
|
||||
workspaceUser1 = { accessLevel: WorkspaceAccessLevel.Owner } as jest.Mocked<WorkspaceUser>
|
||||
workspaceUser2 = { accessLevel: WorkspaceAccessLevel.WriteAndRead } as jest.Mocked<WorkspaceUser>
|
||||
|
||||
workspaceRepository = {} as jest.Mocked<WorkspaceRepositoryInterface>
|
||||
workspaceRepository.findByUuids = jest
|
||||
.fn()
|
||||
.mockReturnValueOnce([ownedWorkspace])
|
||||
.mockReturnValueOnce([joinedWorkspace])
|
||||
|
||||
workspaceUserRepository = {} as jest.Mocked<WorkspaceUserRepositoryInterface>
|
||||
workspaceUserRepository.findByUserUuid = jest.fn().mockReturnValue([workspaceUser1, workspaceUser2])
|
||||
})
|
||||
|
||||
it('should list owned and joined workspaces for a user', async () => {
|
||||
const result = await createUseCase().execute({
|
||||
userUuid: 'u-1-2-3',
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
ownedWorkspaces: [ownedWorkspace],
|
||||
joinedWorkspaces: [joinedWorkspace],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
import { WorkspaceAccessLevel } from '@standardnotes/common'
|
||||
import { inject, injectable } from 'inversify'
|
||||
|
||||
import TYPES from '../../../Bootstrap/Types'
|
||||
import { WorkspaceRepositoryInterface } from '../../Workspace/WorkspaceRepositoryInterface'
|
||||
import { WorkspaceUserRepositoryInterface } from '../../Workspace/WorkspaceUserRepositoryInterface'
|
||||
import { UseCaseInterface } from '../UseCaseInterface'
|
||||
|
||||
import { ListWorkspacesDTO } from './ListWorkspacesDTO'
|
||||
import { ListWorkspacesResponse } from './ListWorkspacesResponse'
|
||||
|
||||
@injectable()
|
||||
export class ListWorkspaces implements UseCaseInterface {
|
||||
constructor(
|
||||
@inject(TYPES.WorkspaceRepository) private workspaceRepository: WorkspaceRepositoryInterface,
|
||||
@inject(TYPES.WorkspaceUserRepository) private workspaceUserRepository: WorkspaceUserRepositoryInterface,
|
||||
) {}
|
||||
|
||||
async execute(dto: ListWorkspacesDTO): Promise<ListWorkspacesResponse> {
|
||||
const workspaceAssociations = await this.workspaceUserRepository.findByUserUuid(dto.userUuid)
|
||||
|
||||
const ownedWorkspacesUuids = []
|
||||
const joinedWorkspacesUuids = []
|
||||
for (const workspaceAssociation of workspaceAssociations) {
|
||||
if ([WorkspaceAccessLevel.Admin, WorkspaceAccessLevel.Owner].includes(workspaceAssociation.accessLevel)) {
|
||||
ownedWorkspacesUuids.push(workspaceAssociation.uuid)
|
||||
} else {
|
||||
joinedWorkspacesUuids.push(workspaceAssociation.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
const ownedWorkspaces = await this.workspaceRepository.findByUuids(ownedWorkspacesUuids)
|
||||
const joinedWorkspaces = await this.workspaceRepository.findByUuids(joinedWorkspacesUuids)
|
||||
|
||||
return {
|
||||
ownedWorkspaces,
|
||||
joinedWorkspaces,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
|
||||
export type ListWorkspacesDTO = {
|
||||
userUuid: Uuid
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { Workspace } from '../../Workspace/Workspace'
|
||||
|
||||
export type ListWorkspacesResponse = {
|
||||
ownedWorkspaces: Array<Workspace>
|
||||
joinedWorkspaces: Array<Workspace>
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { Workspace } from './Workspace'
|
||||
|
||||
export interface WorkspaceRepositoryInterface {
|
||||
save(workspace: Workspace): Promise<Workspace>
|
||||
findByUuids(uuids: Uuid[]): Promise<Workspace[]>
|
||||
findOneByUuid(uuid: Uuid): Promise<Workspace | null>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { WorkspaceAccessLevel, WorkspaceUserStatus } from '@standardnotes/common'
|
||||
import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm'
|
||||
import { WorkspaceAccessLevel } from './WorkspaceAccessLevel'
|
||||
import { WorkspaceUserStatus } from './WorkspaceUserStatus'
|
||||
|
||||
@Entity({ name: 'workspace_users' })
|
||||
@Index('index_workspace_users_on_workspace_and_user', ['userUuid', 'workspaceUuid'], { unique: true })
|
||||
@@ -20,6 +19,14 @@ export class WorkspaceUser {
|
||||
})
|
||||
declare userUuid: string
|
||||
|
||||
@Column({
|
||||
name: 'user_display_name',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
declare userDisplayName: string | null
|
||||
|
||||
@Column({
|
||||
name: 'workspace_uuid',
|
||||
length: 36,
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Uuid } from '@standardnotes/common'
|
||||
import { WorkspaceUser } from './WorkspaceUser'
|
||||
|
||||
export interface WorkspaceUserRepositoryInterface {
|
||||
save(workspace: WorkspaceUser): Promise<WorkspaceUser>
|
||||
findByUserUuid(userUuid: Uuid): Promise<WorkspaceUser[]>
|
||||
findByWorkspaceUuid(workspaceUuid: Uuid): Promise<WorkspaceUser[]>
|
||||
findOneByUserUuidAndWorkspaceUuid(dto: { workspaceUuid: Uuid; userUuid: Uuid }): Promise<WorkspaceUser | null>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, httpPost, results } from 'inversify-express-utils'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { WorkspacesController } from '../../Controller/WorkspacesController'
|
||||
|
||||
@controller('/invites', TYPES.ApiGatewayAuthMiddleware)
|
||||
export class InversifyExpressInvitesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.WorkspacesController) private workspacesController: WorkspacesController) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/:inviteUuid/accept')
|
||||
async acceptInvite(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.workspacesController.acceptInvite({
|
||||
...request.body,
|
||||
inviteUuid: request.params.inviteUuid,
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
import { Request, Response } from 'express'
|
||||
import { inject } from 'inversify'
|
||||
import { BaseHttpController, controller, httpPost, results } from 'inversify-express-utils'
|
||||
import { BaseHttpController, controller, httpGet, httpPost, results } from 'inversify-express-utils'
|
||||
import TYPES from '../../Bootstrap/Types'
|
||||
import { WorkspacesController } from '../../Controller/WorkspacesController'
|
||||
|
||||
@controller('/workspaces')
|
||||
@controller('/workspaces', TYPES.ApiGatewayAuthMiddleware)
|
||||
export class InversifyExpressWorkspacesController extends BaseHttpController {
|
||||
constructor(@inject(TYPES.WorkspacesController) private workspacesController: WorkspacesController) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpPost('/', TYPES.ApiGatewayAuthMiddleware)
|
||||
@httpPost('/')
|
||||
async create(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.workspacesController.createWorkspace({
|
||||
...request.body,
|
||||
@@ -19,4 +19,56 @@ export class InversifyExpressWorkspacesController extends BaseHttpController {
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
|
||||
@httpGet('/')
|
||||
async listWorkspaces(response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.workspacesController.listWorkspaces({
|
||||
userUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
|
||||
@httpGet('/:workspaceUuid/users')
|
||||
async listWorkspaceUsers(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.workspacesController.listWorkspaceUsers({
|
||||
userUuid: response.locals.user.uuid,
|
||||
workspaceUuid: request.params.workspaceUuid,
|
||||
})
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/users/:userUuid/keyshare')
|
||||
async initiateKeyshare(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
const result = await this.workspacesController.initiateKeyshare({
|
||||
userUuid: request.params.userUuid,
|
||||
workspaceUuid: request.params.workspaceUuid,
|
||||
encryptedWorkspaceKey: request.body.encryptedWorkspaceKey,
|
||||
performingUserUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
|
||||
@httpPost('/:workspaceUuid/invites')
|
||||
async inviteToWorkspace(request: Request, response: Response): Promise<results.JsonResult> {
|
||||
if (request.params.workspaceUuid !== request.body.workspaceUuid) {
|
||||
return this.json(
|
||||
{
|
||||
error: {
|
||||
message: 'Invalid workspace uuid.',
|
||||
},
|
||||
},
|
||||
400,
|
||||
)
|
||||
}
|
||||
|
||||
const result = await this.workspacesController.inviteToWorkspace({
|
||||
...request.body,
|
||||
inviterUuid: response.locals.user.uuid,
|
||||
})
|
||||
|
||||
return this.json(result.data, result.status)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,13 @@ describe('MySQLWorkspaceInviteRepository', () => {
|
||||
|
||||
expect(ormRepository.save).toHaveBeenCalledWith(invite)
|
||||
})
|
||||
|
||||
it('should find one by uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createRepository().findOneByUuid('i-1-2-3')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('uuid = :uuid', { uuid: 'i-1-2-3' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,6 +12,10 @@ export class MySQLWorkspaceInviteRepository implements WorkspaceInviteRepository
|
||||
private ormRepository: Repository<WorkspaceInvite>,
|
||||
) {}
|
||||
|
||||
async findOneByUuid(uuid: string): Promise<WorkspaceInvite | null> {
|
||||
return this.ormRepository.createQueryBuilder().where('uuid = :uuid', { uuid }).getOne()
|
||||
}
|
||||
|
||||
async save(workspaceInvite: WorkspaceInvite): Promise<WorkspaceInvite> {
|
||||
return this.ormRepository.save(workspaceInvite)
|
||||
}
|
||||
|
||||
@@ -28,4 +28,22 @@ describe('MySQLWorkspaceRepository', () => {
|
||||
|
||||
expect(ormRepository.save).toHaveBeenCalledWith(workspace)
|
||||
})
|
||||
|
||||
it('should find many by uuids', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([])
|
||||
|
||||
await createRepository().findByUuids(['i-1-2-3'])
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('uuid IN (:...uuids)', { uuids: ['i-1-2-3'] })
|
||||
})
|
||||
|
||||
it('should find one by uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createRepository().findOneByUuid('i-1-2-3')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('uuid = :uuid', { uuid: 'i-1-2-3' })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,6 +11,14 @@ export class MySQLWorkspaceRepository implements WorkspaceRepositoryInterface {
|
||||
private ormRepository: Repository<Workspace>,
|
||||
) {}
|
||||
|
||||
async findOneByUuid(uuid: string): Promise<Workspace | null> {
|
||||
return this.ormRepository.createQueryBuilder().where('uuid = :uuid', { uuid }).getOne()
|
||||
}
|
||||
|
||||
async findByUuids(uuids: string[]): Promise<Workspace[]> {
|
||||
return this.ormRepository.createQueryBuilder().where('uuid IN (:...uuids)', { uuids }).getMany()
|
||||
}
|
||||
|
||||
async save(workspace: Workspace): Promise<Workspace> {
|
||||
return this.ormRepository.save(workspace)
|
||||
}
|
||||
|
||||
@@ -27,4 +27,34 @@ describe('MySQLWorkspaceUserRepository', () => {
|
||||
|
||||
expect(ormRepository.save).toHaveBeenCalledWith(workspace)
|
||||
})
|
||||
|
||||
it('should find many by user uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([])
|
||||
|
||||
await createRepository().findByUserUuid('i-1-2-3')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('user_uuid = :userUuid', { userUuid: 'i-1-2-3' })
|
||||
})
|
||||
|
||||
it('should find many by workspace uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([])
|
||||
|
||||
await createRepository().findByWorkspaceUuid('i-1-2-3')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('workspace_uuid = :workspaceUuid', { workspaceUuid: 'i-1-2-3' })
|
||||
})
|
||||
|
||||
it('should find one by workspace uuid and user uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(null)
|
||||
|
||||
await createRepository().findOneByUserUuidAndWorkspaceUuid({ workspaceUuid: 'w-1-2-3', userUuid: 'u-1-2-3' })
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('workspace_uuid = :workspaceUuid AND user_uuid = :userUuid', {
|
||||
workspaceUuid: 'w-1-2-3',
|
||||
userUuid: 'u-1-2-3',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,6 +12,27 @@ export class MySQLWorkspaceUserRepository implements WorkspaceUserRepositoryInte
|
||||
private ormRepository: Repository<WorkspaceUser>,
|
||||
) {}
|
||||
|
||||
async findOneByUserUuidAndWorkspaceUuid(dto: {
|
||||
workspaceUuid: string
|
||||
userUuid: string
|
||||
}): Promise<WorkspaceUser | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder()
|
||||
.where('workspace_uuid = :workspaceUuid AND user_uuid = :userUuid', {
|
||||
workspaceUuid: dto.workspaceUuid,
|
||||
userUuid: dto.userUuid,
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async findByWorkspaceUuid(workspaceUuid: string): Promise<WorkspaceUser[]> {
|
||||
return this.ormRepository.createQueryBuilder().where('workspace_uuid = :workspaceUuid', { workspaceUuid }).getMany()
|
||||
}
|
||||
|
||||
async findByUserUuid(userUuid: string): Promise<WorkspaceUser[]> {
|
||||
return this.ormRepository.createQueryBuilder().where('user_uuid = :userUuid', { userUuid }).getMany()
|
||||
}
|
||||
|
||||
async save(workspaceUser: WorkspaceUser): Promise<WorkspaceUser> {
|
||||
return this.ormRepository.save(workspaceUser)
|
||||
}
|
||||
|
||||
179
yarn.lock
179
yarn.lock
@@ -1824,33 +1824,18 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/api@npm:^1.11.0":
|
||||
version: 1.11.0
|
||||
resolution: "@standardnotes/api@npm:1.11.0"
|
||||
"@standardnotes/api@npm:^1.16.0":
|
||||
version: 1.16.0
|
||||
resolution: "@standardnotes/api@npm:1.16.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/encryption": 1.16.0
|
||||
"@standardnotes/models": 1.24.0
|
||||
"@standardnotes/responses": 1.10.4
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/encryption": 1.17.1
|
||||
"@standardnotes/models": 1.27.0
|
||||
"@standardnotes/responses": 1.11.0
|
||||
"@standardnotes/security": ^1.1.0
|
||||
"@standardnotes/utils": 1.9.0
|
||||
"@standardnotes/utils": 1.10.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: f1134efb44a7a2cc8f117bc6a7826d89e11f7780d7ed6f16f654f1a96987cbe6f445ec5f5a91abb11fc79a1e621d9bab30f46fb7a746d44bb5e8b81904d9370f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/api@npm:^1.9.0":
|
||||
version: 1.9.0
|
||||
resolution: "@standardnotes/api@npm:1.9.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/encryption": 1.15.9
|
||||
"@standardnotes/models": 1.22.0
|
||||
"@standardnotes/responses": 1.10.3
|
||||
"@standardnotes/security": ^1.1.0
|
||||
"@standardnotes/utils": 1.9.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: cc3feac3935a382e0ce1fcaf233206a547b6c998cb99ab362d5c7030b3f4e7cbbd3a083eab40bdecbcdc9497dcd283e4513e29dbf200e815ffa46b192ed61b01
|
||||
checksum: 465f76dd29f0207019cf16e916ab7495e0f09c5e332225a5e089bcc26b6969754b9c813f132cbc9225830705dc9d88b03fa9cbdce8f84e45ffb303be410b260f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -1861,7 +1846,7 @@ __metadata:
|
||||
"@newrelic/winston-enricher": ^4.0.0
|
||||
"@sentry/node": ^7.3.0
|
||||
"@standardnotes/analytics": "workspace:*"
|
||||
"@standardnotes/api": ^1.9.0
|
||||
"@standardnotes/api": ^1.16.0
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-events": "workspace:*"
|
||||
"@standardnotes/domain-events-infra": "workspace:*"
|
||||
@@ -1922,7 +1907,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/common@^1.19.1, @standardnotes/common@^1.23.1, @standardnotes/common@^1.32.0, @standardnotes/common@workspace:*, @standardnotes/common@workspace:^, @standardnotes/common@workspace:packages/common":
|
||||
"@standardnotes/common@^1.19.1, @standardnotes/common@^1.23.1, @standardnotes/common@^1.32.0, @standardnotes/common@^1.39.0, @standardnotes/common@workspace:*, @standardnotes/common@workspace:^, @standardnotes/common@workspace:packages/common":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@standardnotes/common@workspace:packages/common"
|
||||
dependencies:
|
||||
@@ -1987,31 +1972,17 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/encryption@npm:1.15.9":
|
||||
version: 1.15.9
|
||||
resolution: "@standardnotes/encryption@npm:1.15.9"
|
||||
"@standardnotes/encryption@npm:1.17.1":
|
||||
version: 1.17.1
|
||||
resolution: "@standardnotes/encryption@npm:1.17.1"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/models": 1.22.0
|
||||
"@standardnotes/responses": 1.10.3
|
||||
"@standardnotes/sncrypto-common": 1.12.0
|
||||
"@standardnotes/utils": 1.9.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 7595ac08cea6e54e1456cbff3958969318d90ae237aff166bc4429da5bcf6167c5eb03aa8658a1747486a0639b80b523dae46106ab253c75d24579643cf3e948
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/encryption@npm:1.16.0":
|
||||
version: 1.16.0
|
||||
resolution: "@standardnotes/encryption@npm:1.16.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/models": 1.24.0
|
||||
"@standardnotes/responses": 1.10.4
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/models": 1.27.0
|
||||
"@standardnotes/responses": 1.11.0
|
||||
"@standardnotes/sncrypto-common": 1.13.0
|
||||
"@standardnotes/utils": 1.9.0
|
||||
"@standardnotes/utils": 1.10.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 9971b9afcc8d32c7a6d720b43b7e098659d40212abfe1c4387f6b8aee4f410198a0e28db61bfdfdd8defe8cd336a9823e4cc565bf8f72145da2d349cb4be12a6
|
||||
checksum: 2b2408ffbd3440a89d7b2ddbaf8d50563ea126cd37c8d8ba9aa2b9cd4682f394a28fe16a5e5161685c49866def00a2cc13c3947c2b81e656f94713ac8c526a13
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2043,27 +2014,15 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/features@npm:1.52.1, @standardnotes/features@npm:^1.52.1":
|
||||
version: 1.52.1
|
||||
resolution: "@standardnotes/features@npm:1.52.1"
|
||||
"@standardnotes/features@npm:1.53.0":
|
||||
version: 1.53.0
|
||||
resolution: "@standardnotes/features@npm:1.53.0"
|
||||
dependencies:
|
||||
"@standardnotes/auth": ^3.19.4
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/security": ^1.2.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: ff3684399e0e0c0e799f11e69dddea2e1f65f315e5a5dd3ca5640e24e836ee85faf1f5ee15fc804411bf083004527fcef08411d5c2d0b5894491bf2f28ceca68
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/features@npm:1.52.2":
|
||||
version: 1.52.2
|
||||
resolution: "@standardnotes/features@npm:1.52.2"
|
||||
dependencies:
|
||||
"@standardnotes/auth": ^3.19.4
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/security": ^1.2.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: ab345f8dc11be32967e439366de0507707a95590b898d6c4332d5d42adbba7bb3fe32bca2ee1fd9948b9106d8186402439916402e8df50cf9e440996420251df
|
||||
checksum: a856e815a313d42706836b1e4c8bbaeb987ee86f2e53bf4b928ed401de483ab11ecd31a8bcb13b23cb7b6a53f72a9f481da2acf68d5e0e970af4ce8c8f2e7c65
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2078,6 +2037,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/features@npm:^1.52.1":
|
||||
version: 1.52.1
|
||||
resolution: "@standardnotes/features@npm:1.52.1"
|
||||
dependencies:
|
||||
"@standardnotes/auth": ^3.19.4
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/security": ^1.2.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: ff3684399e0e0c0e799f11e69dddea2e1f65f315e5a5dd3ca5640e24e836ee85faf1f5ee15fc804411bf083004527fcef08411d5c2d0b5894491bf2f28ceca68
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/files-server@workspace:packages/files":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@standardnotes/files-server@workspace:packages/files"
|
||||
@@ -2128,31 +2099,31 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/models@npm:1.22.0":
|
||||
version: 1.22.0
|
||||
resolution: "@standardnotes/models@npm:1.22.0"
|
||||
"@standardnotes/models@npm:1.27.0":
|
||||
version: 1.27.0
|
||||
resolution: "@standardnotes/models@npm:1.27.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/features": 1.52.1
|
||||
"@standardnotes/responses": 1.10.3
|
||||
"@standardnotes/utils": 1.9.0
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/features": 1.53.0
|
||||
"@standardnotes/responses": 1.11.0
|
||||
"@standardnotes/utils": 1.10.0
|
||||
lodash: ^4.17.21
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 9928246368b7de7062314374219065507642ed3b6764c27f14ed8d42f0c5a9370fab8731a43a57885a000e461ea60694ba4caa7d9940350839d487cedb7079b5
|
||||
checksum: 263fd9e9233daa997060c538cc48df8fb5598be6464fe952f79d3fb90ce759ddb8279516ecc8b56c763ee3488c2783218d289b40020835db7c51047cb21196ad
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/models@npm:1.24.0":
|
||||
version: 1.24.0
|
||||
resolution: "@standardnotes/models@npm:1.24.0"
|
||||
"@standardnotes/models@npm:^1.26.0":
|
||||
version: 1.26.0
|
||||
resolution: "@standardnotes/models@npm:1.26.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/features": 1.52.2
|
||||
"@standardnotes/responses": 1.10.4
|
||||
"@standardnotes/utils": 1.9.0
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/features": 1.53.0
|
||||
"@standardnotes/responses": 1.11.0
|
||||
"@standardnotes/utils": 1.10.0
|
||||
lodash: ^4.17.21
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 2acbbbc0629011bc9c901f2e61df936b13f349ff90409072dcfecf4899872f6d0e80d22bd45af9fc0928adc33097fd573983d0f194c4c84e7bf204a28de9943d
|
||||
checksum: f595a3de88ca815d04190ff188c1355ac1848369f94b81f7fc7be3a03918eaaa964eb078bb77622efd1ba7e2db1a47595db94e3ecc90c3544a54838a3980d6b0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2181,27 +2152,15 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/responses@npm:1.10.3":
|
||||
version: 1.10.3
|
||||
resolution: "@standardnotes/responses@npm:1.10.3"
|
||||
"@standardnotes/responses@npm:1.11.0":
|
||||
version: 1.11.0
|
||||
resolution: "@standardnotes/responses@npm:1.11.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/features": 1.52.1
|
||||
"@standardnotes/common": ^1.39.0
|
||||
"@standardnotes/features": 1.53.0
|
||||
"@standardnotes/security": ^1.1.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 4a1e31eb89342461488f00c65884839d7d935d47d4e217da83967c7cbe181fd0341ee58b744fe607def30d06cd1a8069c3b54f40b3933765303db35ee63f6a0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/responses@npm:1.10.4":
|
||||
version: 1.10.4
|
||||
resolution: "@standardnotes/responses@npm:1.10.4"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/features": 1.52.2
|
||||
"@standardnotes/security": ^1.1.0
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 41e4971144950e168c0de3d05a2871857d9a416ad8061849b646992f04d6590dce43e8161af0cb05c3faed1856fd4bf93fceea3fcabeebf62b5761e5376ce8c6
|
||||
checksum: 46d6a479806507b15dc6f949e8863915f2cac81964aac715aab0bfc063e7bd7961767fcc6488e08397a0e1a52ccf2808429d8660b98a79415e25f601719125dc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2301,15 +2260,6 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/sncrypto-common@npm:1.12.0":
|
||||
version: 1.12.0
|
||||
resolution: "@standardnotes/sncrypto-common@npm:1.12.0"
|
||||
dependencies:
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: b89a14bd233cb781213b2e25dd8d7bfe911820d341903f2987647c168745c3afb710ad94fa42e50aaafb644ee4a7b5fd64ec137f622cfc082951fd584af0d230
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standardnotes/sncrypto-common@npm:1.13.0":
|
||||
version: 1.13.0
|
||||
resolution: "@standardnotes/sncrypto-common@npm:1.13.0"
|
||||
@@ -2413,15 +2363,15 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@standardnotes/utils@npm:1.9.0":
|
||||
version: 1.9.0
|
||||
resolution: "@standardnotes/utils@npm:1.9.0"
|
||||
"@standardnotes/utils@npm:1.10.0":
|
||||
version: 1.10.0
|
||||
resolution: "@standardnotes/utils@npm:1.10.0"
|
||||
dependencies:
|
||||
"@standardnotes/common": ^1.32.0
|
||||
"@standardnotes/common": ^1.39.0
|
||||
dompurify: ^2.3.8
|
||||
lodash: ^4.17.21
|
||||
reflect-metadata: ^0.1.13
|
||||
checksum: 4591aff48d074b30b911f96c63eaaf521ab49563507672fbd4d7fe460e51f88a45effb002d1c82cca3513d2199c0cdb720556b03ec3e0266f593317c8efa764a
|
||||
checksum: c02d54ca8a4debb62ecf6642825e1c313b3aad11767b1f0e6d36a976ec3058eba8592ab3194544042825296357676bc821e168489b638b0514bd1650dc9d0e0d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2442,10 +2392,11 @@ __metadata:
|
||||
dependencies:
|
||||
"@newrelic/winston-enricher": ^4.0.0
|
||||
"@sentry/node": ^7.3.0
|
||||
"@standardnotes/api": ^1.11.0
|
||||
"@standardnotes/api": ^1.16.0
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/domain-events": "workspace:^"
|
||||
"@standardnotes/domain-events-infra": "workspace:^"
|
||||
"@standardnotes/models": ^1.26.0
|
||||
"@standardnotes/security": "workspace:*"
|
||||
"@standardnotes/time": "workspace:^"
|
||||
"@types/cors": ^2.8.9
|
||||
|
||||
Reference in New Issue
Block a user