@jskit-ai/workspaces-core 0.1.31 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.descriptor.mjs +11 -22
- package/package.json +11 -9
- package/src/server/WorkspacesCoreServiceProvider.js +22 -2
- package/src/server/common/repositories/workspaceInvitesRepository.js +233 -78
- package/src/server/common/repositories/workspaceMembershipsRepository.js +177 -86
- package/src/server/common/repositories/workspacesRepository.js +179 -86
- package/src/server/common/services/workspaceContextService.js +26 -24
- package/src/server/common/validators/routeParamsValidator.js +36 -53
- package/src/server/registerWorkspaceCore.js +6 -7
- package/src/server/registerWorkspaceRepositories.js +7 -3
- package/src/server/support/workspaceServerScopeSupport.js +1 -1
- package/src/server/workspaceBootstrapContributor.js +5 -14
- package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +54 -27
- package/src/server/workspaceDirectory/workspaceDirectoryActions.js +30 -24
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +70 -32
- package/src/server/workspaceMembers/workspaceMembersActions.js +61 -27
- package/src/server/workspaceMembers/workspaceMembersService.js +43 -7
- package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +28 -13
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +13 -15
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +33 -10
- package/src/server/workspaceSettings/bootWorkspaceSettings.js +32 -13
- package/src/server/workspaceSettings/registerWorkspaceSettings.js +5 -1
- package/src/server/workspaceSettings/workspaceSettingsActions.js +18 -12
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +104 -91
- package/src/server/workspaceSettings/workspaceSettingsService.js +5 -6
- package/src/shared/jsonApiTransports.js +79 -0
- package/src/shared/resources/workspaceInvitesResource.js +158 -0
- package/src/shared/resources/workspaceMembersResource.js +176 -311
- package/src/shared/resources/workspaceMembershipsResource.js +96 -0
- package/src/shared/resources/workspacePendingInvitationsResource.js +25 -72
- package/src/shared/resources/workspaceResource.js +113 -144
- package/src/shared/resources/workspaceRoleCatalogSchema.js +31 -0
- package/src/shared/resources/workspaceSettingsResource.js +276 -148
- package/test/repositoryContracts.test.js +16 -4
- package/test/resourcesCanonical.test.js +39 -16
- package/test/routeParamsValidator.test.js +37 -19
- package/test/usersRouteResources.test.js +27 -17
- package/test/workspaceActionContextContributor.test.js +1 -1
- package/test/workspaceInternalCrudResources.test.js +98 -0
- package/test/workspaceInvitesRepository.test.js +196 -148
- package/test/workspaceMembersResource.test.js +35 -0
- package/test/workspaceMembershipsRepository.test.js +155 -115
- package/test/workspacePendingInvitationsResource.test.js +18 -23
- package/test/workspacePendingInvitationsService.test.js +2 -1
- package/test/workspaceServerScopeSupport.test.js +21 -3
- package/test/workspaceSettingsActions.test.js +5 -7
- package/test/workspaceSettingsInternalResource.test.js +8 -0
- package/test/workspaceSettingsRepository.test.js +158 -123
- package/test/workspaceSettingsResource.test.js +51 -62
- package/test/workspaceSettingsService.test.js +0 -1
- package/test/workspacesRepository.test.js +318 -174
- package/test/workspacesRouteRequestInputValidator.test.js +25 -11
- package/src/server/common/resources/workspaceInvitesResource.js +0 -207
- package/src/server/common/resources/workspaceMembershipsResource.js +0 -154
- package/src/server/common/resources/workspacesResource.js +0 -170
- package/src/server/common/validators/authenticatedUserValidator.js +0 -43
- package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
- package/src/shared/resources/workspaceSettingsFields.js +0 -65
- package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
- package/test/settingsFieldRegistriesSingleton.test.js +0 -14
- package/test-support/registerDefaultSettingsFields.js +0 -1
package/package.descriptor.mjs
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/workspaces-core",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.32",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Workspace tenancy runtime plus HTTP routes, role catalog, and workspace config scaffolding.",
|
|
7
7
|
dependsOn: [
|
|
8
|
+
"@jskit-ai/json-rest-api-core",
|
|
9
|
+
"@jskit-ai/resource-core",
|
|
10
|
+
"@jskit-ai/resource-crud-core",
|
|
8
11
|
"@jskit-ai/users-core"
|
|
9
12
|
],
|
|
10
13
|
capabilities: {
|
|
@@ -13,7 +16,8 @@ export default Object.freeze({
|
|
|
13
16
|
"workspaces.server-routes"
|
|
14
17
|
],
|
|
15
18
|
requires: [
|
|
16
|
-
"users.core"
|
|
19
|
+
"users.core",
|
|
20
|
+
"json-rest-api.core"
|
|
17
21
|
]
|
|
18
22
|
},
|
|
19
23
|
runtime: {
|
|
@@ -112,7 +116,10 @@ export default Object.freeze({
|
|
|
112
116
|
mutations: {
|
|
113
117
|
dependencies: {
|
|
114
118
|
runtime: {
|
|
115
|
-
"@jskit-ai/
|
|
119
|
+
"@jskit-ai/json-rest-api-core": "0.1.1",
|
|
120
|
+
"@jskit-ai/resource-core": "0.1.1",
|
|
121
|
+
"@jskit-ai/resource-crud-core": "0.1.1",
|
|
122
|
+
"@jskit-ai/users-core": "0.1.66"
|
|
116
123
|
},
|
|
117
124
|
dev: {}
|
|
118
125
|
},
|
|
@@ -147,18 +154,10 @@ export default Object.freeze({
|
|
|
147
154
|
from: "templates/migrations/workspaces_core_workspaces_drop_color.cjs",
|
|
148
155
|
toDir: "migrations",
|
|
149
156
|
extension: ".cjs",
|
|
150
|
-
reason: "Drop
|
|
157
|
+
reason: "Drop workspaces.color now that workspace theme colors live in workspace_settings.",
|
|
151
158
|
category: "migration",
|
|
152
159
|
id: "users-core-workspaces-drop-color"
|
|
153
160
|
},
|
|
154
|
-
{
|
|
155
|
-
from: "templates/packages/main/src/shared/resources/workspaceSettingsFields.js",
|
|
156
|
-
to: "packages/main/src/shared/resources/workspaceSettingsFields.js",
|
|
157
|
-
preserveOnRemove: true,
|
|
158
|
-
reason: "Install app-owned workspace settings field definitions.",
|
|
159
|
-
category: "workspaces-core",
|
|
160
|
-
id: "users-core-app-owned-workspace-settings-fields"
|
|
161
|
-
},
|
|
162
161
|
{
|
|
163
162
|
from: "templates/config/roles.js",
|
|
164
163
|
to: "config/roles.js",
|
|
@@ -169,16 +168,6 @@ export default Object.freeze({
|
|
|
169
168
|
}
|
|
170
169
|
],
|
|
171
170
|
text: [
|
|
172
|
-
{
|
|
173
|
-
op: "append-text",
|
|
174
|
-
file: "packages/main/src/shared/index.js",
|
|
175
|
-
position: "top",
|
|
176
|
-
skipIfContains: "import \"./resources/workspaceSettingsFields.js\";",
|
|
177
|
-
value: "import \"./resources/workspaceSettingsFields.js\";\n",
|
|
178
|
-
reason: "Load app-owned workspace settings field definitions inside the main shared module.",
|
|
179
|
-
category: "workspaces-core",
|
|
180
|
-
id: "users-core-main-shared-workspace-settings-field-import"
|
|
181
|
-
},
|
|
182
171
|
{
|
|
183
172
|
op: "append-text",
|
|
184
173
|
file: "config/public.js",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/workspaces-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.32",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test"
|
|
@@ -9,20 +9,22 @@
|
|
|
9
9
|
"./server/WorkspacesCoreServiceProvider": "./src/server/WorkspacesCoreServiceProvider.js",
|
|
10
10
|
"./server/validators/routeParamsValidator": "./src/server/common/validators/routeParamsValidator.js",
|
|
11
11
|
"./server/support/workspaceRouteInput": "./src/server/support/workspaceRouteInput.js",
|
|
12
|
+
"./shared/jsonApiTransports": "./src/shared/jsonApiTransports.js",
|
|
12
13
|
"./shared/settings": "./src/shared/settings.js",
|
|
13
14
|
"./shared/tenancyProfile": "./src/shared/tenancyProfile.js",
|
|
14
15
|
"./shared/support/workspacePathModel": "./src/shared/support/workspacePathModel.js",
|
|
15
16
|
"./shared/resources/workspaceResource": "./src/shared/resources/workspaceResource.js",
|
|
16
|
-
"./shared/resources/workspaceSettingsFields": "./src/shared/resources/workspaceSettingsFields.js",
|
|
17
17
|
"./shared/resources/workspaceSettingsResource": "./src/shared/resources/workspaceSettingsResource.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@
|
|
21
|
-
"@jskit-ai/
|
|
22
|
-
"@jskit-ai/
|
|
23
|
-
"@jskit-ai/
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/
|
|
26
|
-
"
|
|
20
|
+
"@jskit-ai/auth-core": "0.1.55",
|
|
21
|
+
"@jskit-ai/database-runtime": "0.1.56",
|
|
22
|
+
"@jskit-ai/http-runtime": "0.1.55",
|
|
23
|
+
"@jskit-ai/json-rest-api-core": "0.1.1",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.56",
|
|
25
|
+
"@jskit-ai/resource-crud-core": "0.1.1",
|
|
26
|
+
"@jskit-ai/resource-core": "0.1.1",
|
|
27
|
+
"@jskit-ai/users-core": "0.1.66",
|
|
28
|
+
"json-rest-schema": "1.x.x"
|
|
27
29
|
}
|
|
28
30
|
}
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INTERNAL_JSON_REST_API,
|
|
3
|
+
addResourceIfMissing,
|
|
4
|
+
createJsonRestResourceScopeOptions
|
|
5
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
6
|
+
import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
|
|
1
7
|
import { bootWorkspaceDirectoryRoutes } from "./workspaceDirectory/bootWorkspaceDirectoryRoutes.js";
|
|
2
8
|
import { registerWorkspaceDirectory } from "./workspaceDirectory/registerWorkspaceDirectory.js";
|
|
3
9
|
import {
|
|
@@ -11,13 +17,17 @@ import { bootWorkspaceSettings } from "./workspaceSettings/bootWorkspaceSettings
|
|
|
11
17
|
import { registerWorkspaceRepositories } from "./registerWorkspaceRepositories.js";
|
|
12
18
|
import { registerWorkspaceCore } from "./registerWorkspaceCore.js";
|
|
13
19
|
import { registerWorkspaceBootstrap } from "./registerWorkspaceBootstrap.js";
|
|
20
|
+
import { workspaceResource } from "../shared/resources/workspaceResource.js";
|
|
21
|
+
import { workspaceMembershipsResource } from "../shared/resources/workspaceMembershipsResource.js";
|
|
22
|
+
import { workspaceInvitesResource } from "../shared/resources/workspaceInvitesResource.js";
|
|
23
|
+
import { workspaceSettingsResource } from "../shared/resources/workspaceSettingsResource.js";
|
|
14
24
|
|
|
15
25
|
class WorkspacesCoreServiceProvider {
|
|
16
26
|
static id = "workspaces.core";
|
|
17
27
|
|
|
18
|
-
static dependsOn = ["users.core"];
|
|
28
|
+
static dependsOn = ["users.core", "json-rest-api.core"];
|
|
19
29
|
|
|
20
|
-
register(app) {
|
|
30
|
+
async register(app) {
|
|
21
31
|
registerWorkspaceRepositories(app);
|
|
22
32
|
registerWorkspaceCore(app);
|
|
23
33
|
registerWorkspaceBootstrap(app);
|
|
@@ -28,6 +38,16 @@ class WorkspacesCoreServiceProvider {
|
|
|
28
38
|
}
|
|
29
39
|
|
|
30
40
|
async boot(app) {
|
|
41
|
+
const api = app.make(INTERNAL_JSON_REST_API);
|
|
42
|
+
const scopeOptions = {
|
|
43
|
+
writeSerializers: {
|
|
44
|
+
"datetime-utc": toDatabaseDateTimeUtc
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
await addResourceIfMissing(api, "workspaces", createJsonRestResourceScopeOptions(workspaceResource, scopeOptions));
|
|
48
|
+
await addResourceIfMissing(api, "workspaceMemberships", createJsonRestResourceScopeOptions(workspaceMembershipsResource, scopeOptions));
|
|
49
|
+
await addResourceIfMissing(api, "workspaceInvites", createJsonRestResourceScopeOptions(workspaceInvitesResource, scopeOptions));
|
|
50
|
+
await addResourceIfMissing(api, "workspaceSettings", createJsonRestResourceScopeOptions(workspaceSettingsResource, scopeOptions));
|
|
31
51
|
if (app.make("workspaces.enabled") !== true) {
|
|
32
52
|
return;
|
|
33
53
|
}
|
|
@@ -1,26 +1,70 @@
|
|
|
1
|
-
import { createCrudResourceRuntime } from "@jskit-ai/crud-core/server/resourceRuntime";
|
|
2
1
|
import {
|
|
2
|
+
createWithTransaction,
|
|
3
3
|
normalizeLowerText,
|
|
4
4
|
normalizeRecordId,
|
|
5
|
+
normalizeDbRecordId,
|
|
5
6
|
normalizeText,
|
|
6
7
|
nowDb,
|
|
7
|
-
isDuplicateEntryError
|
|
8
|
+
isDuplicateEntryError,
|
|
9
|
+
toIsoString
|
|
8
10
|
} from "./repositoryUtils.js";
|
|
9
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
createJsonApiInputRecord,
|
|
13
|
+
createJsonApiRelationship,
|
|
14
|
+
createJsonRestContext,
|
|
15
|
+
simplifyJsonApiDocument
|
|
16
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
10
17
|
|
|
11
|
-
const
|
|
12
|
-
context: "internal.repository.workspace-invites"
|
|
13
|
-
});
|
|
18
|
+
const RESOURCE_TYPE = "workspaceInvites";
|
|
14
19
|
|
|
15
20
|
function normalizeInviteRecord(payload) {
|
|
16
21
|
if (!payload) {
|
|
17
22
|
return null;
|
|
18
23
|
}
|
|
19
|
-
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
id: normalizeDbRecordId(payload.id, { fallback: null }),
|
|
27
|
+
workspaceId: normalizeDbRecordId(payload?.workspace?.id || payload?.workspaceId, { fallback: null }),
|
|
28
|
+
email: normalizeLowerText(payload.email),
|
|
29
|
+
roleSid: normalizeLowerText(payload.roleSid || "member") || "member",
|
|
30
|
+
status: normalizeLowerText(payload.status || "pending") || "pending",
|
|
31
|
+
tokenHash: normalizeText(payload.tokenHash),
|
|
32
|
+
invitedByUserId: normalizeDbRecordId(payload?.invitedByUser?.id || payload?.invitedByUserId, { fallback: null }),
|
|
33
|
+
expiresAt: payload.expiresAt ? toIsoString(payload.expiresAt) : null,
|
|
34
|
+
acceptedAt: payload.acceptedAt ? toIsoString(payload.acceptedAt) : null,
|
|
35
|
+
revokedAt: payload.revokedAt ? toIsoString(payload.revokedAt) : null,
|
|
36
|
+
createdAt: payload.createdAt ? toIsoString(payload.createdAt) : null,
|
|
37
|
+
updatedAt: payload.updatedAt ? toIsoString(payload.updatedAt) : null
|
|
38
|
+
};
|
|
20
39
|
}
|
|
21
40
|
|
|
22
41
|
function normalizeInvitePatchPayload(payload = {}) {
|
|
23
|
-
|
|
42
|
+
const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
43
|
+
const normalized = {};
|
|
44
|
+
|
|
45
|
+
if (Object.hasOwn(source, "roleSid")) {
|
|
46
|
+
normalized.roleSid = normalizeLowerText(source.roleSid);
|
|
47
|
+
}
|
|
48
|
+
if (Object.hasOwn(source, "status")) {
|
|
49
|
+
normalized.status = normalizeLowerText(source.status);
|
|
50
|
+
}
|
|
51
|
+
if (Object.hasOwn(source, "invitedByUserId")) {
|
|
52
|
+
normalized.invitedByUserId =
|
|
53
|
+
source.invitedByUserId == null
|
|
54
|
+
? null
|
|
55
|
+
: normalizeRecordId(source.invitedByUserId, { fallback: null });
|
|
56
|
+
}
|
|
57
|
+
if (Object.hasOwn(source, "expiresAt")) {
|
|
58
|
+
normalized.expiresAt = source.expiresAt == null ? null : new Date(source.expiresAt);
|
|
59
|
+
}
|
|
60
|
+
if (Object.hasOwn(source, "acceptedAt")) {
|
|
61
|
+
normalized.acceptedAt = source.acceptedAt == null ? null : new Date(source.acceptedAt);
|
|
62
|
+
}
|
|
63
|
+
if (Object.hasOwn(source, "revokedAt")) {
|
|
64
|
+
normalized.revokedAt = source.revokedAt == null ? null : new Date(source.revokedAt);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return normalized;
|
|
24
68
|
}
|
|
25
69
|
|
|
26
70
|
function normalizeInviteWithWorkspace(payload = {}) {
|
|
@@ -31,48 +75,82 @@ function normalizeInviteWithWorkspace(payload = {}) {
|
|
|
31
75
|
|
|
32
76
|
return {
|
|
33
77
|
...invite,
|
|
34
|
-
workspaceSlug: payload?.
|
|
35
|
-
workspaceName: payload?.
|
|
36
|
-
workspaceAvatarUrl: payload?.
|
|
78
|
+
workspaceSlug: payload?.workspace?.slug ? normalizeText(payload.workspace.slug) : undefined,
|
|
79
|
+
workspaceName: payload?.workspace?.name ? normalizeText(payload.workspace.name) : undefined,
|
|
80
|
+
workspaceAvatarUrl: payload?.workspace?.avatarUrl ? normalizeText(payload.workspace.avatarUrl) : undefined
|
|
37
81
|
};
|
|
38
82
|
}
|
|
39
83
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"w.slug as workspace_slug",
|
|
43
|
-
"w.name as workspace_name",
|
|
44
|
-
"w.avatar_url as workspace_avatar_url"
|
|
45
|
-
]);
|
|
84
|
+
function createInviteRelationships({ workspaceId = null, invitedByUserId = undefined } = {}) {
|
|
85
|
+
const relationships = {};
|
|
46
86
|
|
|
47
|
-
|
|
87
|
+
if (workspaceId) {
|
|
88
|
+
relationships.workspace = createJsonApiRelationship("workspaces", workspaceId);
|
|
89
|
+
}
|
|
90
|
+
if (invitedByUserId !== undefined) {
|
|
91
|
+
relationships.invitedByUser = createJsonApiRelationship("userProfiles", invitedByUserId);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return relationships;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function createRepository({ api, knex } = {}) {
|
|
98
|
+
if (!api?.resources?.workspaceInvites) {
|
|
99
|
+
throw new TypeError("workspaceInvitesRepository requires json-rest-api workspaceInvites resource.");
|
|
100
|
+
}
|
|
48
101
|
if (typeof knex !== "function") {
|
|
49
102
|
throw new TypeError("workspaceInvitesRepository requires knex.");
|
|
50
103
|
}
|
|
51
|
-
|
|
52
|
-
const withTransaction =
|
|
104
|
+
|
|
105
|
+
const withTransaction = createWithTransaction(knex);
|
|
106
|
+
|
|
107
|
+
async function queryInvites(filters = {}, options = {}, { includeWorkspace = false } = {}) {
|
|
108
|
+
const result = await api.resources.workspaceInvites.query(
|
|
109
|
+
{
|
|
110
|
+
queryParams: {
|
|
111
|
+
filters,
|
|
112
|
+
...(includeWorkspace ? { include: ["workspace"] } : {})
|
|
113
|
+
},
|
|
114
|
+
transaction: options?.trx || null,
|
|
115
|
+
simplified: false
|
|
116
|
+
},
|
|
117
|
+
createJsonRestContext(options?.context || null)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return Array.isArray(simplifyJsonApiDocument(result)) ? simplifyJsonApiDocument(result) : [];
|
|
121
|
+
}
|
|
53
122
|
|
|
54
123
|
async function findPendingByTokenHash(tokenHash, options = {}) {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
124
|
+
const rows = await queryInvites(
|
|
125
|
+
{
|
|
126
|
+
tokenHash: normalizeText(tokenHash),
|
|
127
|
+
status: "pending"
|
|
128
|
+
},
|
|
129
|
+
options
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return normalizeInviteRecord(rows[0] || null);
|
|
60
133
|
}
|
|
61
134
|
|
|
62
135
|
async function listPendingByEmail(email, options = {}) {
|
|
63
|
-
const client = options?.trx || knex;
|
|
64
136
|
const normalizedEmail = normalizeLowerText(email);
|
|
65
137
|
if (!normalizedEmail) {
|
|
66
138
|
return [];
|
|
67
139
|
}
|
|
68
140
|
|
|
69
|
-
const rows = await
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
141
|
+
const rows = await queryInvites(
|
|
142
|
+
{
|
|
143
|
+
email: normalizedEmail,
|
|
144
|
+
status: "pending"
|
|
145
|
+
},
|
|
146
|
+
options,
|
|
147
|
+
{ includeWorkspace: true }
|
|
148
|
+
);
|
|
74
149
|
|
|
75
|
-
return rows
|
|
150
|
+
return rows
|
|
151
|
+
.map(normalizeInviteWithWorkspace)
|
|
152
|
+
.filter(Boolean)
|
|
153
|
+
.sort((left, right) => String(right.createdAt || "").localeCompare(String(left.createdAt || "")));
|
|
76
154
|
}
|
|
77
155
|
|
|
78
156
|
async function listPendingByWorkspaceIdWithWorkspace(workspaceId, options = {}) {
|
|
@@ -81,18 +159,22 @@ function createRepository(knex) {
|
|
|
81
159
|
return [];
|
|
82
160
|
}
|
|
83
161
|
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
162
|
+
const rows = await queryInvites(
|
|
163
|
+
{
|
|
164
|
+
workspace: normalizedWorkspaceId,
|
|
165
|
+
status: "pending"
|
|
166
|
+
},
|
|
167
|
+
options,
|
|
168
|
+
{ includeWorkspace: true }
|
|
169
|
+
);
|
|
90
170
|
|
|
91
|
-
return rows
|
|
171
|
+
return rows
|
|
172
|
+
.map(normalizeInviteWithWorkspace)
|
|
173
|
+
.filter(Boolean)
|
|
174
|
+
.sort((left, right) => String(right.createdAt || "").localeCompare(String(left.createdAt || "")));
|
|
92
175
|
}
|
|
93
176
|
|
|
94
177
|
async function insert(payload = {}, options = {}) {
|
|
95
|
-
const client = options?.trx || knex;
|
|
96
178
|
const source = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
97
179
|
const workspaceId = normalizeRecordId(source.workspaceId, { fallback: null });
|
|
98
180
|
if (!workspaceId) {
|
|
@@ -109,22 +191,52 @@ function createRepository(knex) {
|
|
|
109
191
|
};
|
|
110
192
|
|
|
111
193
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
194
|
+
const created = await api.resources.workspaceInvites.post(
|
|
195
|
+
{
|
|
196
|
+
inputRecord: createJsonApiInputRecord(
|
|
197
|
+
RESOURCE_TYPE,
|
|
198
|
+
{
|
|
199
|
+
email: createPayload.email,
|
|
200
|
+
roleSid: createPayload.roleSid,
|
|
201
|
+
status: createPayload.status,
|
|
202
|
+
tokenHash: createPayload.tokenHash,
|
|
203
|
+
expiresAt: createPayload.expiresAt ?? null,
|
|
204
|
+
acceptedAt: null,
|
|
205
|
+
revokedAt: null,
|
|
206
|
+
createdAt: new Date(),
|
|
207
|
+
updatedAt: new Date()
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
relationships: createInviteRelationships({
|
|
211
|
+
workspaceId: createPayload.workspaceId,
|
|
212
|
+
invitedByUserId: createPayload.invitedByUserId ?? null
|
|
213
|
+
})
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
transaction: options?.trx || null,
|
|
217
|
+
simplified: false
|
|
218
|
+
},
|
|
219
|
+
createJsonRestContext(options?.context || null)
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return normalizeInviteRecord(simplifyJsonApiDocument(created));
|
|
117
223
|
} catch (error) {
|
|
118
224
|
if (!isDuplicateEntryError(error)) {
|
|
119
225
|
throw error;
|
|
120
226
|
}
|
|
121
227
|
}
|
|
122
228
|
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
229
|
+
const rows = await queryInvites(
|
|
230
|
+
{
|
|
231
|
+
workspace: createPayload.workspaceId,
|
|
232
|
+
email: createPayload.email,
|
|
233
|
+
status: "pending"
|
|
234
|
+
},
|
|
235
|
+
options
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
rows.sort((left, right) => String(right.id || "").localeCompare(String(left.id || "")));
|
|
239
|
+
return normalizeInviteRecord(rows[0] || null);
|
|
128
240
|
}
|
|
129
241
|
|
|
130
242
|
async function expirePendingByWorkspaceIdAndEmail(workspaceId, email, options = {}) {
|
|
@@ -133,14 +245,38 @@ function createRepository(knex) {
|
|
|
133
245
|
return;
|
|
134
246
|
}
|
|
135
247
|
|
|
136
|
-
const client = options?.trx || knex;
|
|
137
248
|
const patch = normalizeInvitePatchPayload({ status: "expired" });
|
|
138
|
-
await
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
249
|
+
const rows = await queryInvites(
|
|
250
|
+
{
|
|
251
|
+
workspace: normalizedWorkspaceId,
|
|
252
|
+
email: normalizeLowerText(email),
|
|
253
|
+
status: "pending"
|
|
254
|
+
},
|
|
255
|
+
options
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
for (const row of rows) {
|
|
259
|
+
if (!row?.id) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
await api.resources.workspaceInvites.patch(
|
|
263
|
+
{
|
|
264
|
+
inputRecord: createJsonApiInputRecord(
|
|
265
|
+
RESOURCE_TYPE,
|
|
266
|
+
{
|
|
267
|
+
status: patch.status,
|
|
268
|
+
updatedAt: nowDb()
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: row.id
|
|
272
|
+
}
|
|
273
|
+
),
|
|
274
|
+
transaction: options?.trx || null,
|
|
275
|
+
simplified: false
|
|
276
|
+
},
|
|
277
|
+
createJsonRestContext(options?.context || null)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
144
280
|
}
|
|
145
281
|
|
|
146
282
|
async function markAcceptedById(inviteId, options = {}) {
|
|
@@ -149,16 +285,23 @@ function createRepository(knex) {
|
|
|
149
285
|
return;
|
|
150
286
|
}
|
|
151
287
|
|
|
152
|
-
await
|
|
153
|
-
normalizedInviteId,
|
|
288
|
+
await api.resources.workspaceInvites.patch(
|
|
154
289
|
{
|
|
155
|
-
|
|
156
|
-
|
|
290
|
+
inputRecord: createJsonApiInputRecord(
|
|
291
|
+
RESOURCE_TYPE,
|
|
292
|
+
{
|
|
293
|
+
status: "accepted",
|
|
294
|
+
acceptedAt: new Date(),
|
|
295
|
+
updatedAt: new Date()
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
id: normalizedInviteId
|
|
299
|
+
}
|
|
300
|
+
),
|
|
301
|
+
transaction: options?.trx || null,
|
|
302
|
+
simplified: false
|
|
157
303
|
},
|
|
158
|
-
|
|
159
|
-
...options,
|
|
160
|
-
include: "none"
|
|
161
|
-
}
|
|
304
|
+
createJsonRestContext(options?.context || null)
|
|
162
305
|
);
|
|
163
306
|
}
|
|
164
307
|
|
|
@@ -168,16 +311,23 @@ function createRepository(knex) {
|
|
|
168
311
|
return;
|
|
169
312
|
}
|
|
170
313
|
|
|
171
|
-
await
|
|
172
|
-
normalizedInviteId,
|
|
314
|
+
await api.resources.workspaceInvites.patch(
|
|
173
315
|
{
|
|
174
|
-
|
|
175
|
-
|
|
316
|
+
inputRecord: createJsonApiInputRecord(
|
|
317
|
+
RESOURCE_TYPE,
|
|
318
|
+
{
|
|
319
|
+
status: "revoked",
|
|
320
|
+
revokedAt: new Date(),
|
|
321
|
+
updatedAt: new Date()
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: normalizedInviteId
|
|
325
|
+
}
|
|
326
|
+
),
|
|
327
|
+
transaction: options?.trx || null,
|
|
328
|
+
simplified: false
|
|
176
329
|
},
|
|
177
|
-
|
|
178
|
-
...options,
|
|
179
|
-
include: "none"
|
|
180
|
-
}
|
|
330
|
+
createJsonRestContext(options?.context || null)
|
|
181
331
|
);
|
|
182
332
|
}
|
|
183
333
|
|
|
@@ -188,11 +338,16 @@ function createRepository(knex) {
|
|
|
188
338
|
return null;
|
|
189
339
|
}
|
|
190
340
|
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
341
|
+
const rows = await queryInvites(
|
|
342
|
+
{
|
|
343
|
+
id: normalizedInviteId,
|
|
344
|
+
workspace: normalizedWorkspaceId,
|
|
345
|
+
status: "pending"
|
|
346
|
+
},
|
|
347
|
+
options
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
return normalizeInviteRecord(rows[0] || null);
|
|
196
351
|
}
|
|
197
352
|
|
|
198
353
|
return Object.freeze({
|