@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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
+
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
2
3
|
import { buildInviteToken, hashInviteToken } from "@jskit-ai/auth-core/server/inviteTokens";
|
|
3
4
|
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
4
5
|
import { OWNER_ROLE_ID, createWorkspaceRoleCatalog, cloneWorkspaceRoleCatalog } from "../../shared/roles.js";
|
|
@@ -41,6 +42,41 @@ function createService({
|
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
function mapWorkspaceSummary(workspace = {}) {
|
|
46
|
+
return {
|
|
47
|
+
id: normalizeRecordId(workspace.id, { fallback: "" }),
|
|
48
|
+
slug: normalizeText(workspace.slug),
|
|
49
|
+
name: normalizeText(workspace.name),
|
|
50
|
+
ownerUserId: normalizeRecordId(workspace.ownerUserId, { fallback: "" }),
|
|
51
|
+
avatarUrl: normalizeText(workspace.avatarUrl)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mapMemberSummary(member = {}, workspace = {}) {
|
|
56
|
+
const userId = normalizeRecordId(member.userId, { fallback: "" });
|
|
57
|
+
const roleSid = normalizeLowerText(member.roleSid || "member") || "member";
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
userId,
|
|
61
|
+
roleSid,
|
|
62
|
+
status: normalizeLowerText(member.status || "active") || "active",
|
|
63
|
+
displayName: normalizeText(member.displayName),
|
|
64
|
+
email: normalizeLowerText(member.email),
|
|
65
|
+
isOwner: userId === normalizeRecordId(workspace.ownerUserId, { fallback: "" }) || roleSid === OWNER_ROLE_ID
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function mapInviteSummary(invite = {}) {
|
|
70
|
+
return {
|
|
71
|
+
id: normalizeRecordId(invite.id, { fallback: "" }),
|
|
72
|
+
email: normalizeLowerText(invite.email),
|
|
73
|
+
roleSid: normalizeLowerText(invite.roleSid || "member") || "member",
|
|
74
|
+
status: normalizeLowerText(invite.status || "pending") || "pending",
|
|
75
|
+
expiresAt: invite.expiresAt || null,
|
|
76
|
+
invitedByUserId: invite.invitedByUserId == null ? null : normalizeRecordId(invite.invitedByUserId, { fallback: null })
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
44
80
|
async function listRoles(options = {}) {
|
|
45
81
|
return cloneWorkspaceRoleCatalog({
|
|
46
82
|
...resolvedRoleCatalog,
|
|
@@ -52,8 +88,8 @@ function createService({
|
|
|
52
88
|
const members = await workspaceMembershipsRepository.listActiveByWorkspaceId(workspace.id, options);
|
|
53
89
|
|
|
54
90
|
return withRoleCatalog({
|
|
55
|
-
workspace,
|
|
56
|
-
members
|
|
91
|
+
workspace: mapWorkspaceSummary(workspace),
|
|
92
|
+
members: members.map((member) => mapMemberSummary(member, workspace))
|
|
57
93
|
});
|
|
58
94
|
}
|
|
59
95
|
|
|
@@ -63,7 +99,7 @@ function createService({
|
|
|
63
99
|
|
|
64
100
|
async function updateMemberRole(workspace, payload = {}, options = {}) {
|
|
65
101
|
const memberUserId = normalizeRecordId(payload.memberUserId, { fallback: null });
|
|
66
|
-
const roleSid = payload.roleSid;
|
|
102
|
+
const roleSid = normalizeLowerText(payload.roleSid || "");
|
|
67
103
|
if (!memberUserId) {
|
|
68
104
|
throw new AppError(400, "Validation failed.");
|
|
69
105
|
}
|
|
@@ -130,8 +166,8 @@ function createService({
|
|
|
130
166
|
const invites = await workspaceInvitesRepository.listPendingByWorkspaceIdWithWorkspace(workspace.id, options);
|
|
131
167
|
|
|
132
168
|
return withRoleCatalog({
|
|
133
|
-
workspace,
|
|
134
|
-
invites
|
|
169
|
+
workspace: mapWorkspaceSummary(workspace),
|
|
170
|
+
invites: invites.map((invite) => mapInviteSummary(invite))
|
|
135
171
|
});
|
|
136
172
|
}
|
|
137
173
|
|
|
@@ -140,8 +176,8 @@ function createService({
|
|
|
140
176
|
}
|
|
141
177
|
|
|
142
178
|
async function createInvite(workspace, user, payload = {}, options = {}) {
|
|
143
|
-
const email = payload.email;
|
|
144
|
-
const roleSid = payload.roleSid;
|
|
179
|
+
const email = normalizeLowerText(payload.email);
|
|
180
|
+
const roleSid = normalizeLowerText(payload.roleSid || "member") || "member";
|
|
145
181
|
if (!assignableRoleIds.includes(roleSid)) {
|
|
146
182
|
throw new AppError(400, "Validation failed.", {
|
|
147
183
|
details: {
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createJsonApiResourceRouteContract } from "@jskit-ai/http-runtime/shared/validators/jsonApiRouteTransport";
|
|
2
2
|
import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
|
|
3
3
|
import { workspacePendingInvitationsResource } from "../../shared/resources/workspacePendingInvitationsResource.js";
|
|
4
|
+
import {
|
|
5
|
+
WORKSPACE_INVITE_REDEEM_TRANSPORT,
|
|
6
|
+
WORKSPACE_PENDING_INVITATIONS_TRANSPORT
|
|
7
|
+
} from "../../shared/jsonApiTransports.js";
|
|
8
|
+
|
|
9
|
+
function resolveAuthenticatedUserRecordId(_record, context = {}) {
|
|
10
|
+
const userId = context?.request?.user?.id;
|
|
11
|
+
if (userId != null && String(userId).trim()) {
|
|
12
|
+
return userId;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
throw new Error("JSON:API response requires request.user.id.");
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
function bootWorkspacePendingInvitations(app) {
|
|
6
19
|
if (!app || typeof app.make !== "function") {
|
|
@@ -18,8 +31,11 @@ function bootWorkspacePendingInvitations(app) {
|
|
|
18
31
|
tags: ["workspace"],
|
|
19
32
|
summary: "List pending workspace invitations for authenticated user"
|
|
20
33
|
},
|
|
21
|
-
|
|
22
|
-
|
|
34
|
+
...createJsonApiResourceRouteContract({
|
|
35
|
+
...WORKSPACE_PENDING_INVITATIONS_TRANSPORT,
|
|
36
|
+
output: workspacePendingInvitationsResource.operations.list.output,
|
|
37
|
+
outputKind: "record",
|
|
38
|
+
getRecordId: resolveAuthenticatedUserRecordId
|
|
23
39
|
})
|
|
24
40
|
},
|
|
25
41
|
async function (request, reply) {
|
|
@@ -39,20 +55,19 @@ function bootWorkspacePendingInvitations(app) {
|
|
|
39
55
|
tags: ["workspace"],
|
|
40
56
|
summary: "Accept or refuse a workspace invitation using an invite token"
|
|
41
57
|
},
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
...createJsonApiResourceRouteContract({
|
|
59
|
+
...WORKSPACE_INVITE_REDEEM_TRANSPORT,
|
|
60
|
+
body: workspaceMembersResource.operations.redeemInvite.body,
|
|
61
|
+
output: workspaceMembersResource.operations.redeemInvite.output,
|
|
62
|
+
outputKind: "record",
|
|
63
|
+
getRecordId: resolveAuthenticatedUserRecordId,
|
|
64
|
+
includeValidation400: true
|
|
65
|
+
})
|
|
49
66
|
},
|
|
50
67
|
async function (request, reply) {
|
|
51
68
|
const response = await request.executeAction({
|
|
52
69
|
actionId: "workspace.invite.redeem",
|
|
53
|
-
input:
|
|
54
|
-
payload: request.input.body
|
|
55
|
-
}
|
|
70
|
+
input: request.input.body
|
|
56
71
|
});
|
|
57
72
|
reply.code(200).send(response);
|
|
58
73
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
emptyInputValidator
|
|
3
3
|
} from "@jskit-ai/kernel/shared/actions/actionContributorHelpers";
|
|
4
|
+
import { returnJsonApiData } from "@jskit-ai/http-runtime/shared";
|
|
4
5
|
import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
|
|
5
|
-
import { workspacePendingInvitationsResource } from "../../shared/resources/workspacePendingInvitationsResource.js";
|
|
6
6
|
import { resolveActionUser } from "../common/support/resolveActionUser.js";
|
|
7
7
|
|
|
8
8
|
const workspacePendingInvitationsActions = Object.freeze([
|
|
@@ -15,19 +15,19 @@ const workspacePendingInvitationsActions = Object.freeze([
|
|
|
15
15
|
permission: {
|
|
16
16
|
require: "authenticated"
|
|
17
17
|
},
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
input: emptyInputValidator,
|
|
19
|
+
output: null,
|
|
20
20
|
idempotency: "none",
|
|
21
21
|
audit: {
|
|
22
22
|
actionName: "workspace.invitations.pending.list"
|
|
23
23
|
},
|
|
24
24
|
observability: {},
|
|
25
25
|
async execute(input, context, deps) {
|
|
26
|
-
return {
|
|
26
|
+
return returnJsonApiData({
|
|
27
27
|
pendingInvites: await deps.workspacePendingInvitationsService.listPendingInvitesForUser(resolveActionUser(context, input), {
|
|
28
28
|
context
|
|
29
29
|
})
|
|
30
|
-
};
|
|
30
|
+
});
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
{
|
|
@@ -39,34 +39,32 @@ const workspacePendingInvitationsActions = Object.freeze([
|
|
|
39
39
|
permission: {
|
|
40
40
|
require: "authenticated"
|
|
41
41
|
},
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
},
|
|
45
|
-
outputValidator: workspaceMembersResource.operations.redeemInvite.outputValidator,
|
|
42
|
+
input: workspaceMembersResource.operations.redeemInvite.body,
|
|
43
|
+
output: null,
|
|
46
44
|
idempotency: "optional",
|
|
47
45
|
audit: {
|
|
48
46
|
actionName: "workspace.invite.redeem"
|
|
49
47
|
},
|
|
50
48
|
observability: {},
|
|
51
49
|
async execute(input, context, deps) {
|
|
52
|
-
const payload = input
|
|
50
|
+
const payload = input || {};
|
|
53
51
|
const user = resolveActionUser(context, input);
|
|
54
52
|
|
|
55
53
|
if (payload.decision === "accept") {
|
|
56
|
-
return deps.workspacePendingInvitationsService.acceptInviteByToken({
|
|
54
|
+
return returnJsonApiData(await deps.workspacePendingInvitationsService.acceptInviteByToken({
|
|
57
55
|
user,
|
|
58
56
|
token: payload.token
|
|
59
57
|
}, {
|
|
60
58
|
context
|
|
61
|
-
});
|
|
59
|
+
}));
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
return deps.workspacePendingInvitationsService.refuseInviteByToken({
|
|
62
|
+
return returnJsonApiData(await deps.workspacePendingInvitationsService.refuseInviteByToken({
|
|
65
63
|
user,
|
|
66
64
|
token: payload.token
|
|
67
65
|
}, {
|
|
68
66
|
context
|
|
69
|
-
});
|
|
67
|
+
}));
|
|
70
68
|
}
|
|
71
69
|
}
|
|
72
70
|
]);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { resolveInviteTokenHash } from "@jskit-ai/auth-core/server/inviteTokens";
|
|
2
|
+
import { encodeInviteTokenHash } from "@jskit-ai/auth-core/shared/inviteTokens";
|
|
2
3
|
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
3
4
|
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
4
5
|
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
5
|
-
import { authenticatedUserValidator } from "../common/validators/authenticatedUserValidator.js";
|
|
6
6
|
|
|
7
7
|
function createService({
|
|
8
8
|
workspaceInvitesRepository,
|
|
@@ -13,12 +13,11 @@ function createService({
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function requireAuthenticatedInviteUser(user) {
|
|
16
|
-
|
|
17
|
-
if (!normalizedUser) {
|
|
16
|
+
if (!normalizeRecordId(user?.id, { fallback: null })) {
|
|
18
17
|
throw new AppError(401, "Authentication required.");
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
return
|
|
20
|
+
return user;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
function requireInviteTokenHash(token) {
|
|
@@ -35,8 +34,30 @@ function createService({
|
|
|
35
34
|
return tokenHash;
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
function mapPendingInvite(invite = {}) {
|
|
38
|
+
const id = normalizeRecordId(invite.id, { fallback: null });
|
|
39
|
+
const workspaceId = normalizeRecordId(invite.workspaceId, { fallback: null });
|
|
40
|
+
const tokenHash = normalizeText(invite.tokenHash);
|
|
41
|
+
|
|
42
|
+
if (!id || !workspaceId || !tokenHash) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
id,
|
|
48
|
+
workspaceId,
|
|
49
|
+
workspaceSlug: normalizeText(invite.workspaceSlug),
|
|
50
|
+
workspaceName: normalizeText(invite.workspaceName || invite.workspaceSlug),
|
|
51
|
+
workspaceAvatarUrl: normalizeText(invite.workspaceAvatarUrl),
|
|
52
|
+
roleSid: normalizeLowerText(invite.roleSid || "member") || "member",
|
|
53
|
+
status: normalizeLowerText(invite.status || "pending") || "pending",
|
|
54
|
+
expiresAt: invite.expiresAt || null,
|
|
55
|
+
token: encodeInviteTokenHash(tokenHash)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
38
59
|
async function requirePendingInviteForUserByToken(user, token, options = {}) {
|
|
39
|
-
const
|
|
60
|
+
const actor = requireAuthenticatedInviteUser(user);
|
|
40
61
|
const tokenHash = requireInviteTokenHash(token);
|
|
41
62
|
|
|
42
63
|
const invite = await workspaceInvitesRepository.findPendingByTokenHash(tokenHash, options);
|
|
@@ -44,12 +65,12 @@ function createService({
|
|
|
44
65
|
throw new AppError(404, "Invitation not found or already handled.");
|
|
45
66
|
}
|
|
46
67
|
|
|
47
|
-
if (normalizeLowerText(invite.email) !==
|
|
68
|
+
if (normalizeLowerText(invite.email) !== normalizeLowerText(actor?.email)) {
|
|
48
69
|
throw new AppError(403, "Invitation email does not match authenticated user.");
|
|
49
70
|
}
|
|
50
71
|
|
|
51
72
|
return {
|
|
52
|
-
user:
|
|
73
|
+
user: actor,
|
|
53
74
|
invite
|
|
54
75
|
};
|
|
55
76
|
}
|
|
@@ -62,12 +83,14 @@ function createService({
|
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
async function listPendingInvitesForUser(user, options = {}) {
|
|
65
|
-
const
|
|
66
|
-
|
|
86
|
+
const actor = requireAuthenticatedInviteUser(user);
|
|
87
|
+
const actorEmail = normalizeLowerText(actor?.email);
|
|
88
|
+
if (!actorEmail) {
|
|
67
89
|
return [];
|
|
68
90
|
}
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
const invites = await workspaceInvitesRepository.listPendingByEmail(actorEmail, options);
|
|
93
|
+
return invites.map((invite) => mapPendingInvite(invite)).filter(Boolean);
|
|
71
94
|
}
|
|
72
95
|
|
|
73
96
|
function requireWorkspaceIdFromInvite(invite, methodName = "workspacePendingInvitationsService") {
|
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createJsonApiResourceRouteContract } from "@jskit-ai/http-runtime/shared/validators/jsonApiRouteTransport";
|
|
2
|
+
import { WORKSPACE_SETTINGS_TRANSPORT } from "../../shared/jsonApiTransports.js";
|
|
2
3
|
import { workspaceSettingsResource } from "../../shared/resources/workspaceSettingsResource.js";
|
|
3
4
|
import { resolveWorkspaceRoutePath } from "../common/support/workspaceRoutePaths.js";
|
|
4
5
|
import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
|
|
5
6
|
import { resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig } from "../support/workspaceActionSurfaces.js";
|
|
6
7
|
|
|
8
|
+
function resolveWorkspaceSettingsRecordId(record = {}, context = {}) {
|
|
9
|
+
const workspaceId = record?.workspace?.id;
|
|
10
|
+
if (workspaceId != null && String(workspaceId).trim()) {
|
|
11
|
+
return workspaceId;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const workspaceSlug = context?.request?.params?.workspaceSlug;
|
|
15
|
+
if (workspaceSlug != null && String(workspaceSlug).trim()) {
|
|
16
|
+
return workspaceSlug;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
throw new Error("Workspace settings JSON:API response requires workspace id.");
|
|
20
|
+
}
|
|
21
|
+
|
|
7
22
|
function bootWorkspaceSettings(app) {
|
|
8
23
|
if (!app || typeof app.make !== "function") {
|
|
9
24
|
throw new Error("bootWorkspaceSettings requires application make().");
|
|
@@ -24,9 +39,12 @@ function bootWorkspaceSettings(app) {
|
|
|
24
39
|
tags: ["workspace"],
|
|
25
40
|
summary: "Get workspace settings and role catalog by workspace slug"
|
|
26
41
|
},
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
params: workspaceSlugParamsValidator,
|
|
43
|
+
...createJsonApiResourceRouteContract({
|
|
44
|
+
...WORKSPACE_SETTINGS_TRANSPORT,
|
|
45
|
+
output: workspaceSettingsResource.operations.view.output,
|
|
46
|
+
outputKind: "record",
|
|
47
|
+
getRecordId: resolveWorkspaceSettingsRecordId
|
|
30
48
|
})
|
|
31
49
|
},
|
|
32
50
|
async function (request, reply) {
|
|
@@ -51,21 +69,22 @@ function bootWorkspaceSettings(app) {
|
|
|
51
69
|
tags: ["workspace"],
|
|
52
70
|
summary: "Update workspace settings by workspace slug"
|
|
53
71
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
72
|
+
params: workspaceSlugParamsValidator,
|
|
73
|
+
...createJsonApiResourceRouteContract({
|
|
74
|
+
...WORKSPACE_SETTINGS_TRANSPORT,
|
|
75
|
+
body: workspaceSettingsResource.operations.patch.body,
|
|
76
|
+
output: workspaceSettingsResource.operations.patch.output,
|
|
77
|
+
outputKind: "record",
|
|
78
|
+
getRecordId: resolveWorkspaceSettingsRecordId,
|
|
79
|
+
includeValidation400: true
|
|
80
|
+
})
|
|
62
81
|
},
|
|
63
82
|
async function (request, reply) {
|
|
64
83
|
const response = await request.executeAction({
|
|
65
84
|
actionId: "workspace.settings.update",
|
|
66
85
|
input: {
|
|
67
86
|
workspaceSlug: request.input.params.workspaceSlug,
|
|
68
|
-
|
|
87
|
+
...(request.input.body || {})
|
|
69
88
|
}
|
|
70
89
|
});
|
|
71
90
|
reply.code(200).send(response);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
2
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
3
|
+
import { INTERNAL_JSON_REST_API } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
3
4
|
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
4
5
|
import { createRepository as createWorkspaceSettingsRepository } from "./workspaceSettingsRepository.js";
|
|
5
6
|
import { createService as createWorkspaceSettingsService } from "./workspaceSettingsService.js";
|
|
@@ -23,9 +24,12 @@ function registerWorkspaceSettings(app) {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
app.singleton("internal.repository.workspace-settings", (scope) => {
|
|
27
|
+
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
26
28
|
const knex = scope.make("jskit.database.knex");
|
|
27
29
|
const appConfig = resolveAppConfig(scope);
|
|
28
|
-
return createWorkspaceSettingsRepository(
|
|
30
|
+
return createWorkspaceSettingsRepository({
|
|
31
|
+
api,
|
|
32
|
+
knex,
|
|
29
33
|
defaultInvitesEnabled: resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig)
|
|
30
34
|
});
|
|
31
35
|
});
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import { composeSchemaDefinitions } from "@jskit-ai/kernel/shared/validators";
|
|
2
|
+
import { returnJsonApiData } from "@jskit-ai/http-runtime/shared";
|
|
1
3
|
import { workspaceSettingsResource } from "../../shared/resources/workspaceSettingsResource.js";
|
|
2
4
|
import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
|
|
3
5
|
import { resolveWorkspace } from "../support/resolveWorkspace.js";
|
|
4
6
|
|
|
7
|
+
const workspaceSettingsUpdateInputValidator = composeSchemaDefinitions([
|
|
8
|
+
workspaceSlugParamsValidator,
|
|
9
|
+
workspaceSettingsResource.operations.patch.body
|
|
10
|
+
], {
|
|
11
|
+
mode: "patch",
|
|
12
|
+
context: "workspaceSettingsActions.workspaceSettingsUpdateInputValidator"
|
|
13
|
+
});
|
|
14
|
+
|
|
5
15
|
const workspaceSettingsActions = Object.freeze([
|
|
6
16
|
{
|
|
7
17
|
id: "workspace.settings.read",
|
|
@@ -13,8 +23,8 @@ const workspaceSettingsActions = Object.freeze([
|
|
|
13
23
|
require: "any",
|
|
14
24
|
permissions: ["workspace.settings.view", "workspace.settings.update"]
|
|
15
25
|
},
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
input: workspaceSlugParamsValidator,
|
|
27
|
+
output: null,
|
|
18
28
|
idempotency: "none",
|
|
19
29
|
audit: {
|
|
20
30
|
actionName: "workspace.settings.read"
|
|
@@ -25,7 +35,7 @@ const workspaceSettingsActions = Object.freeze([
|
|
|
25
35
|
context
|
|
26
36
|
});
|
|
27
37
|
|
|
28
|
-
return response;
|
|
38
|
+
return returnJsonApiData(response);
|
|
29
39
|
}
|
|
30
40
|
},
|
|
31
41
|
{
|
|
@@ -38,13 +48,8 @@ const workspaceSettingsActions = Object.freeze([
|
|
|
38
48
|
require: "all",
|
|
39
49
|
permissions: ["workspace.settings.update"]
|
|
40
50
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{
|
|
44
|
-
patch: workspaceSettingsResource.operations.patch.bodyValidator
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
outputValidator: workspaceSettingsResource.operations.patch.outputValidator,
|
|
51
|
+
input: workspaceSettingsUpdateInputValidator,
|
|
52
|
+
output: null,
|
|
48
53
|
idempotency: "optional",
|
|
49
54
|
audit: {
|
|
50
55
|
actionName: "workspace.settings.update"
|
|
@@ -56,15 +61,16 @@ const workspaceSettingsActions = Object.freeze([
|
|
|
56
61
|
}
|
|
57
62
|
},
|
|
58
63
|
async execute(input, context, deps) {
|
|
64
|
+
const { workspaceSlug, ...patch } = input;
|
|
59
65
|
const response = await deps.workspaceSettingsService.updateWorkspaceSettings(
|
|
60
66
|
resolveWorkspace(context, input),
|
|
61
|
-
|
|
67
|
+
patch,
|
|
62
68
|
{
|
|
63
69
|
context
|
|
64
70
|
}
|
|
65
71
|
);
|
|
66
72
|
|
|
67
|
-
return response;
|
|
73
|
+
return returnJsonApiData(response);
|
|
68
74
|
}
|
|
69
75
|
}
|
|
70
76
|
]);
|