@jskit-ai/workspaces-core 0.1.138-oldline.1 → 0.1.138
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.json +154 -10
- package/patterns/workspace-server/PATTERN.md +60 -0
- package/src/server/WorkspacesFeature.js +226 -0
- package/src/server/WorkspacesIntegrationsProvider.js +74 -0
- package/src/server/common/contributors/workspaceActionContextContributor.js +5 -9
- package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +16 -2
- package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +22 -4
- package/src/server/common/support/realtimeServiceEvents.js +102 -61
- package/src/server/support/workspaceActionSurfaces.js +1 -13
- package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +5 -11
- package/src/server/workspaceDirectory/workspaceDirectoryActions.js +24 -14
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +5 -12
- package/src/server/workspaceMembers/workspaceMembersActions.js +63 -23
- package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +4 -6
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +31 -12
- package/src/server/workspaceSettings/bootWorkspaceSettings.js +5 -8
- package/src/server/workspaceSettings/workspaceSettingsActions.js +26 -8
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +4 -4
- package/test/exportsContract.test.js +2 -1
- package/test/featureRuntime.test.js +116 -0
- package/test/packageMetadata.test.js +59 -0
- package/test/usersRouteResources.test.js +1 -1
- package/test/workspaceActionContextContributor.test.js +7 -12
- package/test/workspaceAuthPolicyContextResolver.test.js +30 -0
- package/test/workspaceRouteVisibilityResolver.test.js +31 -0
- package/test/workspaceService.test.js +2 -2
- package/test/workspaceSettingsActions.test.js +103 -17
- package/test/workspacesRouteRequestInputValidator.test.js +14 -65
- package/package.descriptor.mjs +0 -352
- package/src/server/WorkspacesCoreServiceProvider.js +0 -64
- package/src/server/registerWorkspaceBootstrap.js +0 -27
- package/src/server/registerWorkspaceCore.js +0 -103
- package/src/server/registerWorkspaceRepositories.js +0 -30
- package/src/server/support/workspaceServerScopeSupport.js +0 -18
- package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
- package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -137
- package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -130
- package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -66
- package/test/packageDescriptor.test.js +0 -91
- package/test/registerServiceRealtimeEvents.test.js +0 -116
- package/test/registerWorkspaceBootstrap.test.js +0 -86
- package/test/registerWorkspaceDirectory.test.js +0 -31
- package/test/registerWorkspaceMembers.test.js +0 -140
- package/test/registerWorkspaceSettings.test.js +0 -40
- package/test/workspaceServerScopeSupport.test.js +0 -131
- /package/{templates/migrations → migrations}/workspaces_core_initial.cjs +0 -0
- /package/{templates/migrations → migrations}/workspaces_core_workspace_settings_single_name_source.cjs +0 -0
- /package/{templates/migrations → migrations}/workspaces_core_workspaces_drop_color.cjs +0 -0
- /package/{templates → patterns/workspace-server/example}/config/roles.js +0 -0
- /package/{templates → patterns/workspace-server/example}/packages/main/src/server/email/workspaceInviteEmail.js +0 -0
|
@@ -1,91 +1,132 @@
|
|
|
1
|
+
import { normalizeDbRecordId } from "@jskit-ai/database-runtime/shared";
|
|
2
|
+
import { createEntityChangedActionEvent } from "@jskit-ai/kernel/server/actions";
|
|
1
3
|
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import {
|
|
4
|
+
import { resolveWorkspace } from "../../support/resolveWorkspace.js";
|
|
3
5
|
|
|
4
|
-
function
|
|
5
|
-
return
|
|
6
|
+
function resultValue({ result } = {}) {
|
|
7
|
+
return result?.value ?? result ?? {};
|
|
6
8
|
}
|
|
7
9
|
|
|
8
|
-
function
|
|
10
|
+
function actorId({ context } = {}) {
|
|
11
|
+
return context?.actor?.id;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function workspaceId(execution = {}) {
|
|
15
|
+
return resolveWorkspace(execution.context, execution.input)?.id || resultValue(execution)?.workspaceId;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function workspaceSlugPayload(execution = {}) {
|
|
9
19
|
return {
|
|
10
|
-
workspaceSlug: String(
|
|
20
|
+
workspaceSlug: String(
|
|
21
|
+
resolveWorkspace(execution.context, execution.input)?.slug || execution.input?.workspaceSlug || ""
|
|
22
|
+
).trim()
|
|
11
23
|
};
|
|
12
24
|
}
|
|
13
25
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
{
|
|
27
|
-
type: "entity.changed",
|
|
28
|
-
source: "users",
|
|
29
|
-
entity: "bootstrap",
|
|
30
|
-
operation: "updated",
|
|
31
|
-
entityId: resolveActorScopedEntityId,
|
|
32
|
-
realtime: {
|
|
33
|
-
event: "users.bootstrap.changed",
|
|
34
|
-
audience: "actor_user"
|
|
35
|
-
}
|
|
26
|
+
const INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE = Object.freeze({
|
|
27
|
+
preset: "event_scope",
|
|
28
|
+
async userQuery({ knex, event } = {}) {
|
|
29
|
+
if (typeof knex !== "function") return [];
|
|
30
|
+
const inviteId = normalizeRecordId(event?.entityId, { fallback: null });
|
|
31
|
+
if (!inviteId) return [];
|
|
32
|
+
const row = await knex("workspace_invites as wi")
|
|
33
|
+
.join("users as up", "up.email", "wi.email")
|
|
34
|
+
.where("wi.id", inviteId)
|
|
35
|
+
.first("up.id as user_id");
|
|
36
|
+
const userId = normalizeDbRecordId(row?.user_id, { fallback: null });
|
|
37
|
+
return userId ? [{ userId }] : [];
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
});
|
|
38
40
|
|
|
39
41
|
function createWorkspaceEntityAndBootstrapEvents({
|
|
40
42
|
workspaceEntity,
|
|
41
43
|
workspaceOperation,
|
|
42
44
|
workspaceRealtimeEvent,
|
|
43
|
-
|
|
44
|
-
bootstrapEntityId = ({ args }) => args?.[0]?.id,
|
|
45
|
+
bootstrapEntityId = workspaceId,
|
|
45
46
|
bootstrapAudience = "event_scope"
|
|
46
47
|
} = {}) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.trim()
|
|
50
|
-
.toLowerCase();
|
|
51
|
-
const normalizedWorkspaceRealtimeEvent = String(workspaceRealtimeEvent || "").trim();
|
|
52
|
-
if (!normalizedWorkspaceEntity || !normalizedWorkspaceOperation || !normalizedWorkspaceRealtimeEvent) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
"createWorkspaceEntityAndBootstrapEvents requires workspaceEntity, workspaceOperation, and workspaceRealtimeEvent."
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
if (typeof workspaceEntityId !== "function") {
|
|
58
|
-
throw new Error("createWorkspaceEntityAndBootstrapEvents requires workspaceEntityId to be a function.");
|
|
59
|
-
}
|
|
60
|
-
if (typeof bootstrapEntityId !== "function") {
|
|
61
|
-
throw new Error("createWorkspaceEntityAndBootstrapEvents requires bootstrapEntityId to be a function.");
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return deepFreeze([
|
|
65
|
-
{
|
|
66
|
-
type: "entity.changed",
|
|
48
|
+
return Object.freeze([
|
|
49
|
+
createEntityChangedActionEvent({
|
|
67
50
|
source: "workspace",
|
|
68
|
-
entity:
|
|
69
|
-
operation:
|
|
70
|
-
entityId:
|
|
51
|
+
entity: workspaceEntity,
|
|
52
|
+
operation: workspaceOperation,
|
|
53
|
+
entityId: workspaceId,
|
|
71
54
|
realtime: {
|
|
72
|
-
event:
|
|
73
|
-
|
|
74
|
-
|
|
55
|
+
event: workspaceRealtimeEvent,
|
|
56
|
+
audience: "event_scope",
|
|
57
|
+
payload: workspaceSlugPayload
|
|
75
58
|
}
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
type: "entity.changed",
|
|
59
|
+
}),
|
|
60
|
+
createEntityChangedActionEvent({
|
|
79
61
|
source: "users",
|
|
80
62
|
entity: "bootstrap",
|
|
81
63
|
operation: "updated",
|
|
82
|
-
entityId:
|
|
64
|
+
entityId: bootstrapEntityId,
|
|
83
65
|
realtime: {
|
|
84
66
|
event: "users.bootstrap.changed",
|
|
85
67
|
audience: bootstrapAudience
|
|
86
68
|
}
|
|
69
|
+
})
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function createActorEvent({ source, entity, realtimeEvent }) {
|
|
74
|
+
return createEntityChangedActionEvent({
|
|
75
|
+
source,
|
|
76
|
+
entity,
|
|
77
|
+
operation: "updated",
|
|
78
|
+
entityId: actorId,
|
|
79
|
+
realtime: { event: realtimeEvent, audience: "actor_user" }
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function createWorkspaceAudienceEvent({ entity, realtimeEvent }) {
|
|
84
|
+
return createEntityChangedActionEvent({
|
|
85
|
+
source: "workspace",
|
|
86
|
+
entity,
|
|
87
|
+
operation: "updated",
|
|
88
|
+
entityId: ({ result }) => result?.value?.workspaceId ?? result?.workspaceId,
|
|
89
|
+
realtime: {
|
|
90
|
+
event: realtimeEvent,
|
|
91
|
+
audience: ({ result }) => ({ workspaceId: result?.value?.workspaceId ?? result?.workspaceId })
|
|
87
92
|
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function onlyAccepted(builder) {
|
|
97
|
+
return (execution) => execution.input?.decision === "accept" ? builder(execution) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function createInviteDecisionEvents() {
|
|
101
|
+
return Object.freeze([
|
|
102
|
+
createActorEvent({
|
|
103
|
+
source: "workspace",
|
|
104
|
+
entity: "invitation",
|
|
105
|
+
realtimeEvent: "workspace.invitations.pending.changed"
|
|
106
|
+
}),
|
|
107
|
+
createActorEvent({
|
|
108
|
+
source: "users",
|
|
109
|
+
entity: "bootstrap",
|
|
110
|
+
realtimeEvent: "users.bootstrap.changed"
|
|
111
|
+
}),
|
|
112
|
+
onlyAccepted(createActorEvent({
|
|
113
|
+
source: "workspace",
|
|
114
|
+
entity: "directory",
|
|
115
|
+
realtimeEvent: "workspaces.changed"
|
|
116
|
+
})),
|
|
117
|
+
onlyAccepted(createWorkspaceAudienceEvent({
|
|
118
|
+
entity: "member",
|
|
119
|
+
realtimeEvent: "workspace.members.changed"
|
|
120
|
+
})),
|
|
121
|
+
createWorkspaceAudienceEvent({
|
|
122
|
+
entity: "invite",
|
|
123
|
+
realtimeEvent: "workspace.invites.changed"
|
|
124
|
+
})
|
|
88
125
|
]);
|
|
89
126
|
}
|
|
90
127
|
|
|
91
|
-
export {
|
|
128
|
+
export {
|
|
129
|
+
INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE,
|
|
130
|
+
createInviteDecisionEvents,
|
|
131
|
+
createWorkspaceEntityAndBootstrapEvents
|
|
132
|
+
};
|
|
@@ -89,17 +89,6 @@ function materializeWorkspaceActionSurfaces(actions = [], { workspaceSurfaceIds
|
|
|
89
89
|
return Object.freeze(materialized.map((entry) => Object.freeze({ ...entry })));
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function registerWorkspaceActionSurfaceSources(app) {
|
|
93
|
-
if (!app || typeof app.actionSurfaceSource !== "function") {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
app.actionSurfaceSource("workspace", ({ scope }) => {
|
|
98
|
-
const appConfig = scope?.has?.("appConfig") ? scope.make("appConfig") : {};
|
|
99
|
-
return resolveWorkspaceSurfaceIdsFromAppConfig(appConfig);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
92
|
function materializeWorkspaceActionSurfacesFromAppConfig(actions = [], { appConfig = {} } = {}) {
|
|
104
93
|
const workspaceSurfaceIds = resolveWorkspaceSurfaceIdsFromAppConfig(appConfig);
|
|
105
94
|
return materializeWorkspaceActionSurfaces(actions, { workspaceSurfaceIds });
|
|
@@ -130,6 +119,5 @@ export {
|
|
|
130
119
|
resolveWorkspaceMembershipOptionalSurfaceIdsFromAppConfig,
|
|
131
120
|
resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig,
|
|
132
121
|
materializeWorkspaceActionSurfaces,
|
|
133
|
-
materializeWorkspaceActionSurfacesFromAppConfig
|
|
134
|
-
registerWorkspaceActionSurfaceSources
|
|
122
|
+
materializeWorkspaceActionSurfacesFromAppConfig
|
|
135
123
|
};
|
|
@@ -25,17 +25,11 @@ function resolveWorkspaceRecordId(record = {}, context = {}) {
|
|
|
25
25
|
throw new Error("Workspace JSON:API response requires workspace id.");
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
function
|
|
29
|
-
if (!
|
|
30
|
-
throw new
|
|
28
|
+
function registerWorkspaceDirectoryRoutes(router, { config = {}, workspaceSelfCreateEnabled = false } = {}) {
|
|
29
|
+
if (!router || typeof router.register !== "function") {
|
|
30
|
+
throw new TypeError("registerWorkspaceDirectoryRoutes requires router.register().");
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
const router = app.make("jskit.http.router");
|
|
34
|
-
const appConfig = app.has("appConfig") ? app.make("appConfig") : {};
|
|
35
|
-
const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(appConfig);
|
|
36
|
-
const workspaceSelfCreateEnabled = app.has("workspaces.self-create.enabled")
|
|
37
|
-
? app.make("workspaces.self-create.enabled") === true
|
|
38
|
-
: false;
|
|
32
|
+
const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(config);
|
|
39
33
|
|
|
40
34
|
if (workspaceSelfCreateEnabled) {
|
|
41
35
|
router.register(
|
|
@@ -157,4 +151,4 @@ function bootWorkspaceDirectoryRoutes(app) {
|
|
|
157
151
|
);
|
|
158
152
|
}
|
|
159
153
|
|
|
160
|
-
export {
|
|
154
|
+
export { registerWorkspaceDirectoryRoutes };
|
|
@@ -16,13 +16,13 @@ const workspaceUpdateInputValidator = composeSchemaDefinitions([
|
|
|
16
16
|
context: "workspaceDirectoryActions.workspaceUpdateInputValidator"
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const workspaceDirectoryActionSpecifications = Object.freeze([
|
|
20
20
|
{
|
|
21
21
|
id: "workspace.workspaces.create",
|
|
22
22
|
version: 1,
|
|
23
23
|
kind: "command",
|
|
24
24
|
channels: ["api", "assistant_tool", "automation", "internal"],
|
|
25
|
-
|
|
25
|
+
surfaces: ["*"],
|
|
26
26
|
permission: {
|
|
27
27
|
require: "authenticated"
|
|
28
28
|
},
|
|
@@ -38,8 +38,8 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
38
38
|
description: "Create a workspace for the authenticated user."
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
async
|
|
42
|
-
return returnJsonApiData(await
|
|
41
|
+
async run(workspaceService, input, context) {
|
|
42
|
+
return returnJsonApiData(await workspaceService.createWorkspaceForAuthenticatedUser(resolveActionUser(context, input), input, {
|
|
43
43
|
request: resolveRequest(context),
|
|
44
44
|
context
|
|
45
45
|
}));
|
|
@@ -50,7 +50,7 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
50
50
|
version: 1,
|
|
51
51
|
kind: "query",
|
|
52
52
|
channels: ["api", "automation", "internal"],
|
|
53
|
-
|
|
53
|
+
surfaces: ["*"],
|
|
54
54
|
permission: {
|
|
55
55
|
require: "authenticated"
|
|
56
56
|
},
|
|
@@ -61,9 +61,9 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
61
61
|
actionName: "workspace.workspaces.list"
|
|
62
62
|
},
|
|
63
63
|
observability: {},
|
|
64
|
-
async
|
|
64
|
+
async run(workspaceService, input, context) {
|
|
65
65
|
return returnJsonApiData({
|
|
66
|
-
items: await
|
|
66
|
+
items: await workspaceService.listWorkspacesForAuthenticatedUser(resolveActionUser(context, input), {
|
|
67
67
|
request: resolveRequest(context),
|
|
68
68
|
context
|
|
69
69
|
}),
|
|
@@ -76,7 +76,7 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
76
76
|
version: 1,
|
|
77
77
|
kind: "query",
|
|
78
78
|
channels: ["api", "automation", "internal"],
|
|
79
|
-
|
|
79
|
+
surfaces: ["*"],
|
|
80
80
|
permission: {
|
|
81
81
|
require: "any",
|
|
82
82
|
permissions: ["workspace.settings.view", "workspace.settings.update"]
|
|
@@ -88,8 +88,8 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
88
88
|
actionName: "workspace.workspaces.read"
|
|
89
89
|
},
|
|
90
90
|
observability: {},
|
|
91
|
-
async
|
|
92
|
-
return returnJsonApiData(await
|
|
91
|
+
async run(workspaceService, input, context) {
|
|
92
|
+
return returnJsonApiData(await workspaceService.getWorkspaceForAuthenticatedUser(
|
|
93
93
|
resolveActionUser(context, input),
|
|
94
94
|
input.workspaceSlug,
|
|
95
95
|
{
|
|
@@ -104,7 +104,7 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
104
104
|
version: 1,
|
|
105
105
|
kind: "command",
|
|
106
106
|
channels: ["api", "assistant_tool", "automation", "internal"],
|
|
107
|
-
|
|
107
|
+
surfaces: ["*"],
|
|
108
108
|
permission: {
|
|
109
109
|
require: "all",
|
|
110
110
|
permissions: ["workspace.settings.update"]
|
|
@@ -121,9 +121,9 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
121
121
|
description: "Update workspace profile fields."
|
|
122
122
|
}
|
|
123
123
|
},
|
|
124
|
-
async
|
|
124
|
+
async run(workspaceService, input, context) {
|
|
125
125
|
const { workspaceSlug, ...patch } = input;
|
|
126
|
-
return returnJsonApiData(await
|
|
126
|
+
return returnJsonApiData(await workspaceService.updateWorkspaceForAuthenticatedUser(
|
|
127
127
|
resolveActionUser(context, input),
|
|
128
128
|
workspaceSlug,
|
|
129
129
|
patch,
|
|
@@ -136,4 +136,14 @@ const workspaceDirectoryActions = Object.freeze([
|
|
|
136
136
|
}
|
|
137
137
|
]);
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
function buildWorkspaceDirectoryActions({ workspaceService } = {}) {
|
|
140
|
+
if (!workspaceService) throw new TypeError("buildWorkspaceDirectoryActions requires workspaceService.");
|
|
141
|
+
return workspaceDirectoryActionSpecifications.map(({ run, ...definition }) => Object.freeze({
|
|
142
|
+
...definition,
|
|
143
|
+
execute(input, context) {
|
|
144
|
+
return run(workspaceService, input, context);
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { workspaceDirectoryActionSpecifications, buildWorkspaceDirectoryActions };
|
|
@@ -28,18 +28,11 @@ function resolveWorkspaceAggregateRecordId(record = {}, context = {}) {
|
|
|
28
28
|
throw new Error("Workspace JSON:API response requires workspace id.");
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
32
|
-
if (!
|
|
33
|
-
throw new
|
|
31
|
+
function registerWorkspaceMembersRoutes(router, { config = {}, workspaceInvitationsEnabled = false } = {}) {
|
|
32
|
+
if (!router || typeof router.register !== "function") {
|
|
33
|
+
throw new TypeError("registerWorkspaceMembersRoutes requires router.register().");
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
const router = app.make("jskit.http.router");
|
|
37
|
-
const appConfig = typeof app.has === "function" && app.has("appConfig") ? app.make("appConfig") : {};
|
|
38
|
-
const workspaceInvitationsEnabled =
|
|
39
|
-
typeof app.has === "function" && app.has("workspaces.invitations.enabled")
|
|
40
|
-
? app.make("workspaces.invitations.enabled") === true
|
|
41
|
-
: false;
|
|
42
|
-
const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(appConfig);
|
|
35
|
+
const workspaceRouteSurfaceId = resolveDefaultWorkspaceRouteSurfaceIdFromAppConfig(config);
|
|
43
36
|
|
|
44
37
|
router.register(
|
|
45
38
|
"GET",
|
|
@@ -271,4 +264,4 @@ function bootWorkspaceMembers(app) {
|
|
|
271
264
|
}
|
|
272
265
|
}
|
|
273
266
|
|
|
274
|
-
export {
|
|
267
|
+
export { registerWorkspaceMembersRoutes };
|
|
@@ -3,8 +3,34 @@ import { returnJsonApiData } from "@jskit-ai/http-runtime/shared";
|
|
|
3
3
|
import { resolveWorkspace } from "../support/resolveWorkspace.js";
|
|
4
4
|
import { resolveActionUser } from "../common/support/resolveActionUser.js";
|
|
5
5
|
import { workspaceMembersResource } from "../../shared/resources/workspaceMembersResource.js";
|
|
6
|
+
import {
|
|
7
|
+
INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE,
|
|
8
|
+
createWorkspaceEntityAndBootstrapEvents
|
|
9
|
+
} from "../common/support/realtimeServiceEvents.js";
|
|
6
10
|
import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
|
|
7
11
|
|
|
12
|
+
const WORKSPACE_MEMBER_CHANGED_EVENTS = createWorkspaceEntityAndBootstrapEvents({
|
|
13
|
+
workspaceEntity: "member",
|
|
14
|
+
workspaceOperation: "updated",
|
|
15
|
+
workspaceRealtimeEvent: "workspace.members.changed"
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const WORKSPACE_INVITE_CREATED_EVENTS = createWorkspaceEntityAndBootstrapEvents({
|
|
19
|
+
workspaceEntity: "invite",
|
|
20
|
+
workspaceOperation: "created",
|
|
21
|
+
workspaceRealtimeEvent: "workspace.invites.changed",
|
|
22
|
+
bootstrapEntityId: ({ result } = {}) => result?.value?.createdInviteId,
|
|
23
|
+
bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const WORKSPACE_INVITE_REVOKED_EVENTS = createWorkspaceEntityAndBootstrapEvents({
|
|
27
|
+
workspaceEntity: "invite",
|
|
28
|
+
workspaceOperation: "updated",
|
|
29
|
+
workspaceRealtimeEvent: "workspace.invites.changed",
|
|
30
|
+
bootstrapEntityId: ({ result } = {}) => result?.value?.revokedInviteId,
|
|
31
|
+
bootstrapAudience: INVITE_RECIPIENT_BOOTSTRAP_AUDIENCE
|
|
32
|
+
});
|
|
33
|
+
|
|
8
34
|
const updateMemberRoleActionInput = composeSchemaDefinitions([
|
|
9
35
|
workspaceSlugParamsValidator,
|
|
10
36
|
workspaceMembersResource.operations.updateMemberRole.input
|
|
@@ -37,13 +63,13 @@ const revokeInviteActionInput = composeSchemaDefinitions([
|
|
|
37
63
|
context: "workspaceMembersActions.revokeInviteActionInput"
|
|
38
64
|
});
|
|
39
65
|
|
|
40
|
-
const
|
|
66
|
+
const workspaceMembersActionSpecifications = Object.freeze([
|
|
41
67
|
{
|
|
42
68
|
id: "workspace.roles.list",
|
|
43
69
|
version: 1,
|
|
44
70
|
kind: "query",
|
|
45
71
|
channels: ["api", "automation", "internal"],
|
|
46
|
-
|
|
72
|
+
surfaces: ["*"],
|
|
47
73
|
permission: {
|
|
48
74
|
require: "all",
|
|
49
75
|
permissions: ["workspace.roles.view"]
|
|
@@ -55,8 +81,8 @@ const workspaceMembersActions = Object.freeze([
|
|
|
55
81
|
actionName: "workspace.roles.list"
|
|
56
82
|
},
|
|
57
83
|
observability: {},
|
|
58
|
-
async
|
|
59
|
-
return returnJsonApiData(await
|
|
84
|
+
async run(workspaceMembersService, _input, context) {
|
|
85
|
+
return returnJsonApiData(await workspaceMembersService.listRoles({ context }));
|
|
60
86
|
}
|
|
61
87
|
},
|
|
62
88
|
{
|
|
@@ -64,7 +90,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
64
90
|
version: 1,
|
|
65
91
|
kind: "query",
|
|
66
92
|
channels: ["api", "automation", "internal"],
|
|
67
|
-
|
|
93
|
+
surfaces: ["*"],
|
|
68
94
|
permission: {
|
|
69
95
|
require: "all",
|
|
70
96
|
permissions: ["workspace.members.view"]
|
|
@@ -76,8 +102,8 @@ const workspaceMembersActions = Object.freeze([
|
|
|
76
102
|
actionName: "workspace.members.list"
|
|
77
103
|
},
|
|
78
104
|
observability: {},
|
|
79
|
-
async
|
|
80
|
-
return returnJsonApiData(await
|
|
105
|
+
async run(workspaceMembersService, input, context) {
|
|
106
|
+
return returnJsonApiData(await workspaceMembersService.listMembers(resolveWorkspace(context, input), {
|
|
81
107
|
context
|
|
82
108
|
}));
|
|
83
109
|
}
|
|
@@ -87,7 +113,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
87
113
|
version: 1,
|
|
88
114
|
kind: "command",
|
|
89
115
|
channels: ["api", "automation", "internal"],
|
|
90
|
-
|
|
116
|
+
surfaces: ["*"],
|
|
91
117
|
permission: {
|
|
92
118
|
require: "all",
|
|
93
119
|
permissions: ["workspace.members.manage"]
|
|
@@ -99,8 +125,9 @@ const workspaceMembersActions = Object.freeze([
|
|
|
99
125
|
actionName: "workspace.member.role.update"
|
|
100
126
|
},
|
|
101
127
|
observability: {},
|
|
102
|
-
|
|
103
|
-
|
|
128
|
+
events: WORKSPACE_MEMBER_CHANGED_EVENTS,
|
|
129
|
+
async run(workspaceMembersService, input, context) {
|
|
130
|
+
return returnJsonApiData(await workspaceMembersService.updateMemberRole(resolveWorkspace(context, input), {
|
|
104
131
|
memberUserId: input.memberUserId,
|
|
105
132
|
roleSid: input.roleSid
|
|
106
133
|
}, {
|
|
@@ -113,7 +140,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
113
140
|
version: 1,
|
|
114
141
|
kind: "command",
|
|
115
142
|
channels: ["api", "automation", "internal"],
|
|
116
|
-
|
|
143
|
+
surfaces: ["*"],
|
|
117
144
|
permission: {
|
|
118
145
|
require: "all",
|
|
119
146
|
permissions: ["workspace.members.manage"]
|
|
@@ -125,8 +152,9 @@ const workspaceMembersActions = Object.freeze([
|
|
|
125
152
|
actionName: "workspace.member.remove"
|
|
126
153
|
},
|
|
127
154
|
observability: {},
|
|
128
|
-
|
|
129
|
-
|
|
155
|
+
events: WORKSPACE_MEMBER_CHANGED_EVENTS,
|
|
156
|
+
async run(workspaceMembersService, input, context) {
|
|
157
|
+
return returnJsonApiData(await workspaceMembersService.removeMember(resolveWorkspace(context, input), {
|
|
130
158
|
memberUserId: input.memberUserId
|
|
131
159
|
}, {
|
|
132
160
|
context
|
|
@@ -138,7 +166,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
138
166
|
version: 1,
|
|
139
167
|
kind: "query",
|
|
140
168
|
channels: ["api", "automation", "internal"],
|
|
141
|
-
|
|
169
|
+
surfaces: ["*"],
|
|
142
170
|
permission: {
|
|
143
171
|
require: "all",
|
|
144
172
|
permissions: ["workspace.members.view"]
|
|
@@ -150,8 +178,8 @@ const workspaceMembersActions = Object.freeze([
|
|
|
150
178
|
actionName: "workspace.invites.list"
|
|
151
179
|
},
|
|
152
180
|
observability: {},
|
|
153
|
-
async
|
|
154
|
-
return returnJsonApiData(await
|
|
181
|
+
async run(workspaceMembersService, input, context) {
|
|
182
|
+
return returnJsonApiData(await workspaceMembersService.listInvites(resolveWorkspace(context, input), {
|
|
155
183
|
context
|
|
156
184
|
}));
|
|
157
185
|
}
|
|
@@ -161,7 +189,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
161
189
|
version: 1,
|
|
162
190
|
kind: "command",
|
|
163
191
|
channels: ["api", "assistant_tool", "automation", "internal"],
|
|
164
|
-
|
|
192
|
+
surfaces: ["*"],
|
|
165
193
|
permission: {
|
|
166
194
|
require: "all",
|
|
167
195
|
permissions: ["workspace.members.invite"]
|
|
@@ -173,13 +201,14 @@ const workspaceMembersActions = Object.freeze([
|
|
|
173
201
|
actionName: "workspace.invite.create"
|
|
174
202
|
},
|
|
175
203
|
observability: {},
|
|
204
|
+
events: WORKSPACE_INVITE_CREATED_EVENTS,
|
|
176
205
|
extensions: {
|
|
177
206
|
assistant: {
|
|
178
207
|
description: "Invite a person to the workspace."
|
|
179
208
|
}
|
|
180
209
|
},
|
|
181
|
-
async
|
|
182
|
-
return returnJsonApiData(await
|
|
210
|
+
async run(workspaceMembersService, input, context) {
|
|
211
|
+
return returnJsonApiData(await workspaceMembersService.createInvite(
|
|
183
212
|
resolveWorkspace(context, input),
|
|
184
213
|
resolveActionUser(context, input),
|
|
185
214
|
{
|
|
@@ -197,7 +226,7 @@ const workspaceMembersActions = Object.freeze([
|
|
|
197
226
|
version: 1,
|
|
198
227
|
kind: "command",
|
|
199
228
|
channels: ["api", "automation", "internal"],
|
|
200
|
-
|
|
229
|
+
surfaces: ["*"],
|
|
201
230
|
permission: {
|
|
202
231
|
require: "all",
|
|
203
232
|
permissions: ["workspace.invites.revoke"]
|
|
@@ -209,12 +238,23 @@ const workspaceMembersActions = Object.freeze([
|
|
|
209
238
|
actionName: "workspace.invite.revoke"
|
|
210
239
|
},
|
|
211
240
|
observability: {},
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
events: WORKSPACE_INVITE_REVOKED_EVENTS,
|
|
242
|
+
async run(workspaceMembersService, input, context) {
|
|
243
|
+
return returnJsonApiData(await workspaceMembersService.revokeInvite(resolveWorkspace(context, input), input.inviteId, {
|
|
214
244
|
context
|
|
215
245
|
}));
|
|
216
246
|
}
|
|
217
247
|
}
|
|
218
248
|
]);
|
|
219
249
|
|
|
220
|
-
|
|
250
|
+
function buildWorkspaceMembersActions({ workspaceMembersService } = {}) {
|
|
251
|
+
if (!workspaceMembersService) throw new TypeError("buildWorkspaceMembersActions requires workspaceMembersService.");
|
|
252
|
+
return workspaceMembersActionSpecifications.map(({ run, ...definition }) => Object.freeze({
|
|
253
|
+
...definition,
|
|
254
|
+
execute(input, context) {
|
|
255
|
+
return run(workspaceMembersService, input, context);
|
|
256
|
+
}
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export { workspaceMembersActionSpecifications, buildWorkspaceMembersActions };
|
|
@@ -24,13 +24,11 @@ function resolveInviteResolutionRecordId(record = {}) {
|
|
|
24
24
|
return "workspace-invitation";
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
function
|
|
28
|
-
if (!
|
|
29
|
-
throw new
|
|
27
|
+
function registerWorkspacePendingInvitationsRoutes(router) {
|
|
28
|
+
if (!router || typeof router.register !== "function") {
|
|
29
|
+
throw new TypeError("registerWorkspacePendingInvitationsRoutes requires router.register().");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
const router = app.make("jskit.http.router");
|
|
33
|
-
|
|
34
32
|
router.register(
|
|
35
33
|
"GET",
|
|
36
34
|
"/api/workspace/invitations/resolve",
|
|
@@ -110,4 +108,4 @@ function bootWorkspacePendingInvitations(app) {
|
|
|
110
108
|
);
|
|
111
109
|
}
|
|
112
110
|
|
|
113
|
-
export {
|
|
111
|
+
export { registerWorkspacePendingInvitationsRoutes };
|