@jskit-ai/users-core 0.1.32 → 0.1.35
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 +16 -245
- package/package.json +7 -7
- package/src/server/UsersCoreServiceProvider.js +4 -28
- package/src/server/UsersWorkspacesServiceProvider.js +44 -0
- package/src/server/accountNotifications/accountNotificationsService.js +3 -3
- package/src/server/accountNotifications/registerAccountNotifications.js +1 -1
- package/src/server/accountPreferences/accountPreferencesService.js +3 -3
- package/src/server/accountPreferences/registerAccountPreferences.js +1 -1
- package/src/server/accountProfile/accountProfileActions.js +8 -2
- package/src/server/accountProfile/accountProfileService.js +10 -10
- package/src/server/accountProfile/avatarService.js +9 -9
- package/src/server/accountProfile/bootAccountProfileRoutes.js +5 -3
- package/src/server/accountProfile/registerAccountProfile.js +2 -2
- package/src/server/accountSecurity/accountSecurityService.js +3 -3
- package/src/server/accountSecurity/registerAccountSecurity.js +1 -1
- package/src/server/common/contributors/workspaceActionContextContributor.js +24 -17
- package/src/server/common/registerCommonRepositories.js +3 -22
- package/src/server/common/repositories/userSettingsRepository.js +1 -12
- package/src/server/common/repositories/{userProfilesRepository.js → usersRepository.js} +1 -1
- package/src/server/common/services/accountContextService.js +4 -4
- package/src/server/common/services/authProfileSyncService.js +10 -10
- package/src/server/registerUsersBootstrap.js +22 -0
- package/src/server/registerUsersCore.js +30 -0
- package/src/server/registerWorkspaceBootstrap.js +3 -6
- package/src/server/registerWorkspaceCore.js +5 -17
- package/src/server/registerWorkspaceRepositories.js +26 -0
- package/src/server/usersBootstrapContributor.js +248 -0
- package/src/server/workspaceBootstrapContributor.js +65 -259
- package/src/shared/roles.js +31 -6
- package/src/shared/settings.js +1 -2
- package/templates/migrations/users_core_generic_initial.cjs +69 -0
- package/test/authProfileSyncService.test.js +3 -3
- package/test/avatarService.test.js +2 -2
- package/test/registerUsersCore.test.js +42 -0
- package/test/roles.test.js +90 -5
- package/test/usersBootstrapContributor.test.js +172 -0
- package/test/usersRouteRequestInputValidator.test.js +7 -390
- package/test/workspaceActionContextContributor.test.js +98 -5
- package/test/workspaceBootstrapContributor.test.js +34 -346
- package/test/workspaceMembersService.test.js +4 -2
- package/test/workspaceService.test.js +12 -8
- package/test/workspaceSettingsResource.test.js +4 -2
- package/test-support/registerDefaultSettingsFields.js +1 -1
- package/templates/config/workspaceRoles.js +0 -30
- package/templates/migrations/users_core_initial.cjs +0 -123
- package/templates/migrations/users_core_workspace_settings_single_name_source.cjs +0 -71
- package/templates/migrations/users_core_workspaces_drop_color.cjs +0 -85
- package/templates/packages/main/src/shared/resources/workspaceSettingsFields.js +0 -197
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
2
|
import test from "node:test";
|
|
3
3
|
import { UsersCoreServiceProvider } from "../src/server/UsersCoreServiceProvider.js";
|
|
4
|
-
import { resolveTenancyProfile } from "../src/shared/tenancyProfile.js";
|
|
5
4
|
|
|
6
5
|
function createReplyDouble() {
|
|
7
6
|
return {
|
|
@@ -29,11 +28,7 @@ function findRoute(routes, { method, path }) {
|
|
|
29
28
|
|
|
30
29
|
async function registerRoutes({
|
|
31
30
|
authService = {},
|
|
32
|
-
consoleService = null
|
|
33
|
-
workspaceEnabled = true,
|
|
34
|
-
workspaceTenancyEnabled = true,
|
|
35
|
-
workspaceInvitationsEnabled = true,
|
|
36
|
-
workspaceSelfCreateEnabled = true
|
|
31
|
+
consoleService = null
|
|
37
32
|
} = {}) {
|
|
38
33
|
const registeredRoutes = [];
|
|
39
34
|
const router = {
|
|
@@ -61,11 +56,7 @@ async function registerRoutes({
|
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
],
|
|
64
|
-
["actionExecutor", {}]
|
|
65
|
-
["users.workspace.enabled", workspaceEnabled],
|
|
66
|
-
["users.workspace.tenancy.enabled", workspaceTenancyEnabled],
|
|
67
|
-
["users.workspace.invitations.enabled", workspaceInvitationsEnabled],
|
|
68
|
-
["users.workspace.self-create.enabled", workspaceSelfCreateEnabled]
|
|
59
|
+
["actionExecutor", {}]
|
|
69
60
|
]);
|
|
70
61
|
|
|
71
62
|
if (consoleService) {
|
|
@@ -90,23 +81,6 @@ async function registerRoutes({
|
|
|
90
81
|
return registeredRoutes;
|
|
91
82
|
}
|
|
92
83
|
|
|
93
|
-
async function registerRoutesForMode({
|
|
94
|
-
tenancyMode = "none",
|
|
95
|
-
tenancyPolicy = {}
|
|
96
|
-
} = {}) {
|
|
97
|
-
const tenancyProfile = resolveTenancyProfile({
|
|
98
|
-
tenancyMode,
|
|
99
|
-
tenancyPolicy
|
|
100
|
-
});
|
|
101
|
-
return registerRoutes({
|
|
102
|
-
workspaceEnabled: tenancyProfile.workspace.enabled === true,
|
|
103
|
-
workspaceTenancyEnabled: tenancyProfile.mode === "workspace",
|
|
104
|
-
workspaceInvitationsEnabled:
|
|
105
|
-
tenancyProfile.workspace.enabled === true && tenancyProfile.mode !== "none",
|
|
106
|
-
workspaceSelfCreateEnabled: tenancyProfile.workspace.allowSelfCreate === true
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
84
|
function createActionRequest({ input = {}, executeAction, file = null }) {
|
|
111
85
|
return {
|
|
112
86
|
input,
|
|
@@ -118,372 +92,15 @@ function createActionRequest({ input = {}, executeAction, file = null }) {
|
|
|
118
92
|
};
|
|
119
93
|
}
|
|
120
94
|
|
|
121
|
-
test("
|
|
95
|
+
test("users-core boot mounts account and console routes without workspace routes", async () => {
|
|
122
96
|
const routes = await registerRoutes();
|
|
123
97
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
});
|
|
128
|
-
const workspacePatch = findRoute(routes, {
|
|
129
|
-
method: "PATCH",
|
|
130
|
-
path: "/api/w/:workspaceSlug"
|
|
131
|
-
});
|
|
132
|
-
const workspaceSettingsPatch = findRoute(routes, {
|
|
133
|
-
method: "PATCH",
|
|
134
|
-
path: "/api/w/:workspaceSlug/settings"
|
|
135
|
-
});
|
|
136
|
-
const workspaceMemberRole = findRoute(routes, {
|
|
137
|
-
method: "PATCH",
|
|
138
|
-
path: "/api/w/:workspaceSlug/members/:memberUserId/role"
|
|
139
|
-
});
|
|
140
|
-
const workspaceMemberDelete = findRoute(routes, {
|
|
141
|
-
method: "DELETE",
|
|
142
|
-
path: "/api/w/:workspaceSlug/members/:memberUserId"
|
|
143
|
-
});
|
|
144
|
-
const workspaceInviteDelete = findRoute(routes, {
|
|
145
|
-
method: "DELETE",
|
|
146
|
-
path: "/api/w/:workspaceSlug/invites/:inviteId"
|
|
147
|
-
});
|
|
148
|
-
const settingsProfilePatch = findRoute(routes, {
|
|
149
|
-
method: "PATCH",
|
|
150
|
-
path: "/api/settings/profile"
|
|
151
|
-
});
|
|
152
|
-
const settingsOAuthStart = findRoute(routes, {
|
|
153
|
-
method: "GET",
|
|
154
|
-
path: "/api/settings/security/oauth/:provider/start"
|
|
155
|
-
});
|
|
156
|
-
const consoleSettingsPatch = findRoute(routes, {
|
|
157
|
-
method: "PATCH",
|
|
158
|
-
path: "/api/console/settings"
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
assert.equal(typeof workspaceSettings?.paramsValidator?.normalize, "function");
|
|
162
|
-
assert.equal(typeof workspacePatch?.bodyValidator?.normalize, "function");
|
|
163
|
-
assert.equal(typeof workspaceSettingsPatch?.bodyValidator?.normalize, "function");
|
|
164
|
-
assert.equal(typeof workspaceMemberRole?.paramsValidator?.normalize, "function");
|
|
165
|
-
assert.equal(typeof workspaceMemberRole?.bodyValidator?.normalize, "function");
|
|
166
|
-
assert.equal(typeof workspaceMemberDelete?.paramsValidator?.normalize, "function");
|
|
167
|
-
assert.equal(typeof workspaceInviteDelete?.paramsValidator?.normalize, "function");
|
|
168
|
-
assert.equal(typeof settingsProfilePatch?.bodyValidator?.normalize, "function");
|
|
169
|
-
assert.equal(typeof settingsOAuthStart?.paramsValidator?.normalize, "function");
|
|
170
|
-
assert.equal(typeof settingsOAuthStart?.queryValidator?.normalize, "function");
|
|
171
|
-
assert.equal(typeof consoleSettingsPatch?.bodyValidator?.normalize, "function");
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test("workspace core/settings routes mount one canonical workspace endpoint", async () => {
|
|
175
|
-
const routes = await registerRoutes();
|
|
176
|
-
const workspace = findRoute(routes, {
|
|
177
|
-
method: "GET",
|
|
178
|
-
path: "/api/w/:workspaceSlug"
|
|
179
|
-
});
|
|
180
|
-
const workspacePatch = findRoute(routes, {
|
|
181
|
-
method: "PATCH",
|
|
182
|
-
path: "/api/w/:workspaceSlug"
|
|
183
|
-
});
|
|
184
|
-
const workspaceSettings = findRoute(routes, {
|
|
185
|
-
method: "GET",
|
|
186
|
-
path: "/api/w/:workspaceSlug/settings"
|
|
187
|
-
});
|
|
188
|
-
const workspaceSettingsPatch = findRoute(routes, {
|
|
189
|
-
method: "PATCH",
|
|
190
|
-
path: "/api/w/:workspaceSlug/settings"
|
|
191
|
-
});
|
|
192
|
-
const adminWorkspaceSettings = findRoute(routes, {
|
|
193
|
-
method: "GET",
|
|
194
|
-
path: "/api/admin/w/:workspaceSlug/workspace/settings"
|
|
195
|
-
});
|
|
196
|
-
const consoleWorkspaceSettings = findRoute(routes, {
|
|
197
|
-
method: "GET",
|
|
198
|
-
path: "/api/console/w/:workspaceSlug/workspace/settings"
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
assert.ok(workspace);
|
|
202
|
-
assert.equal(workspace?.visibility, "workspace");
|
|
203
|
-
assert.equal(workspacePatch?.visibility, "workspace");
|
|
204
|
-
assert.equal(workspace?.surface, "");
|
|
205
|
-
assert.equal(workspacePatch?.surface, "");
|
|
206
|
-
assert.ok(workspaceSettings);
|
|
207
|
-
assert.equal(workspaceSettings?.visibility, "workspace");
|
|
208
|
-
assert.equal(workspaceSettingsPatch?.visibility, "workspace");
|
|
209
|
-
assert.equal(workspaceSettings?.surface, "");
|
|
210
|
-
assert.equal(workspaceSettingsPatch?.surface, "");
|
|
211
|
-
assert.equal(adminWorkspaceSettings, null);
|
|
212
|
-
assert.equal(consoleWorkspaceSettings, null);
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test("users-core boot skips workspace routes when workspace policy is disabled", async () => {
|
|
216
|
-
const routes = await registerRoutes({
|
|
217
|
-
workspaceEnabled: false,
|
|
218
|
-
workspaceTenancyEnabled: false,
|
|
219
|
-
workspaceInvitationsEnabled: false,
|
|
220
|
-
workspaceSelfCreateEnabled: false
|
|
221
|
-
});
|
|
222
|
-
|
|
98
|
+
assert.equal(findRoute(routes, { method: "GET", path: "/api/settings" })?.path, "/api/settings");
|
|
99
|
+
assert.equal(findRoute(routes, { method: "PATCH", path: "/api/settings/profile" })?.path, "/api/settings/profile");
|
|
100
|
+
assert.equal(findRoute(routes, { method: "GET", path: "/api/console/settings" })?.path, "/api/console/settings");
|
|
101
|
+
assert.equal(findRoute(routes, { method: "PATCH", path: "/api/console/settings" })?.path, "/api/console/settings");
|
|
223
102
|
assert.equal(findRoute(routes, { method: "GET", path: "/api/workspaces" }), null);
|
|
224
|
-
assert.equal(findRoute(routes, { method: "POST", path: "/api/workspaces" }), null);
|
|
225
|
-
assert.equal(findRoute(routes, { method: "GET", path: "/api/w/:workspaceSlug" }), null);
|
|
226
|
-
assert.equal(findRoute(routes, { method: "PATCH", path: "/api/w/:workspaceSlug" }), null);
|
|
227
103
|
assert.equal(findRoute(routes, { method: "GET", path: "/api/w/:workspaceSlug/settings" }), null);
|
|
228
|
-
assert.equal(findRoute(routes, { method: "GET", path: "/api/settings" })?.path, "/api/settings");
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
test("users-core boot skips workspace create route when self-create policy is disabled", async () => {
|
|
232
|
-
const routes = await registerRoutes({
|
|
233
|
-
workspaceEnabled: true,
|
|
234
|
-
workspaceTenancyEnabled: true,
|
|
235
|
-
workspaceInvitationsEnabled: true,
|
|
236
|
-
workspaceSelfCreateEnabled: false
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
assert.equal(findRoute(routes, { method: "POST", path: "/api/workspaces" }), null);
|
|
240
|
-
assert.equal(findRoute(routes, { method: "GET", path: "/api/workspaces" })?.path, "/api/workspaces");
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
test("users-core route registration follows tenancy mode matrix", async () => {
|
|
244
|
-
const noneRoutes = await registerRoutesForMode({
|
|
245
|
-
tenancyMode: "none"
|
|
246
|
-
});
|
|
247
|
-
const personalRoutes = await registerRoutesForMode({
|
|
248
|
-
tenancyMode: "personal"
|
|
249
|
-
});
|
|
250
|
-
const workspaceRoutes = await registerRoutesForMode({
|
|
251
|
-
tenancyMode: "workspaces"
|
|
252
|
-
});
|
|
253
|
-
const workspaceSelfCreateRoutes = await registerRoutesForMode({
|
|
254
|
-
tenancyMode: "workspaces",
|
|
255
|
-
tenancyPolicy: {
|
|
256
|
-
workspace: {
|
|
257
|
-
allowSelfCreate: true
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
assert.equal(findRoute(noneRoutes, { method: "GET", path: "/api/workspaces" }), null);
|
|
263
|
-
assert.equal(findRoute(noneRoutes, { method: "POST", path: "/api/workspaces" }), null);
|
|
264
|
-
assert.equal(findRoute(noneRoutes, { method: "GET", path: "/api/w/:workspaceSlug" }), null);
|
|
265
|
-
assert.equal(findRoute(noneRoutes, { method: "PATCH", path: "/api/w/:workspaceSlug" }), null);
|
|
266
|
-
assert.equal(findRoute(noneRoutes, { method: "GET", path: "/api/w/:workspaceSlug/settings" }), null);
|
|
267
|
-
assert.equal(findRoute(noneRoutes, { method: "GET", path: "/api/workspace/invitations/pending" }), null);
|
|
268
|
-
|
|
269
|
-
assert.equal(findRoute(personalRoutes, { method: "GET", path: "/api/workspaces" })?.path, "/api/workspaces");
|
|
270
|
-
assert.equal(findRoute(personalRoutes, { method: "POST", path: "/api/workspaces" }), null);
|
|
271
|
-
assert.equal(
|
|
272
|
-
findRoute(personalRoutes, { method: "GET", path: "/api/w/:workspaceSlug" })?.path,
|
|
273
|
-
"/api/w/:workspaceSlug"
|
|
274
|
-
);
|
|
275
|
-
assert.equal(
|
|
276
|
-
findRoute(personalRoutes, { method: "PATCH", path: "/api/w/:workspaceSlug" })?.path,
|
|
277
|
-
"/api/w/:workspaceSlug"
|
|
278
|
-
);
|
|
279
|
-
assert.equal(
|
|
280
|
-
findRoute(personalRoutes, { method: "GET", path: "/api/w/:workspaceSlug/settings" })?.path,
|
|
281
|
-
"/api/w/:workspaceSlug/settings"
|
|
282
|
-
);
|
|
283
|
-
assert.equal(
|
|
284
|
-
findRoute(personalRoutes, { method: "GET", path: "/api/workspace/invitations/pending" })?.path,
|
|
285
|
-
"/api/workspace/invitations/pending"
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
assert.equal(findRoute(workspaceRoutes, { method: "GET", path: "/api/workspaces" })?.path, "/api/workspaces");
|
|
289
|
-
assert.equal(findRoute(workspaceRoutes, { method: "POST", path: "/api/workspaces" }), null);
|
|
290
|
-
assert.equal(
|
|
291
|
-
findRoute(workspaceRoutes, { method: "GET", path: "/api/w/:workspaceSlug" })?.path,
|
|
292
|
-
"/api/w/:workspaceSlug"
|
|
293
|
-
);
|
|
294
|
-
assert.equal(
|
|
295
|
-
findRoute(workspaceRoutes, { method: "PATCH", path: "/api/w/:workspaceSlug" })?.path,
|
|
296
|
-
"/api/w/:workspaceSlug"
|
|
297
|
-
);
|
|
298
|
-
assert.equal(
|
|
299
|
-
findRoute(workspaceRoutes, { method: "GET", path: "/api/w/:workspaceSlug/settings" })?.path,
|
|
300
|
-
"/api/w/:workspaceSlug/settings"
|
|
301
|
-
);
|
|
302
|
-
assert.equal(
|
|
303
|
-
findRoute(workspaceRoutes, { method: "GET", path: "/api/workspace/invitations/pending" })?.path,
|
|
304
|
-
"/api/workspace/invitations/pending"
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
assert.equal(
|
|
308
|
-
findRoute(workspaceSelfCreateRoutes, { method: "POST", path: "/api/workspaces" })?.path,
|
|
309
|
-
"/api/workspaces"
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
test("users-core boot skips invitation redeem/list routes when workspace invitations are disabled", async () => {
|
|
314
|
-
const routes = await registerRoutes({
|
|
315
|
-
workspaceEnabled: true,
|
|
316
|
-
workspaceTenancyEnabled: true,
|
|
317
|
-
workspaceInvitationsEnabled: false,
|
|
318
|
-
workspaceSelfCreateEnabled: false
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
assert.equal(findRoute(routes, { method: "GET", path: "/api/workspace/invitations/pending" }), null);
|
|
322
|
-
assert.equal(findRoute(routes, { method: "POST", path: "/api/workspace/invitations/redeem" }), null);
|
|
323
|
-
assert.equal(findRoute(routes, { method: "GET", path: "/api/w/:workspaceSlug/invites" }), null);
|
|
324
|
-
assert.equal(findRoute(routes, { method: "POST", path: "/api/w/:workspaceSlug/invites" }), null);
|
|
325
|
-
assert.equal(findRoute(routes, { method: "DELETE", path: "/api/w/:workspaceSlug/invites/:inviteId" }), null);
|
|
326
|
-
});
|
|
327
|
-
|
|
328
|
-
test("workspace invite and member handlers build action input from request.input", async () => {
|
|
329
|
-
const routes = await registerRoutes();
|
|
330
|
-
const workspaceCreate = findRoute(routes, {
|
|
331
|
-
method: "POST",
|
|
332
|
-
path: "/api/workspaces"
|
|
333
|
-
});
|
|
334
|
-
const workspaceInviteRedeem = findRoute(routes, {
|
|
335
|
-
method: "POST",
|
|
336
|
-
path: "/api/workspace/invitations/redeem"
|
|
337
|
-
});
|
|
338
|
-
const workspaceMemberRolePatch = findRoute(routes, {
|
|
339
|
-
method: "PATCH",
|
|
340
|
-
path: "/api/w/:workspaceSlug/members/:memberUserId/role"
|
|
341
|
-
});
|
|
342
|
-
const workspaceMemberDelete = findRoute(routes, {
|
|
343
|
-
method: "DELETE",
|
|
344
|
-
path: "/api/w/:workspaceSlug/members/:memberUserId"
|
|
345
|
-
});
|
|
346
|
-
const workspaceInviteCreate = findRoute(routes, {
|
|
347
|
-
method: "POST",
|
|
348
|
-
path: "/api/w/:workspaceSlug/invites"
|
|
349
|
-
});
|
|
350
|
-
const workspaceInviteDelete = findRoute(routes, {
|
|
351
|
-
method: "DELETE",
|
|
352
|
-
path: "/api/w/:workspaceSlug/invites/:inviteId"
|
|
353
|
-
});
|
|
354
|
-
const calls = [];
|
|
355
|
-
const executeAction = async (payload) => {
|
|
356
|
-
calls.push(payload);
|
|
357
|
-
return {};
|
|
358
|
-
};
|
|
359
|
-
|
|
360
|
-
await workspaceCreate.handler(
|
|
361
|
-
createActionRequest({
|
|
362
|
-
input: {
|
|
363
|
-
body: { name: "Operations", slug: "operations" }
|
|
364
|
-
},
|
|
365
|
-
executeAction
|
|
366
|
-
}),
|
|
367
|
-
createReplyDouble()
|
|
368
|
-
);
|
|
369
|
-
await workspaceInviteRedeem.handler(
|
|
370
|
-
createActionRequest({
|
|
371
|
-
input: {
|
|
372
|
-
body: { token: "token-1", decision: "accept" }
|
|
373
|
-
},
|
|
374
|
-
executeAction
|
|
375
|
-
}),
|
|
376
|
-
createReplyDouble()
|
|
377
|
-
);
|
|
378
|
-
await workspaceMemberRolePatch.handler(
|
|
379
|
-
createActionRequest({
|
|
380
|
-
input: {
|
|
381
|
-
params: { workspaceSlug: "acme", memberUserId: "12" },
|
|
382
|
-
body: { roleSid: "admin" }
|
|
383
|
-
},
|
|
384
|
-
executeAction
|
|
385
|
-
}),
|
|
386
|
-
createReplyDouble()
|
|
387
|
-
);
|
|
388
|
-
await workspaceInviteCreate.handler(
|
|
389
|
-
createActionRequest({
|
|
390
|
-
input: {
|
|
391
|
-
params: { workspaceSlug: "acme" },
|
|
392
|
-
body: { email: "user@example.com", roleSid: "member" }
|
|
393
|
-
},
|
|
394
|
-
executeAction
|
|
395
|
-
}),
|
|
396
|
-
createReplyDouble()
|
|
397
|
-
);
|
|
398
|
-
await workspaceMemberDelete.handler(
|
|
399
|
-
createActionRequest({
|
|
400
|
-
input: {
|
|
401
|
-
params: { workspaceSlug: "acme", memberUserId: "44" }
|
|
402
|
-
},
|
|
403
|
-
executeAction
|
|
404
|
-
}),
|
|
405
|
-
createReplyDouble()
|
|
406
|
-
);
|
|
407
|
-
await workspaceInviteDelete.handler(
|
|
408
|
-
createActionRequest({
|
|
409
|
-
input: {
|
|
410
|
-
params: { workspaceSlug: "acme", inviteId: "55" }
|
|
411
|
-
},
|
|
412
|
-
executeAction
|
|
413
|
-
}),
|
|
414
|
-
createReplyDouble()
|
|
415
|
-
);
|
|
416
|
-
|
|
417
|
-
assert.deepEqual(calls[0], {
|
|
418
|
-
actionId: "workspace.workspaces.create",
|
|
419
|
-
input: { name: "Operations", slug: "operations" }
|
|
420
|
-
});
|
|
421
|
-
assert.deepEqual(calls[1].input, { payload: { token: "token-1", decision: "accept" } });
|
|
422
|
-
assert.deepEqual(calls[2].input, { workspaceSlug: "acme", memberUserId: "12", roleSid: "admin" });
|
|
423
|
-
assert.deepEqual(calls[3].input, { workspaceSlug: "acme", email: "user@example.com", roleSid: "member" });
|
|
424
|
-
assert.deepEqual(calls[4].input, { workspaceSlug: "acme", memberUserId: "44" });
|
|
425
|
-
assert.deepEqual(calls[5].input, { workspaceSlug: "acme", inviteId: "55" });
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
test("workspace settings route handlers build action input from request.input", async () => {
|
|
429
|
-
const routes = await registerRoutes();
|
|
430
|
-
const workspaceSettingsPatch = findRoute(routes, {
|
|
431
|
-
method: "PATCH",
|
|
432
|
-
path: "/api/w/:workspaceSlug/settings"
|
|
433
|
-
});
|
|
434
|
-
const calls = [];
|
|
435
|
-
const executeAction = async (payload) => {
|
|
436
|
-
calls.push(payload);
|
|
437
|
-
return {};
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
await workspaceSettingsPatch.handler(
|
|
441
|
-
createActionRequest({
|
|
442
|
-
input: {
|
|
443
|
-
params: { workspaceSlug: "acme" },
|
|
444
|
-
body: { lightPrimaryColor: "#0F6B54" }
|
|
445
|
-
},
|
|
446
|
-
executeAction
|
|
447
|
-
}),
|
|
448
|
-
createReplyDouble()
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
assert.deepEqual(calls[0], {
|
|
452
|
-
actionId: "workspace.settings.update",
|
|
453
|
-
input: { workspaceSlug: "acme", patch: { lightPrimaryColor: "#0F6B54" } }
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
|
|
457
|
-
test("workspace route handlers build action input from request.input", async () => {
|
|
458
|
-
const routes = await registerRoutes();
|
|
459
|
-
const workspacePatch = findRoute(routes, {
|
|
460
|
-
method: "PATCH",
|
|
461
|
-
path: "/api/w/:workspaceSlug"
|
|
462
|
-
});
|
|
463
|
-
const calls = [];
|
|
464
|
-
const executeAction = async (payload) => {
|
|
465
|
-
calls.push(payload);
|
|
466
|
-
return {};
|
|
467
|
-
};
|
|
468
|
-
|
|
469
|
-
await workspacePatch.handler(
|
|
470
|
-
createActionRequest({
|
|
471
|
-
input: {
|
|
472
|
-
params: { workspaceSlug: "acme" },
|
|
473
|
-
body: { name: "Acme", avatarUrl: "https://example.com/acme.png" }
|
|
474
|
-
},
|
|
475
|
-
executeAction
|
|
476
|
-
}),
|
|
477
|
-
createReplyDouble()
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
assert.deepEqual(calls[0], {
|
|
481
|
-
actionId: "workspace.workspaces.update",
|
|
482
|
-
input: {
|
|
483
|
-
workspaceSlug: "acme",
|
|
484
|
-
patch: { name: "Acme", avatarUrl: "https://example.com/acme.png" }
|
|
485
|
-
}
|
|
486
|
-
});
|
|
487
104
|
});
|
|
488
105
|
|
|
489
106
|
test("account route handlers build action input from request.input", async () => {
|
|
@@ -24,7 +24,8 @@ test("workspace action context contributor resolves workspace context for worksp
|
|
|
24
24
|
permissions: ["workspace.settings.update"]
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
workspaceSurfaceIds: ["admin", "app"]
|
|
28
29
|
});
|
|
29
30
|
|
|
30
31
|
const request = {
|
|
@@ -35,7 +36,10 @@ test("workspace action context contributor resolves workspace context for worksp
|
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
const contribution = await contributor.contribute({
|
|
38
|
-
|
|
39
|
+
definition: {
|
|
40
|
+
id: "workspace.settings.update",
|
|
41
|
+
surfaces: ["admin", "app"]
|
|
42
|
+
},
|
|
39
43
|
input: {
|
|
40
44
|
workspaceSlug: "Acme"
|
|
41
45
|
},
|
|
@@ -118,7 +122,8 @@ test("workspace action context contributor always resolves and stores resolved c
|
|
|
118
122
|
permissions: ["workspace.settings.update"]
|
|
119
123
|
};
|
|
120
124
|
}
|
|
121
|
-
}
|
|
125
|
+
},
|
|
126
|
+
workspaceSurfaceIds: ["admin", "app"]
|
|
122
127
|
});
|
|
123
128
|
|
|
124
129
|
const request = {
|
|
@@ -128,7 +133,10 @@ test("workspace action context contributor always resolves and stores resolved c
|
|
|
128
133
|
};
|
|
129
134
|
|
|
130
135
|
const contribution = await contributor.contribute({
|
|
131
|
-
|
|
136
|
+
definition: {
|
|
137
|
+
id: "workspace.members.list",
|
|
138
|
+
surfaces: ["admin", "app"]
|
|
139
|
+
},
|
|
132
140
|
input: {
|
|
133
141
|
workspaceSlug: "acme"
|
|
134
142
|
},
|
|
@@ -205,7 +213,10 @@ test("workspace action context contributor resolves context for workspace-visibl
|
|
|
205
213
|
};
|
|
206
214
|
|
|
207
215
|
const contribution = await contributor.contribute({
|
|
208
|
-
|
|
216
|
+
definition: {
|
|
217
|
+
id: "assistant.conversations.list",
|
|
218
|
+
surfaces: ["admin"]
|
|
219
|
+
},
|
|
209
220
|
input: {
|
|
210
221
|
workspaceSlug: "acme"
|
|
211
222
|
},
|
|
@@ -249,3 +260,85 @@ test("workspace action context contributor resolves context for workspace-visibl
|
|
|
249
260
|
permissions: ["assistant.chat.use"]
|
|
250
261
|
});
|
|
251
262
|
});
|
|
263
|
+
|
|
264
|
+
test("workspace action context contributor resolves context for workspace surfaces even when route visibility is public", async () => {
|
|
265
|
+
const calls = [];
|
|
266
|
+
const contributor = createWorkspaceActionContextContributor({
|
|
267
|
+
workspaceService: {
|
|
268
|
+
async resolveWorkspaceContextForUserBySlug(user, workspaceSlug, options) {
|
|
269
|
+
calls.push({ user, workspaceSlug, options });
|
|
270
|
+
return {
|
|
271
|
+
workspace: {
|
|
272
|
+
id: 77,
|
|
273
|
+
slug: "acme"
|
|
274
|
+
},
|
|
275
|
+
membership: {
|
|
276
|
+
roleSid: "member"
|
|
277
|
+
},
|
|
278
|
+
permissions: ["crud.breeds.list"]
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
workspaceSurfaceIds: ["admin", "app"]
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
const request = {
|
|
286
|
+
user: {
|
|
287
|
+
id: 42
|
|
288
|
+
},
|
|
289
|
+
routeOptions: {
|
|
290
|
+
config: {
|
|
291
|
+
surface: "admin",
|
|
292
|
+
visibility: "public"
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const contribution = await contributor.contribute({
|
|
298
|
+
definition: {
|
|
299
|
+
id: "crud.breeds.list",
|
|
300
|
+
surfaces: ["admin"]
|
|
301
|
+
},
|
|
302
|
+
input: {
|
|
303
|
+
workspaceSlug: "acme"
|
|
304
|
+
},
|
|
305
|
+
context: {
|
|
306
|
+
requestMeta: {
|
|
307
|
+
request
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
request
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
assert.deepEqual(calls, [
|
|
314
|
+
{
|
|
315
|
+
user: request.user,
|
|
316
|
+
workspaceSlug: "acme",
|
|
317
|
+
options: {
|
|
318
|
+
request
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
]);
|
|
322
|
+
assert.deepEqual(contribution, {
|
|
323
|
+
requestMeta: {
|
|
324
|
+
resolvedWorkspaceContext: {
|
|
325
|
+
workspace: {
|
|
326
|
+
id: 77,
|
|
327
|
+
slug: "acme"
|
|
328
|
+
},
|
|
329
|
+
membership: {
|
|
330
|
+
roleSid: "member"
|
|
331
|
+
},
|
|
332
|
+
permissions: ["crud.breeds.list"]
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
workspace: {
|
|
336
|
+
id: 77,
|
|
337
|
+
slug: "acme"
|
|
338
|
+
},
|
|
339
|
+
membership: {
|
|
340
|
+
roleSid: "member"
|
|
341
|
+
},
|
|
342
|
+
permissions: ["crud.breeds.list"]
|
|
343
|
+
});
|
|
344
|
+
});
|