@jskit-ai/users-core 0.1.48 → 0.1.50
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 +7 -7
- package/package.json +7 -17
- package/src/server/common/services/authProfileSyncService.js +28 -7
- package/src/server/common/support/realtimeServiceEvents.js +1 -59
- package/src/server/profileSyncLifecycleContributorRegistry.js +56 -0
- package/src/server/registerUsersBootstrap.js +0 -1
- package/src/server/registerUsersCore.js +2 -14
- package/src/server/usersBootstrapContributor.js +2 -64
- package/src/shared/index.js +2 -99
- package/src/shared/settings.js +1 -119
- package/test/authProfileSyncService.test.js +19 -10
- package/test/registerServiceRealtimeEvents.test.js +0 -86
- package/test/registerUsersCore.test.js +6 -15
- package/test/repositoryContracts.test.js +1 -9
- package/test/resourcesCanonical.test.js +0 -16
- package/test/settingsFieldRegistriesSingleton.test.js +0 -5
- package/test/usersBootstrapContributor.test.js +2 -26
- package/test/usersRouteResources.test.js +0 -16
- package/src/server/UsersWorkspacesServiceProvider.js +0 -44
- package/src/server/common/contributors/workspaceActionContextContributor.js +0 -88
- package/src/server/common/contributors/workspaceAuthPolicyContextResolver.js +0 -34
- package/src/server/common/contributors/workspaceRouteVisibilityResolver.js +0 -78
- package/src/server/common/formatters/workspaceFormatter.js +0 -53
- package/src/server/common/repositories/workspaceInvitesRepository.js +0 -208
- package/src/server/common/repositories/workspaceMembershipsRepository.js +0 -190
- package/src/server/common/repositories/workspacesRepository.js +0 -202
- package/src/server/common/services/workspaceContextService.js +0 -281
- package/src/server/common/support/workspaceRoutePaths.js +0 -17
- package/src/server/common/validators/routeParamsValidator.js +0 -62
- package/src/server/registerWorkspaceBootstrap.js +0 -27
- package/src/server/registerWorkspaceCore.js +0 -73
- package/src/server/registerWorkspaceRepositories.js +0 -26
- package/src/server/support/resolveWorkspace.js +0 -16
- package/src/server/support/workspaceActionSurfaces.js +0 -118
- package/src/server/support/workspaceInvitationsPolicy.js +0 -45
- package/src/server/support/workspaceRouteInput.js +0 -22
- package/src/server/workspaceBootstrapContributor.js +0 -212
- package/src/server/workspaceDirectory/bootWorkspaceDirectoryRoutes.js +0 -133
- package/src/server/workspaceDirectory/registerWorkspaceDirectory.js +0 -19
- package/src/server/workspaceDirectory/workspaceDirectoryActions.js +0 -133
- package/src/server/workspaceMembers/bootWorkspaceMembers.js +0 -236
- package/src/server/workspaceMembers/registerWorkspaceMembers.js +0 -108
- package/src/server/workspaceMembers/workspaceMembersActions.js +0 -186
- package/src/server/workspaceMembers/workspaceMembersService.js +0 -222
- package/src/server/workspacePendingInvitations/bootWorkspacePendingInvitations.js +0 -62
- package/src/server/workspacePendingInvitations/registerWorkspacePendingInvitations.js +0 -119
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsActions.js +0 -74
- package/src/server/workspacePendingInvitations/workspacePendingInvitationsService.js +0 -138
- package/src/server/workspaceSettings/bootWorkspaceSettings.js +0 -76
- package/src/server/workspaceSettings/registerWorkspaceSettings.js +0 -62
- package/src/server/workspaceSettings/workspaceSettingsActions.js +0 -72
- package/src/server/workspaceSettings/workspaceSettingsRepository.js +0 -154
- package/src/server/workspaceSettings/workspaceSettingsService.js +0 -66
- package/src/shared/resources/workspaceMembersResource.js +0 -354
- package/src/shared/resources/workspacePendingInvitationsResource.js +0 -82
- package/src/shared/resources/workspaceResource.js +0 -176
- package/src/shared/resources/workspaceSettingsFields.js +0 -59
- package/src/shared/resources/workspaceSettingsResource.js +0 -169
- package/src/shared/roles.js +0 -161
- package/src/shared/support/usersApiPaths.js +0 -43
- package/src/shared/support/usersVisibility.js +0 -42
- package/src/shared/support/workspacePathModel.js +0 -145
- package/src/shared/tenancyMode.js +0 -35
- package/src/shared/tenancyProfile.js +0 -73
- package/test/registerWorkspaceDirectory.test.js +0 -31
- package/test/registerWorkspaceSettings.test.js +0 -40
- package/test/roles.test.js +0 -159
- package/test/tenancyProfile.test.js +0 -67
- package/test/usersApiPaths.test.js +0 -49
- package/test/usersRouteValidators.test.js +0 -49
- package/test/usersVisibility.test.js +0 -27
- package/test/workspaceActionContextContributor.test.js +0 -344
- package/test/workspaceActionSurfaces.test.js +0 -85
- package/test/workspaceAuthPolicyContextResolver.test.js +0 -119
- package/test/workspaceBootstrapContributor.test.js +0 -154
- package/test/workspaceInvitationsPolicy.test.js +0 -71
- package/test/workspaceInvitesRepository.test.js +0 -111
- package/test/workspaceMembersService.test.js +0 -398
- package/test/workspacePathModel.test.js +0 -93
- package/test/workspacePendingInvitationsResource.test.js +0 -38
- package/test/workspacePendingInvitationsService.test.js +0 -151
- package/test/workspaceRouteVisibilityResolver.test.js +0 -83
- package/test/workspaceService.test.js +0 -546
- package/test/workspaceSettingsActions.test.js +0 -52
- package/test/workspaceSettingsRepository.test.js +0 -202
- package/test/workspaceSettingsResource.test.js +0 -169
- package/test/workspaceSettingsService.test.js +0 -140
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
2
|
-
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
3
|
-
import { deepFreeze } from "../common/support/deepFreeze.js";
|
|
4
|
-
import { createRepository as createWorkspaceSettingsRepository } from "./workspaceSettingsRepository.js";
|
|
5
|
-
import { createService as createWorkspaceSettingsService } from "./workspaceSettingsService.js";
|
|
6
|
-
import { workspaceSettingsActions } from "./workspaceSettingsActions.js";
|
|
7
|
-
import { createWorkspaceRoleCatalog } from "../../shared/roles.js";
|
|
8
|
-
import { createWorkspaceEntityAndBootstrapEvents } from "../common/support/realtimeServiceEvents.js";
|
|
9
|
-
|
|
10
|
-
function resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig = {}) {
|
|
11
|
-
const defaultInvitesEnabled = appConfig?.workspaceSettings?.defaults?.invitesEnabled;
|
|
12
|
-
|
|
13
|
-
if (typeof defaultInvitesEnabled !== "boolean") {
|
|
14
|
-
throw new TypeError("users.core requires appConfig.workspaceSettings.defaults.invitesEnabled.");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return defaultInvitesEnabled;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function registerWorkspaceSettings(app) {
|
|
21
|
-
if (!app || typeof app.singleton !== "function" || typeof app.actions !== "function" || typeof app.service !== "function") {
|
|
22
|
-
throw new Error("registerWorkspaceSettings requires application singleton()/service()/actions().");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
app.singleton("workspaceSettingsRepository", (scope) => {
|
|
26
|
-
const knex = scope.make("jskit.database.knex");
|
|
27
|
-
const appConfig = resolveAppConfig(scope);
|
|
28
|
-
return createWorkspaceSettingsRepository(knex, {
|
|
29
|
-
defaultInvitesEnabled: resolveWorkspaceSettingsDefaultInvitesEnabled(appConfig)
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
app.service(
|
|
34
|
-
"users.workspace.settings.service",
|
|
35
|
-
(scope) =>
|
|
36
|
-
createWorkspaceSettingsService({
|
|
37
|
-
workspaceSettingsRepository: scope.make("workspaceSettingsRepository"),
|
|
38
|
-
workspaceInvitationsEnabled: scope.make("users.workspace.invitations.enabled"),
|
|
39
|
-
roleCatalog: createWorkspaceRoleCatalog(resolveAppConfig(scope))
|
|
40
|
-
}),
|
|
41
|
-
{
|
|
42
|
-
events: deepFreeze({
|
|
43
|
-
updateWorkspaceSettings: createWorkspaceEntityAndBootstrapEvents({
|
|
44
|
-
workspaceEntity: "settings",
|
|
45
|
-
workspaceOperation: "updated",
|
|
46
|
-
workspaceRealtimeEvent: "workspace.settings.changed"
|
|
47
|
-
})
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
app.actions(
|
|
53
|
-
withActionDefaults(workspaceSettingsActions, {
|
|
54
|
-
domain: "workspace",
|
|
55
|
-
dependencies: {
|
|
56
|
-
workspaceSettingsService: "users.workspace.settings.service"
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export { registerWorkspaceSettings };
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { workspaceSettingsResource } from "../../shared/resources/workspaceSettingsResource.js";
|
|
2
|
-
import { workspaceSlugParamsValidator } from "../common/validators/routeParamsValidator.js";
|
|
3
|
-
import { resolveWorkspace } from "../support/resolveWorkspace.js";
|
|
4
|
-
|
|
5
|
-
const workspaceSettingsActions = Object.freeze([
|
|
6
|
-
{
|
|
7
|
-
id: "workspace.settings.read",
|
|
8
|
-
version: 1,
|
|
9
|
-
kind: "query",
|
|
10
|
-
channels: ["api", "automation", "internal"],
|
|
11
|
-
surfacesFrom: "workspace",
|
|
12
|
-
permission: {
|
|
13
|
-
require: "any",
|
|
14
|
-
permissions: ["workspace.settings.view", "workspace.settings.update"]
|
|
15
|
-
},
|
|
16
|
-
inputValidator: workspaceSlugParamsValidator,
|
|
17
|
-
outputValidator: workspaceSettingsResource.operations.view.outputValidator,
|
|
18
|
-
idempotency: "none",
|
|
19
|
-
audit: {
|
|
20
|
-
actionName: "workspace.settings.read"
|
|
21
|
-
},
|
|
22
|
-
observability: {},
|
|
23
|
-
async execute(input, context, deps) {
|
|
24
|
-
const response = await deps.workspaceSettingsService.getWorkspaceSettings(resolveWorkspace(context, input), {
|
|
25
|
-
context
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
return response;
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: "workspace.settings.update",
|
|
33
|
-
version: 1,
|
|
34
|
-
kind: "command",
|
|
35
|
-
channels: ["api", "assistant_tool", "automation", "internal"],
|
|
36
|
-
surfacesFrom: "workspace",
|
|
37
|
-
permission: {
|
|
38
|
-
require: "all",
|
|
39
|
-
permissions: ["workspace.settings.update"]
|
|
40
|
-
},
|
|
41
|
-
inputValidator: [
|
|
42
|
-
workspaceSlugParamsValidator,
|
|
43
|
-
{
|
|
44
|
-
patch: workspaceSettingsResource.operations.patch.bodyValidator
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
outputValidator: workspaceSettingsResource.operations.patch.outputValidator,
|
|
48
|
-
idempotency: "optional",
|
|
49
|
-
audit: {
|
|
50
|
-
actionName: "workspace.settings.update"
|
|
51
|
-
},
|
|
52
|
-
observability: {},
|
|
53
|
-
extensions: {
|
|
54
|
-
assistant: {
|
|
55
|
-
description: "Update workspace settings."
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
async execute(input, context, deps) {
|
|
59
|
-
const response = await deps.workspaceSettingsService.updateWorkspaceSettings(
|
|
60
|
-
resolveWorkspace(context, input),
|
|
61
|
-
input.patch,
|
|
62
|
-
{
|
|
63
|
-
context
|
|
64
|
-
}
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
return response;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
export { workspaceSettingsActions };
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
normalizeDbRecordId,
|
|
3
|
-
normalizeRecordId,
|
|
4
|
-
toIsoString,
|
|
5
|
-
nowDb,
|
|
6
|
-
isDuplicateEntryError,
|
|
7
|
-
createWithTransaction
|
|
8
|
-
} from "../common/repositories/repositoryUtils.js";
|
|
9
|
-
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
|
|
10
|
-
import { pickOwnProperties } from "@jskit-ai/kernel/shared/support";
|
|
11
|
-
import {
|
|
12
|
-
workspaceSettingsFields,
|
|
13
|
-
resolveWorkspaceSettingsFieldKeys
|
|
14
|
-
} from "../../shared/resources/workspaceSettingsFields.js";
|
|
15
|
-
|
|
16
|
-
function resolveWorkspaceSettingsSeed(workspace = {}, { defaultInvitesEnabled = true } = {}) {
|
|
17
|
-
const source = normalizeObjectInput(workspace);
|
|
18
|
-
const seed = {};
|
|
19
|
-
for (const field of workspaceSettingsFields) {
|
|
20
|
-
const rawValue = Object.hasOwn(source, field.key)
|
|
21
|
-
? source[field.key]
|
|
22
|
-
: field.resolveDefault({
|
|
23
|
-
workspace: source,
|
|
24
|
-
defaultInvitesEnabled
|
|
25
|
-
});
|
|
26
|
-
seed[field.key] = field.normalizeOutput(rawValue, {
|
|
27
|
-
workspace: source,
|
|
28
|
-
defaultInvitesEnabled
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
return seed;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function createRepository(knex, { defaultInvitesEnabled } = {}) {
|
|
35
|
-
if (typeof knex !== "function") {
|
|
36
|
-
throw new TypeError("workspaceSettingsRepository requires knex.");
|
|
37
|
-
}
|
|
38
|
-
const withTransaction = createWithTransaction(knex);
|
|
39
|
-
|
|
40
|
-
function mapRow(row) {
|
|
41
|
-
if (!row) {
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const settings = {
|
|
46
|
-
workspaceId: normalizeDbRecordId(row.workspace_id, { fallback: "" })
|
|
47
|
-
};
|
|
48
|
-
for (const field of workspaceSettingsFields) {
|
|
49
|
-
const rawValue = Object.hasOwn(row, field.dbColumn)
|
|
50
|
-
? row[field.dbColumn]
|
|
51
|
-
: field.resolveDefault({
|
|
52
|
-
defaultInvitesEnabled
|
|
53
|
-
});
|
|
54
|
-
settings[field.key] = field.normalizeOutput(rawValue, {
|
|
55
|
-
defaultInvitesEnabled
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
settings.createdAt = toIsoString(row.created_at);
|
|
60
|
-
settings.updatedAt = toIsoString(row.updated_at);
|
|
61
|
-
return settings;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function findByWorkspaceId(workspaceId, options = {}) {
|
|
65
|
-
const client = options?.trx || knex;
|
|
66
|
-
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
67
|
-
if (!normalizedWorkspaceId) {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const row = await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).first();
|
|
72
|
-
return mapRow(row);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async function ensureForWorkspaceId(workspaceId, options = {}) {
|
|
76
|
-
const client = options?.trx || knex;
|
|
77
|
-
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
78
|
-
if (!normalizedWorkspaceId) {
|
|
79
|
-
throw new TypeError("workspaceSettingsRepository.ensureForWorkspaceId requires a valid workspace id.");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const seed = resolveWorkspaceSettingsSeed(options?.workspace, {
|
|
83
|
-
defaultInvitesEnabled
|
|
84
|
-
});
|
|
85
|
-
const existing = await findByWorkspaceId(normalizedWorkspaceId, { trx: client });
|
|
86
|
-
if (existing) {
|
|
87
|
-
return existing;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const insertPayload = {
|
|
92
|
-
workspace_id: normalizedWorkspaceId,
|
|
93
|
-
created_at: nowDb(),
|
|
94
|
-
updated_at: nowDb()
|
|
95
|
-
};
|
|
96
|
-
for (const field of workspaceSettingsFields) {
|
|
97
|
-
insertPayload[field.dbColumn] = seed[field.key];
|
|
98
|
-
}
|
|
99
|
-
await client("workspace_settings").insert(insertPayload);
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (!isDuplicateEntryError(error)) {
|
|
102
|
-
throw error;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async function updateSettingsByWorkspaceId(workspaceId, patch = {}, options = {}) {
|
|
110
|
-
const client = options?.trx || knex;
|
|
111
|
-
const normalizedWorkspaceId = normalizeRecordId(workspaceId, { fallback: null });
|
|
112
|
-
if (!normalizedWorkspaceId) {
|
|
113
|
-
throw new TypeError("workspaceSettingsRepository.updateSettingsByWorkspaceId requires a valid workspace id.");
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const ensured = await ensureForWorkspaceId(normalizedWorkspaceId, {
|
|
117
|
-
trx: client,
|
|
118
|
-
workspace: options?.workspace
|
|
119
|
-
});
|
|
120
|
-
const source = normalizeObjectInput(patch);
|
|
121
|
-
const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
|
|
122
|
-
|
|
123
|
-
if (Object.keys(settingsPatch).length === 0) {
|
|
124
|
-
return ensured;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const dbPatch = {
|
|
128
|
-
updated_at: nowDb()
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
for (const field of workspaceSettingsFields) {
|
|
132
|
-
if (!Object.hasOwn(settingsPatch, field.key)) {
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
dbPatch[field.dbColumn] = field.normalizeInput(settingsPatch[field.key], {
|
|
136
|
-
payload: source
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
await client("workspace_settings").where({ workspace_id: normalizedWorkspaceId }).update({
|
|
141
|
-
...dbPatch
|
|
142
|
-
});
|
|
143
|
-
return findByWorkspaceId(normalizedWorkspaceId, { trx: client });
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return Object.freeze({
|
|
147
|
-
withTransaction,
|
|
148
|
-
findByWorkspaceId,
|
|
149
|
-
ensureForWorkspaceId,
|
|
150
|
-
updateSettingsByWorkspaceId
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export { createRepository };
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
|
-
import { normalizeObjectInput } from "@jskit-ai/kernel/shared/validators/inputNormalization";
|
|
3
|
-
import { pickOwnProperties } from "@jskit-ai/kernel/shared/support";
|
|
4
|
-
import {
|
|
5
|
-
workspaceSettingsFields,
|
|
6
|
-
resolveWorkspaceSettingsFieldKeys
|
|
7
|
-
} from "../../shared/resources/workspaceSettingsFields.js";
|
|
8
|
-
import { createWorkspaceRoleCatalog, cloneWorkspaceRoleCatalog } from "../../shared/roles.js";
|
|
9
|
-
|
|
10
|
-
function createService({
|
|
11
|
-
workspaceSettingsRepository,
|
|
12
|
-
workspaceInvitationsEnabled = true,
|
|
13
|
-
roleCatalog = null
|
|
14
|
-
} = {}) {
|
|
15
|
-
if (!workspaceSettingsRepository) {
|
|
16
|
-
throw new Error("workspaceSettingsService requires workspaceSettingsRepository.");
|
|
17
|
-
}
|
|
18
|
-
const resolvedRoleCatalog = roleCatalog && typeof roleCatalog === "object" ? roleCatalog : createWorkspaceRoleCatalog();
|
|
19
|
-
const invitesAvailable = workspaceInvitationsEnabled === true;
|
|
20
|
-
|
|
21
|
-
async function getWorkspaceSettings(workspace, options = {}) {
|
|
22
|
-
const settingsRecord = await workspaceSettingsRepository.ensureForWorkspaceId(workspace.id, {
|
|
23
|
-
...options,
|
|
24
|
-
workspace
|
|
25
|
-
});
|
|
26
|
-
const settings = {};
|
|
27
|
-
for (const field of workspaceSettingsFields) {
|
|
28
|
-
settings[field.key] = settingsRecord[field.key];
|
|
29
|
-
}
|
|
30
|
-
const invitesEnabled = invitesAvailable && settings.invitesEnabled !== false;
|
|
31
|
-
settings.invitesEnabled = invitesEnabled;
|
|
32
|
-
settings.invitesAvailable = invitesAvailable;
|
|
33
|
-
settings.invitesEffective = invitesAvailable && invitesEnabled;
|
|
34
|
-
|
|
35
|
-
return {
|
|
36
|
-
workspace: {
|
|
37
|
-
id: normalizeRecordId(workspace.id, { fallback: "" }),
|
|
38
|
-
slug: String(workspace.slug || ""),
|
|
39
|
-
ownerUserId: normalizeRecordId(workspace.ownerUserId, { fallback: "" })
|
|
40
|
-
},
|
|
41
|
-
settings,
|
|
42
|
-
roleCatalog: cloneWorkspaceRoleCatalog(resolvedRoleCatalog)
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async function updateWorkspaceSettings(workspace, payload = {}, options = {}) {
|
|
47
|
-
const source = normalizeObjectInput(payload);
|
|
48
|
-
const settingsPatch = pickOwnProperties(source, resolveWorkspaceSettingsFieldKeys());
|
|
49
|
-
|
|
50
|
-
if (Object.keys(settingsPatch).length > 0) {
|
|
51
|
-
await workspaceSettingsRepository.updateSettingsByWorkspaceId(workspace.id, settingsPatch, {
|
|
52
|
-
...options,
|
|
53
|
-
workspace
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return getWorkspaceSettings(workspace, options);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return Object.freeze({
|
|
61
|
-
getWorkspaceSettings,
|
|
62
|
-
updateWorkspaceSettings
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { createService };
|
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
import { Type } from "@fastify/type-provider-typebox";
|
|
2
|
-
import { normalizeLowerText, normalizeText } from "@jskit-ai/kernel/shared/actions/textNormalization";
|
|
3
|
-
import {
|
|
4
|
-
normalizeObjectInput,
|
|
5
|
-
recordIdSchema,
|
|
6
|
-
recordIdInputSchema,
|
|
7
|
-
nullableRecordIdSchema
|
|
8
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
9
|
-
import { normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
10
|
-
import { createOperationMessages } from "../operationMessages.js";
|
|
11
|
-
import { createWorkspaceRoleCatalog, OWNER_ROLE_ID } from "../roles.js";
|
|
12
|
-
|
|
13
|
-
const workspaceSummaryOutputSchema = Type.Object(
|
|
14
|
-
{
|
|
15
|
-
id: recordIdSchema,
|
|
16
|
-
slug: Type.String({ minLength: 1 }),
|
|
17
|
-
name: Type.String({ minLength: 1 }),
|
|
18
|
-
ownerUserId: recordIdSchema,
|
|
19
|
-
avatarUrl: Type.String()
|
|
20
|
-
},
|
|
21
|
-
{ additionalProperties: false }
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
const memberSummaryOutputSchema = Type.Object(
|
|
25
|
-
{
|
|
26
|
-
userId: recordIdSchema,
|
|
27
|
-
roleSid: Type.String({ minLength: 1 }),
|
|
28
|
-
status: Type.String({ minLength: 1 }),
|
|
29
|
-
displayName: Type.String(),
|
|
30
|
-
email: Type.String({ minLength: 1 }),
|
|
31
|
-
isOwner: Type.Boolean()
|
|
32
|
-
},
|
|
33
|
-
{ additionalProperties: false }
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
const inviteSummaryOutputSchema = Type.Object(
|
|
37
|
-
{
|
|
38
|
-
id: recordIdSchema,
|
|
39
|
-
email: Type.String({ minLength: 3, format: "email" }),
|
|
40
|
-
roleSid: Type.String({ minLength: 1 }),
|
|
41
|
-
status: Type.String({ minLength: 1 }),
|
|
42
|
-
expiresAt: Type.String({ minLength: 1 }),
|
|
43
|
-
invitedByUserId: nullableRecordIdSchema
|
|
44
|
-
},
|
|
45
|
-
{ additionalProperties: false }
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
function normalizeWorkspaceAdminSummary(workspace) {
|
|
49
|
-
const source = normalizeObjectInput(workspace);
|
|
50
|
-
|
|
51
|
-
return {
|
|
52
|
-
id: normalizeRecordId(source.id, { fallback: "" }),
|
|
53
|
-
slug: normalizeText(source.slug),
|
|
54
|
-
name: normalizeText(source.name),
|
|
55
|
-
ownerUserId: normalizeRecordId(source.ownerUserId, { fallback: "" }),
|
|
56
|
-
avatarUrl: normalizeText(source.avatarUrl)
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function normalizeMemberSummary(member, workspace) {
|
|
61
|
-
const source = normalizeObjectInput(member);
|
|
62
|
-
const userId = normalizeRecordId(source.userId, { fallback: "" });
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
userId,
|
|
66
|
-
roleSid: normalizeLowerText(source.roleSid || "member") || "member",
|
|
67
|
-
status: normalizeLowerText(source.status || "active") || "active",
|
|
68
|
-
displayName: normalizeText(source.displayName),
|
|
69
|
-
email: normalizeLowerText(source.email),
|
|
70
|
-
isOwner: userId === workspace.ownerUserId || normalizeLowerText(source.roleSid) === OWNER_ROLE_ID
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function normalizeInviteSummary(invite) {
|
|
75
|
-
const source = normalizeObjectInput(invite);
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
id: normalizeRecordId(source.id, { fallback: "" }),
|
|
79
|
-
email: normalizeLowerText(source.email),
|
|
80
|
-
roleSid: normalizeLowerText(source.roleSid || "member") || "member",
|
|
81
|
-
status: normalizeLowerText(source.status || "pending") || "pending",
|
|
82
|
-
expiresAt: source.expiresAt,
|
|
83
|
-
invitedByUserId: source.invitedByUserId == null ? null : normalizeRecordId(source.invitedByUserId, { fallback: null })
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function normalizeWorkspaceOutputEnvelope(
|
|
88
|
-
payload = {},
|
|
89
|
-
{ itemsKey, normalizeItem, includeInviteTokenPreview = false } = {}
|
|
90
|
-
) {
|
|
91
|
-
const source = normalizeObjectInput(payload);
|
|
92
|
-
const workspace = normalizeWorkspaceAdminSummary(source.workspace);
|
|
93
|
-
const items = Array.isArray(source[itemsKey]) ? source[itemsKey] : [];
|
|
94
|
-
const roleCatalog = normalizeObjectInput(source.roleCatalog);
|
|
95
|
-
const hasRoleCatalog =
|
|
96
|
-
Array.isArray(roleCatalog.roles) &&
|
|
97
|
-
roleCatalog.roles.length > 0 &&
|
|
98
|
-
Array.isArray(roleCatalog.assignableRoleIds);
|
|
99
|
-
const normalized = {
|
|
100
|
-
workspace,
|
|
101
|
-
[itemsKey]: items.map((item) => normalizeItem(item, workspace)),
|
|
102
|
-
roleCatalog: hasRoleCatalog ? roleCatalog : createWorkspaceRoleCatalog()
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
if (includeInviteTokenPreview && Object.hasOwn(source, "inviteTokenPreview")) {
|
|
106
|
-
normalized.inviteTokenPreview = normalizeText(source.inviteTokenPreview);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return normalized;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function normalizeWorkspaceMembersOutput(payload = {}) {
|
|
113
|
-
return normalizeWorkspaceOutputEnvelope(payload, {
|
|
114
|
-
itemsKey: "members",
|
|
115
|
-
normalizeItem: normalizeMemberSummary
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function normalizeWorkspaceInvitesOutput(payload = {}) {
|
|
120
|
-
return normalizeWorkspaceOutputEnvelope(payload, {
|
|
121
|
-
itemsKey: "invites",
|
|
122
|
-
normalizeItem: normalizeInviteSummary,
|
|
123
|
-
includeInviteTokenPreview: true
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const workspaceRoleCatalogOutputValidator = Object.freeze({
|
|
128
|
-
schema: Type.Object(
|
|
129
|
-
{
|
|
130
|
-
collaborationEnabled: Type.Boolean(),
|
|
131
|
-
defaultInviteRole: Type.String(),
|
|
132
|
-
roles: Type.Array(Type.Object({}, { additionalProperties: true })),
|
|
133
|
-
assignableRoleIds: Type.Array(Type.String({ minLength: 1 }))
|
|
134
|
-
},
|
|
135
|
-
{ additionalProperties: true }
|
|
136
|
-
)
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const workspaceMembersOutputValidator = Object.freeze({
|
|
140
|
-
schema: Type.Object(
|
|
141
|
-
{
|
|
142
|
-
workspace: workspaceSummaryOutputSchema,
|
|
143
|
-
members: Type.Array(memberSummaryOutputSchema),
|
|
144
|
-
roleCatalog: workspaceRoleCatalogOutputValidator.schema
|
|
145
|
-
},
|
|
146
|
-
{ additionalProperties: false }
|
|
147
|
-
),
|
|
148
|
-
normalize: normalizeWorkspaceMembersOutput
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
const workspaceInvitesOutputValidator = Object.freeze({
|
|
152
|
-
schema: Type.Object(
|
|
153
|
-
{
|
|
154
|
-
workspace: workspaceSummaryOutputSchema,
|
|
155
|
-
invites: Type.Array(inviteSummaryOutputSchema),
|
|
156
|
-
roleCatalog: workspaceRoleCatalogOutputValidator.schema,
|
|
157
|
-
inviteTokenPreview: Type.Optional(Type.String({ minLength: 1 }))
|
|
158
|
-
},
|
|
159
|
-
{ additionalProperties: false }
|
|
160
|
-
),
|
|
161
|
-
normalize: normalizeWorkspaceInvitesOutput
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
const updateMemberRoleBodyValidator = Object.freeze({
|
|
165
|
-
schema: Type.Object(
|
|
166
|
-
{
|
|
167
|
-
roleSid: Type.String({ minLength: 1 })
|
|
168
|
-
},
|
|
169
|
-
{ additionalProperties: false }
|
|
170
|
-
),
|
|
171
|
-
normalize(payload = {}) {
|
|
172
|
-
const source = normalizeObjectInput(payload);
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
roleSid: normalizeLowerText(source.roleSid)
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const updateMemberRoleInputValidator = Object.freeze({
|
|
181
|
-
schema: Type.Object(
|
|
182
|
-
{
|
|
183
|
-
memberUserId: recordIdInputSchema,
|
|
184
|
-
roleSid: Type.String({ minLength: 1 })
|
|
185
|
-
},
|
|
186
|
-
{ additionalProperties: false }
|
|
187
|
-
),
|
|
188
|
-
normalize(payload = {}) {
|
|
189
|
-
const source = normalizeObjectInput(payload);
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
memberUserId: normalizeRecordId(source.memberUserId, { fallback: "" }),
|
|
193
|
-
roleSid: normalizeLowerText(source.roleSid)
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
const removeMemberInputValidator = Object.freeze({
|
|
199
|
-
schema: Type.Object(
|
|
200
|
-
{
|
|
201
|
-
memberUserId: recordIdInputSchema
|
|
202
|
-
},
|
|
203
|
-
{ additionalProperties: false }
|
|
204
|
-
),
|
|
205
|
-
normalize(payload = {}) {
|
|
206
|
-
const source = normalizeObjectInput(payload);
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
memberUserId: normalizeRecordId(source.memberUserId, { fallback: "" })
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const createInviteBodyValidator = Object.freeze({
|
|
215
|
-
schema: Type.Object(
|
|
216
|
-
{
|
|
217
|
-
email: Type.String({ minLength: 3, format: "email" }),
|
|
218
|
-
roleSid: Type.String({ minLength: 1 })
|
|
219
|
-
},
|
|
220
|
-
{ additionalProperties: false }
|
|
221
|
-
),
|
|
222
|
-
normalize(payload = {}) {
|
|
223
|
-
const source = normalizeObjectInput(payload);
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
email: normalizeLowerText(source.email),
|
|
227
|
-
roleSid: normalizeLowerText(source.roleSid || "member") || "member"
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
const revokeInviteInputValidator = Object.freeze({
|
|
233
|
-
schema: Type.Object(
|
|
234
|
-
{
|
|
235
|
-
inviteId: recordIdInputSchema
|
|
236
|
-
},
|
|
237
|
-
{ additionalProperties: false }
|
|
238
|
-
),
|
|
239
|
-
normalize(payload = {}) {
|
|
240
|
-
const source = normalizeObjectInput(payload);
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
inviteId: normalizeRecordId(source.inviteId, { fallback: "" })
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
const redeemInviteBodyValidator = Object.freeze({
|
|
249
|
-
schema: Type.Object(
|
|
250
|
-
{
|
|
251
|
-
token: Type.String({
|
|
252
|
-
minLength: 1,
|
|
253
|
-
messages: {
|
|
254
|
-
required: "Invite token is required.",
|
|
255
|
-
minLength: "Invite token is required.",
|
|
256
|
-
default: "Invite token is invalid."
|
|
257
|
-
}
|
|
258
|
-
}),
|
|
259
|
-
decision: Type.Union([Type.Literal("accept"), Type.Literal("refuse")], {
|
|
260
|
-
messages: {
|
|
261
|
-
required: "Decision is required.",
|
|
262
|
-
default: "Decision must be accept or refuse."
|
|
263
|
-
}
|
|
264
|
-
})
|
|
265
|
-
},
|
|
266
|
-
{
|
|
267
|
-
additionalProperties: false,
|
|
268
|
-
messages: {
|
|
269
|
-
additionalProperties: "Unexpected field."
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
),
|
|
273
|
-
normalize(payload = {}) {
|
|
274
|
-
const source = normalizeObjectInput(payload);
|
|
275
|
-
|
|
276
|
-
return {
|
|
277
|
-
token: normalizeText(source.token),
|
|
278
|
-
decision: normalizeLowerText(source.decision)
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
const redeemInviteOutputValidator = Object.freeze({
|
|
284
|
-
schema: Type.Object(
|
|
285
|
-
{
|
|
286
|
-
decision: Type.Union([Type.Literal("accepted"), Type.Literal("refused")])
|
|
287
|
-
},
|
|
288
|
-
{ additionalProperties: false }
|
|
289
|
-
),
|
|
290
|
-
normalize(payload = {}) {
|
|
291
|
-
const source = normalizeObjectInput(payload);
|
|
292
|
-
|
|
293
|
-
return {
|
|
294
|
-
decision: normalizeLowerText(source.decision)
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
|
|
299
|
-
const WORKSPACE_MEMBERS_MESSAGES = createOperationMessages();
|
|
300
|
-
|
|
301
|
-
const workspaceMembersResource = Object.freeze({
|
|
302
|
-
resource: "workspaceMembers",
|
|
303
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
304
|
-
operations: Object.freeze({
|
|
305
|
-
rolesList: Object.freeze({
|
|
306
|
-
method: "GET",
|
|
307
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
308
|
-
outputValidator: workspaceRoleCatalogOutputValidator
|
|
309
|
-
}),
|
|
310
|
-
membersList: Object.freeze({
|
|
311
|
-
method: "GET",
|
|
312
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
313
|
-
outputValidator: workspaceMembersOutputValidator
|
|
314
|
-
}),
|
|
315
|
-
updateMemberRole: Object.freeze({
|
|
316
|
-
method: "PATCH",
|
|
317
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
318
|
-
bodyValidator: updateMemberRoleBodyValidator,
|
|
319
|
-
inputValidator: updateMemberRoleInputValidator,
|
|
320
|
-
outputValidator: workspaceMembersOutputValidator
|
|
321
|
-
}),
|
|
322
|
-
removeMember: Object.freeze({
|
|
323
|
-
method: "DELETE",
|
|
324
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
325
|
-
inputValidator: removeMemberInputValidator,
|
|
326
|
-
outputValidator: workspaceMembersOutputValidator
|
|
327
|
-
}),
|
|
328
|
-
invitesList: Object.freeze({
|
|
329
|
-
method: "GET",
|
|
330
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
331
|
-
outputValidator: workspaceInvitesOutputValidator
|
|
332
|
-
}),
|
|
333
|
-
createInvite: Object.freeze({
|
|
334
|
-
method: "POST",
|
|
335
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
336
|
-
bodyValidator: createInviteBodyValidator,
|
|
337
|
-
outputValidator: workspaceInvitesOutputValidator
|
|
338
|
-
}),
|
|
339
|
-
revokeInvite: Object.freeze({
|
|
340
|
-
method: "DELETE",
|
|
341
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
342
|
-
inputValidator: revokeInviteInputValidator,
|
|
343
|
-
outputValidator: workspaceInvitesOutputValidator
|
|
344
|
-
}),
|
|
345
|
-
redeemInvite: Object.freeze({
|
|
346
|
-
method: "POST",
|
|
347
|
-
messages: WORKSPACE_MEMBERS_MESSAGES,
|
|
348
|
-
bodyValidator: redeemInviteBodyValidator,
|
|
349
|
-
outputValidator: redeemInviteOutputValidator
|
|
350
|
-
})
|
|
351
|
-
})
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
export { workspaceMembersResource };
|