@jskit-ai/workspaces-core 0.1.30 → 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.
Files changed (61) hide show
  1. package/package.descriptor.mjs +11 -22
  2. package/package.json +11 -9
  3. package/src/server/WorkspacesCoreServiceProvider.js +22 -2
  4. package/src/server/common/repositories/workspaceInvitesRepository.js +233 -78
  5. package/src/server/common/repositories/workspaceMembershipsRepository.js +177 -86
  6. package/src/server/common/repositories/workspacesRepository.js +179 -86
  7. package/src/server/common/services/workspaceContextService.js +26 -24
  8. package/src/server/common/validators/routeParamsValidator.js +36 -53
  9. package/src/server/registerWorkspaceCore.js +6 -7
  10. package/src/server/registerWorkspaceRepositories.js +7 -3
  11. package/src/server/support/workspaceServerScopeSupport.js +1 -1
  12. package/src/server/workspaceBootstrapContributor.js +5 -14
  13. package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +54 -27
  14. package/src/server/workspaceDirectory/workspaceDirectoryActions.js +30 -24
  15. package/src/server/workspaceMembers/bootWorkspaceMembers.js +70 -32
  16. package/src/server/workspaceMembers/workspaceMembersActions.js +61 -27
  17. package/src/server/workspaceMembers/workspaceMembersService.js +43 -7
  18. package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +28 -13
  19. package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +13 -15
  20. package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +33 -10
  21. package/src/server/workspaceSettings/bootWorkspaceSettings.js +32 -13
  22. package/src/server/workspaceSettings/registerWorkspaceSettings.js +5 -1
  23. package/src/server/workspaceSettings/workspaceSettingsActions.js +18 -12
  24. package/src/server/workspaceSettings/workspaceSettingsRepository.js +104 -91
  25. package/src/server/workspaceSettings/workspaceSettingsService.js +5 -6
  26. package/src/shared/jsonApiTransports.js +79 -0
  27. package/src/shared/resources/workspaceInvitesResource.js +158 -0
  28. package/src/shared/resources/workspaceMembersResource.js +176 -311
  29. package/src/shared/resources/workspaceMembershipsResource.js +96 -0
  30. package/src/shared/resources/workspacePendingInvitationsResource.js +25 -72
  31. package/src/shared/resources/workspaceResource.js +113 -144
  32. package/src/shared/resources/workspaceRoleCatalogSchema.js +31 -0
  33. package/src/shared/resources/workspaceSettingsResource.js +276 -148
  34. package/test/repositoryContracts.test.js +16 -4
  35. package/test/resourcesCanonical.test.js +39 -16
  36. package/test/routeParamsValidator.test.js +37 -19
  37. package/test/usersRouteResources.test.js +27 -17
  38. package/test/workspaceActionContextContributor.test.js +1 -1
  39. package/test/workspaceInternalCrudResources.test.js +98 -0
  40. package/test/workspaceInvitesRepository.test.js +196 -148
  41. package/test/workspaceMembersResource.test.js +35 -0
  42. package/test/workspaceMembershipsRepository.test.js +155 -115
  43. package/test/workspacePendingInvitationsResource.test.js +18 -23
  44. package/test/workspacePendingInvitationsService.test.js +2 -1
  45. package/test/workspaceServerScopeSupport.test.js +21 -3
  46. package/test/workspaceSettingsActions.test.js +5 -7
  47. package/test/workspaceSettingsInternalResource.test.js +8 -0
  48. package/test/workspaceSettingsRepository.test.js +158 -123
  49. package/test/workspaceSettingsResource.test.js +51 -62
  50. package/test/workspaceSettingsService.test.js +0 -1
  51. package/test/workspacesRepository.test.js +318 -174
  52. package/test/workspacesRouteRequestInputValidator.test.js +25 -11
  53. package/src/server/common/resources/workspaceInvitesResource.js +0 -207
  54. package/src/server/common/resources/workspaceMembershipsResource.js +0 -154
  55. package/src/server/common/resources/workspacesResource.js +0 -170
  56. package/src/server/common/validators/authenticatedUserValidator.js +0 -43
  57. package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
  58. package/src/shared/resources/workspaceSettingsFields.js +0 -65
  59. package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
  60. package/test/settingsFieldRegistriesSingleton.test.js +0 -14
  61. 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 { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
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
- responseValidators: withStandardErrorResponses({
22
- 200: workspacePendingInvitationsResource.operations.list.outputValidator
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
- bodyValidator: workspaceMembersResource.operations.redeemInvite.bodyValidator,
43
- responseValidators: withStandardErrorResponses(
44
- {
45
- 200: workspaceMembersResource.operations.redeemInvite.outputValidator
46
- },
47
- { includeValidation400: true }
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
- EMPTY_INPUT_VALIDATOR
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
- inputValidator: EMPTY_INPUT_VALIDATOR,
19
- outputValidator: workspacePendingInvitationsResource.operations.list.outputValidator,
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
- inputValidator: {
43
- payload: workspaceMembersResource.operations.redeemInvite.bodyValidator
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.payload || {};
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
- const normalizedUser = authenticatedUserValidator.normalize(user);
17
- if (!normalizedUser) {
16
+ if (!normalizeRecordId(user?.id, { fallback: null })) {
18
17
  throw new AppError(401, "Authentication required.");
19
18
  }
20
19
 
21
- return normalizedUser;
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 normalizedUser = requireAuthenticatedInviteUser(user);
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) !== normalizedUser.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: normalizedUser,
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 normalizedUser = requireAuthenticatedInviteUser(user);
66
- if (!normalizedUser.email) {
86
+ const actor = requireAuthenticatedInviteUser(user);
87
+ const actorEmail = normalizeLowerText(actor?.email);
88
+ if (!actorEmail) {
67
89
  return [];
68
90
  }
69
91
 
70
- return workspaceInvitesRepository.listPendingByEmail(normalizedUser.email, options);
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 { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
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
- paramsValidator: workspaceSlugParamsValidator,
28
- responseValidators: withStandardErrorResponses({
29
- 200: workspaceSettingsResource.operations.view.outputValidator
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
- paramsValidator: workspaceSlugParamsValidator,
55
- bodyValidator: workspaceSettingsResource.operations.patch.bodyValidator,
56
- responseValidators: withStandardErrorResponses(
57
- {
58
- 200: workspaceSettingsResource.operations.patch.outputValidator
59
- },
60
- { includeValidation400: true }
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
- patch: request.input.body
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(knex, {
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
- inputValidator: workspaceSlugParamsValidator,
17
- outputValidator: workspaceSettingsResource.operations.view.outputValidator,
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
- inputValidator: [
42
- workspaceSlugParamsValidator,
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
- input.patch,
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
  ]);