@jskit-ai/users-core 0.1.65 → 0.1.66
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 +14 -65
- package/package.json +10 -10
- package/src/server/UsersCoreServiceProvider.js +18 -2
- package/src/server/accountNotifications/accountNotificationsActions.js +3 -5
- package/src/server/accountNotifications/accountNotificationsService.js +3 -2
- package/src/server/accountNotifications/bootAccountNotificationsRoutes.js +12 -11
- package/src/server/accountPreferences/accountPreferencesActions.js +3 -5
- package/src/server/accountPreferences/accountPreferencesService.js +3 -2
- package/src/server/accountPreferences/bootAccountPreferencesRoutes.js +12 -11
- package/src/server/accountProfile/accountProfileActions.js +15 -32
- package/src/server/accountProfile/accountProfileService.js +9 -8
- package/src/server/accountProfile/bootAccountProfileRoutes.js +25 -19
- package/src/server/accountSecurity/accountSecurityActions.js +21 -16
- package/src/server/accountSecurity/accountSecurityService.js +16 -6
- package/src/server/accountSecurity/bootAccountSecurityRoutes.js +52 -40
- package/src/server/common/formatters/accountSettingsResponseFormatter.js +8 -18
- package/src/server/common/registerCommonRepositories.js +5 -2
- package/src/server/common/repositories/userProfilesRepository.js +227 -88
- package/src/server/common/repositories/userSettingsRepository.js +108 -100
- package/src/server/common/support/accountSettingsJsonApiTransport.js +10 -0
- package/src/server/usersBootstrapContributor.js +13 -32
- package/src/shared/resources/accountSettingsSchemas.js +83 -0
- package/src/shared/resources/userProfileResource.js +146 -126
- package/src/shared/resources/userSettingsResource.js +376 -353
- package/templates/packages/users/package.descriptor.mjs +4 -5
- package/templates/packages/users/package.json +0 -1
- package/templates/packages/users/src/server/UsersProvider.js +23 -24
- package/templates/packages/users/src/server/actions.js +26 -28
- package/templates/packages/users/src/server/registerRoutes.js +29 -15
- package/templates/packages/users/src/server/repository.js +35 -28
- package/templates/packages/users/src/server/service.js +20 -15
- package/templates/packages/users/src/shared/userResource.js +55 -68
- package/templates/packages/users-workspace/package.descriptor.mjs +4 -5
- package/templates/packages/users-workspace/src/server/UsersProvider.js +23 -24
- package/templates/packages/users-workspace/src/server/actions.js +28 -28
- package/templates/packages/users-workspace/src/server/registerRoutes.js +34 -16
- package/test/accountSecurityService.test.js +32 -0
- package/test/providerLifecycle.test.js +63 -0
- package/test/registerCommonRepositories.test.js +28 -8
- package/test/repositoryContracts.test.js +177 -28
- package/test/resourcesCanonical.test.js +18 -11
- package/test/userSettingsInternalResource.test.js +8 -0
- package/test/userSettingsResource.test.js +24 -7
- package/test/usersBootstrapContributor.test.js +40 -1
- package/test/usersPackageScaffoldContract.test.js +70 -3
- package/test/usersRouteRequestInputValidator.test.js +92 -23
- package/test/usersRouteResources.test.js +28 -18
- package/src/server/common/resources/userProfilesResource.js +0 -203
- package/src/server/common/validators/authenticatedUserValidator.js +0 -43
- package/src/shared/resources/resolveGlobalArrayRegistry.js +0 -6
- package/src/shared/resources/userSettingsFields.js +0 -76
- package/templates/packages/main/src/shared/resources/userSettingsFields.js +0 -138
- package/templates/packages/users/src/server/actionIds.js +0 -6
- package/templates/packages/users/src/server/listConfig.js +0 -16
- package/test/settingsFieldRegistriesSingleton.test.js +0 -14
- package/test-support/registerDefaultSettingsFields.js +0 -2
|
@@ -9,6 +9,8 @@ export default Object.freeze({
|
|
|
9
9
|
"@jskit-ai/crud-core",
|
|
10
10
|
"@jskit-ai/database-runtime",
|
|
11
11
|
"@jskit-ai/http-runtime",
|
|
12
|
+
"@jskit-ai/json-rest-api-core",
|
|
13
|
+
"@jskit-ai/resource-crud-core",
|
|
12
14
|
"@jskit-ai/users-core"
|
|
13
15
|
],
|
|
14
16
|
capabilities: {
|
|
@@ -18,7 +20,8 @@ export default Object.freeze({
|
|
|
18
20
|
requires: [
|
|
19
21
|
"runtime.actions",
|
|
20
22
|
"runtime.database",
|
|
21
|
-
"auth.policy"
|
|
23
|
+
"auth.policy",
|
|
24
|
+
"json-rest-api.core"
|
|
22
25
|
]
|
|
23
26
|
},
|
|
24
27
|
runtime: {
|
|
@@ -34,10 +37,6 @@ export default Object.freeze({
|
|
|
34
37
|
metadata: {
|
|
35
38
|
apiSummary: {
|
|
36
39
|
surfaces: [
|
|
37
|
-
{
|
|
38
|
-
subpath: "./server/actionIds",
|
|
39
|
-
summary: "App-local CRUD public action identifiers."
|
|
40
|
-
},
|
|
41
40
|
{
|
|
42
41
|
subpath: "./shared",
|
|
43
42
|
summary: "App-local CRUD shared resource."
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
2
2
|
import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
INTERNAL_JSON_REST_API,
|
|
5
|
+
addResourceIfMissing,
|
|
6
|
+
createJsonRestResourceScopeOptions
|
|
7
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
7
8
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
9
|
+
import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
|
|
8
10
|
import { createRepository } from "./repository.js";
|
|
9
|
-
import {
|
|
10
|
-
createService,
|
|
11
|
-
serviceEvents
|
|
12
|
-
} from "./service.js";
|
|
11
|
+
import { createService } from "./service.js";
|
|
13
12
|
import { createActions } from "./actions.js";
|
|
14
13
|
import { registerRoutes } from "./registerRoutes.js";
|
|
14
|
+
import { resource } from "../shared/userResource.js";
|
|
15
15
|
|
|
16
16
|
const CRUD_MODULE_CONFIG = Object.freeze({
|
|
17
17
|
namespace: "users",
|
|
@@ -29,25 +29,17 @@ function resolveCrudPolicyFromApp(app) {
|
|
|
29
29
|
class UsersProvider {
|
|
30
30
|
static id = "crud.users";
|
|
31
31
|
|
|
32
|
-
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main"];
|
|
32
|
+
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "json-rest-api.core"];
|
|
33
33
|
|
|
34
34
|
register(app) {
|
|
35
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
36
|
-
throw new Error("UsersProvider requires application singleton()/service()/actions().");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
40
36
|
|
|
41
37
|
app.singleton("repository.users", (scope) => {
|
|
38
|
+
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
42
39
|
const knex = scope.make("jskit.database.knex");
|
|
43
|
-
return createRepository(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
app.singleton("lookup.users", (scope) => {
|
|
49
|
-
return createCrudLookup(scope.make("repository.users"), {
|
|
50
|
-
ownershipFilter: crudPolicy.ownershipFilter
|
|
40
|
+
return createRepository({
|
|
41
|
+
api,
|
|
42
|
+
knex
|
|
51
43
|
});
|
|
52
44
|
});
|
|
53
45
|
|
|
@@ -57,9 +49,6 @@ class UsersProvider {
|
|
|
57
49
|
return createService({
|
|
58
50
|
usersRepository: scope.make("repository.users")
|
|
59
51
|
});
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
events: serviceEvents
|
|
63
52
|
}
|
|
64
53
|
);
|
|
65
54
|
|
|
@@ -78,8 +67,18 @@ class UsersProvider {
|
|
|
78
67
|
);
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
boot(app) {
|
|
70
|
+
async boot(app) {
|
|
82
71
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
72
|
+
const api = app.make(INTERNAL_JSON_REST_API);
|
|
73
|
+
await addResourceIfMissing(
|
|
74
|
+
api,
|
|
75
|
+
"users",
|
|
76
|
+
createJsonRestResourceScopeOptions(resource, {
|
|
77
|
+
writeSerializers: {
|
|
78
|
+
"datetime-utc": toDatabaseDateTimeUtc
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
);
|
|
83
82
|
registerRoutes(app, {
|
|
84
83
|
routeOwnershipFilter: crudPolicy.ownershipFilter,
|
|
85
84
|
routeSurface: crudPolicy.surfaceId,
|
|
@@ -1,67 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
composeSchemaDefinitions,
|
|
3
|
+
recordIdParamsValidator
|
|
4
|
+
} from "@jskit-ai/kernel/shared/validators";
|
|
2
5
|
import {
|
|
3
6
|
createCrudCursorPaginationQueryValidator,
|
|
4
7
|
listSearchQueryValidator
|
|
5
8
|
} from "@jskit-ai/crud-core/server/listQueryValidators";
|
|
6
9
|
import { resource } from "../shared/userResource.js";
|
|
7
|
-
import { actionIds } from "./actionIds.js";
|
|
8
|
-
import { LIST_CONFIG } from "./listConfig.js";
|
|
9
10
|
|
|
10
|
-
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator(
|
|
11
|
+
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator({
|
|
12
|
+
orderBy: resource.defaultSort
|
|
13
|
+
});
|
|
11
14
|
const authenticatedPermission = Object.freeze({
|
|
12
15
|
require: "authenticated"
|
|
13
16
|
});
|
|
14
17
|
|
|
15
|
-
function
|
|
16
|
-
const normalizedSurface = String(surface || "").trim().toLowerCase();
|
|
17
|
-
if (!normalizedSurface) {
|
|
18
|
-
throw new TypeError("createActions requires a non-empty surface.");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return normalizedSurface;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function createActions({ surface = "" } = {}) {
|
|
25
|
-
const actionSurface = requireActionSurface(surface);
|
|
26
|
-
|
|
18
|
+
function createActions({ surface } = {}) {
|
|
27
19
|
return Object.freeze([
|
|
28
20
|
{
|
|
29
|
-
id:
|
|
21
|
+
id: "crud.users.list",
|
|
30
22
|
version: 1,
|
|
31
23
|
kind: "query",
|
|
32
24
|
channels: ["api", "automation", "internal"],
|
|
33
|
-
surfaces: [
|
|
25
|
+
surfaces: [surface],
|
|
34
26
|
permission: authenticatedPermission,
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
input: composeSchemaDefinitions([
|
|
28
|
+
listCursorPaginationQueryValidator,
|
|
29
|
+
listSearchQueryValidator
|
|
30
|
+
]),
|
|
31
|
+
output: null,
|
|
37
32
|
idempotency: "none",
|
|
38
33
|
audit: {
|
|
39
|
-
actionName:
|
|
34
|
+
actionName: "crud.users.list"
|
|
40
35
|
},
|
|
41
36
|
observability: {},
|
|
42
37
|
async execute(input, context, deps) {
|
|
43
|
-
|
|
38
|
+
const { workspaceSlug, ...query } = input || {};
|
|
39
|
+
return deps.usersService.queryDocuments(query, {
|
|
44
40
|
context,
|
|
45
41
|
visibilityContext: context?.visibilityContext
|
|
46
42
|
});
|
|
47
43
|
}
|
|
48
44
|
},
|
|
49
45
|
{
|
|
50
|
-
id:
|
|
46
|
+
id: "crud.users.view",
|
|
51
47
|
version: 1,
|
|
52
48
|
kind: "query",
|
|
53
49
|
channels: ["api", "automation", "internal"],
|
|
54
|
-
surfaces: [
|
|
50
|
+
surfaces: [surface],
|
|
55
51
|
permission: authenticatedPermission,
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
input: composeSchemaDefinitions([
|
|
53
|
+
recordIdParamsValidator
|
|
54
|
+
]),
|
|
55
|
+
output: null,
|
|
58
56
|
idempotency: "none",
|
|
59
57
|
audit: {
|
|
60
|
-
actionName:
|
|
58
|
+
actionName: "crud.users.view"
|
|
61
59
|
},
|
|
62
60
|
observability: {},
|
|
63
61
|
async execute(input, context, deps) {
|
|
64
|
-
return deps.usersService.
|
|
62
|
+
return deps.usersService.getDocumentById(input.recordId, {
|
|
65
63
|
context,
|
|
66
64
|
visibilityContext: context?.visibilityContext
|
|
67
65
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createJsonApiResourceRouteContract } from "@jskit-ai/http-runtime/shared/validators/jsonApiRouteTransport";
|
|
2
2
|
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
3
3
|
import {
|
|
4
4
|
createCrudCursorPaginationQueryValidator,
|
|
@@ -6,12 +6,31 @@ import {
|
|
|
6
6
|
} from "@jskit-ai/crud-core/server/listQueryValidators";
|
|
7
7
|
import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
|
|
8
8
|
import { checkRouteVisibility } from "@jskit-ai/kernel/shared/support/visibility";
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
import {
|
|
10
|
+
composeSchemaDefinitions,
|
|
11
|
+
recordIdParamsValidator
|
|
12
|
+
} from "@jskit-ai/kernel/shared/validators";
|
|
12
13
|
import { resource } from "../shared/userResource.js";
|
|
13
14
|
|
|
14
|
-
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator(
|
|
15
|
+
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator({
|
|
16
|
+
orderBy: resource.defaultSort
|
|
17
|
+
});
|
|
18
|
+
const listRouteQueryValidator = composeSchemaDefinitions([
|
|
19
|
+
listCursorPaginationQueryValidator,
|
|
20
|
+
listSearchQueryValidator
|
|
21
|
+
]);
|
|
22
|
+
const RESOURCE_ROUTE_CONTRACT_TYPE = resource.namespace;
|
|
23
|
+
const listRouteContract = createJsonApiResourceRouteContract({
|
|
24
|
+
type: RESOURCE_ROUTE_CONTRACT_TYPE,
|
|
25
|
+
query: listRouteQueryValidator,
|
|
26
|
+
output: resource.operations.view.output,
|
|
27
|
+
outputKind: "collection"
|
|
28
|
+
});
|
|
29
|
+
const viewRouteContract = createJsonApiResourceRouteContract({
|
|
30
|
+
type: RESOURCE_ROUTE_CONTRACT_TYPE,
|
|
31
|
+
output: resource.operations.view.output,
|
|
32
|
+
outputKind: "record"
|
|
33
|
+
});
|
|
15
34
|
|
|
16
35
|
function registerRoutes(
|
|
17
36
|
app,
|
|
@@ -40,14 +59,11 @@ function registerRoutes(
|
|
|
40
59
|
tags: ["crud"],
|
|
41
60
|
summary: "List users."
|
|
42
61
|
},
|
|
43
|
-
|
|
44
|
-
responseValidators: withStandardErrorResponses({
|
|
45
|
-
200: resource.operations.list.outputValidator
|
|
46
|
-
})
|
|
62
|
+
...listRouteContract
|
|
47
63
|
},
|
|
48
64
|
async function (request, reply) {
|
|
49
65
|
const response = await request.executeAction({
|
|
50
|
-
actionId:
|
|
66
|
+
actionId: "crud.users.list",
|
|
51
67
|
input: {
|
|
52
68
|
...(request.input.query || {})
|
|
53
69
|
}
|
|
@@ -67,14 +83,12 @@ function registerRoutes(
|
|
|
67
83
|
tags: ["crud"],
|
|
68
84
|
summary: "View a user."
|
|
69
85
|
},
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
200: resource.operations.view.outputValidator
|
|
73
|
-
})
|
|
86
|
+
...viewRouteContract,
|
|
87
|
+
params: recordIdParamsValidator
|
|
74
88
|
},
|
|
75
89
|
async function (request, reply) {
|
|
76
90
|
const response = await request.executeAction({
|
|
77
|
-
actionId:
|
|
91
|
+
actionId: "crud.users.view",
|
|
78
92
|
input: {
|
|
79
93
|
recordId: request.input.params.recordId
|
|
80
94
|
}
|
|
@@ -1,40 +1,47 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
|
|
2
|
+
import {
|
|
3
|
+
buildJsonRestQueryParams,
|
|
4
|
+
createJsonRestContext,
|
|
5
|
+
returnNullWhenJsonRestResourceMissing
|
|
6
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
2
7
|
import { resource } from "../shared/userResource.js";
|
|
3
|
-
import { LIST_CONFIG } from "./listConfig.js";
|
|
4
8
|
|
|
5
|
-
const
|
|
6
|
-
context: "users repository",
|
|
7
|
-
list: LIST_CONFIG
|
|
8
|
-
});
|
|
9
|
+
const RESOURCE_TYPE = resource.namespace;
|
|
9
10
|
|
|
10
|
-
function createRepository(
|
|
11
|
-
const
|
|
12
|
-
...options,
|
|
13
|
-
...REPOSITORY_CONFIG
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
async function list(query = {}, callOptions = {}) {
|
|
17
|
-
return resourceRuntime.list(query, callOptions);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function findById(recordId, callOptions = {}) {
|
|
21
|
-
return resourceRuntime.findById(recordId, callOptions);
|
|
22
|
-
}
|
|
11
|
+
function createRepository({ api, knex } = {}) {
|
|
12
|
+
const withTransaction = createWithTransaction(knex);
|
|
23
13
|
|
|
24
|
-
async function
|
|
25
|
-
return
|
|
14
|
+
async function queryDocuments(query = {}, options = {}) {
|
|
15
|
+
return api.resources.users.query(
|
|
16
|
+
{
|
|
17
|
+
queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, query),
|
|
18
|
+
transaction: options?.trx || null,
|
|
19
|
+
simplified: false
|
|
20
|
+
},
|
|
21
|
+
createJsonRestContext(options?.context || null)
|
|
22
|
+
);
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
async function
|
|
29
|
-
return
|
|
25
|
+
async function getDocumentById(recordId, options = {}) {
|
|
26
|
+
return returnNullWhenJsonRestResourceMissing(() =>
|
|
27
|
+
api.resources.users.get(
|
|
28
|
+
{
|
|
29
|
+
id: recordId,
|
|
30
|
+
queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, {}, {
|
|
31
|
+
include: options?.include
|
|
32
|
+
}),
|
|
33
|
+
transaction: options?.trx || null,
|
|
34
|
+
simplified: false
|
|
35
|
+
},
|
|
36
|
+
createJsonRestContext(options?.context || null)
|
|
37
|
+
)
|
|
38
|
+
);
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
return Object.freeze({
|
|
33
|
-
withTransaction
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
listByIds,
|
|
37
|
-
listByForeignIds
|
|
42
|
+
withTransaction,
|
|
43
|
+
queryDocuments,
|
|
44
|
+
getDocumentById
|
|
38
45
|
});
|
|
39
46
|
}
|
|
40
47
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
crudServiceListRecords,
|
|
4
|
-
crudServiceGetRecord
|
|
5
|
-
} from "@jskit-ai/crud-core/server/serviceMethods";
|
|
6
|
-
import { resource } from "../shared/userResource.js";
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
2
|
+
import { returnJsonApiDocument } from "@jskit-ai/http-runtime/shared";
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
function return404IfNotFound(document = null) {
|
|
5
|
+
if (!document) {
|
|
6
|
+
throw new AppError(404, "Document not found.");
|
|
7
|
+
}
|
|
8
|
+
return document;
|
|
9
|
+
}
|
|
12
10
|
|
|
13
11
|
function createService({ usersRepository } = {}) {
|
|
14
12
|
if (!usersRepository) {
|
|
@@ -16,13 +14,20 @@ function createService({ usersRepository } = {}) {
|
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
return Object.freeze({
|
|
19
|
-
|
|
20
|
-
return
|
|
17
|
+
async queryDocuments(query = {}, options = {}) {
|
|
18
|
+
return returnJsonApiDocument(await usersRepository.queryDocuments(query, {
|
|
19
|
+
trx: options?.trx || null,
|
|
20
|
+
context: options?.context || null
|
|
21
|
+
}));
|
|
21
22
|
},
|
|
22
|
-
|
|
23
|
-
return
|
|
23
|
+
async getDocumentById(recordId, options = {}) {
|
|
24
|
+
return returnJsonApiDocument(return404IfNotFound(await usersRepository.getDocumentById(recordId, {
|
|
25
|
+
trx: options?.trx || null,
|
|
26
|
+
context: options?.context || null,
|
|
27
|
+
include: options?.include
|
|
28
|
+
})));
|
|
24
29
|
}
|
|
25
30
|
});
|
|
26
31
|
}
|
|
27
32
|
|
|
28
|
-
export { createService
|
|
33
|
+
export { createService };
|
|
@@ -1,74 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { toIsoString } from "@jskit-ai/database-runtime/shared";
|
|
3
|
-
import {
|
|
4
|
-
createCursorListValidator,
|
|
5
|
-
normalizeObjectInput,
|
|
6
|
-
recordIdSchema
|
|
7
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
8
|
-
import {
|
|
9
|
-
normalizeIfPresent,
|
|
10
|
-
normalizeRecordId,
|
|
11
|
-
normalizeText
|
|
12
|
-
} from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
|
+
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
13
2
|
|
|
14
|
-
const
|
|
15
|
-
{
|
|
16
|
-
id: recordIdSchema,
|
|
17
|
-
name: Type.String({ minLength: 1 }),
|
|
18
|
-
email: Type.String(),
|
|
19
|
-
username: Type.String(),
|
|
20
|
-
createdAt: Type.String({ format: "date-time", minLength: 1 })
|
|
21
|
-
},
|
|
22
|
-
{ additionalProperties: false }
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
const createBodySchema = Type.Object({}, { additionalProperties: false });
|
|
26
|
-
|
|
27
|
-
const recordOutputValidator = Object.freeze({
|
|
28
|
-
schema: recordOutputSchema,
|
|
29
|
-
normalize(payload = {}) {
|
|
30
|
-
const source = normalizeObjectInput(payload);
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
id: normalizeIfPresent(source.id, normalizeRecordId),
|
|
34
|
-
name: normalizeText(source.name || source.email || source.username || source.id),
|
|
35
|
-
email: normalizeText(source.email),
|
|
36
|
-
username: normalizeText(source.username),
|
|
37
|
-
createdAt: normalizeIfPresent(source.createdAt, toIsoString)
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const createBodyValidator = Object.freeze({
|
|
43
|
-
schema: createBodySchema,
|
|
44
|
-
normalize: normalizeObjectInput
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
const resource = Object.freeze({
|
|
3
|
+
const resource = defineCrudResource({
|
|
48
4
|
namespace: "users",
|
|
49
5
|
tableName: "users",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
6
|
+
crudOperations: ["list", "view", "create"],
|
|
7
|
+
schema: {
|
|
8
|
+
name: {
|
|
9
|
+
type: "string",
|
|
10
|
+
required: true,
|
|
11
|
+
minLength: 1,
|
|
12
|
+
maxLength: 160,
|
|
13
|
+
actualField: "display_name",
|
|
14
|
+
search: true,
|
|
15
|
+
operations: {
|
|
16
|
+
output: { required: false }
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
email: {
|
|
20
|
+
type: "string",
|
|
21
|
+
required: true,
|
|
22
|
+
search: true,
|
|
23
|
+
operations: {
|
|
24
|
+
output: { required: false }
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
username: {
|
|
28
|
+
type: "string",
|
|
29
|
+
required: true,
|
|
30
|
+
search: true,
|
|
31
|
+
operations: {
|
|
32
|
+
output: { required: false }
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
createdAt: {
|
|
36
|
+
type: "dateTime",
|
|
37
|
+
required: true,
|
|
38
|
+
storage: { writeSerializer: "datetime-utc" },
|
|
39
|
+
operations: {
|
|
40
|
+
output: { required: true }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
searchSchema: {
|
|
45
|
+
id: {
|
|
46
|
+
type: "id",
|
|
47
|
+
actualField: "id"
|
|
48
|
+
},
|
|
49
|
+
q: {
|
|
50
|
+
type: "string",
|
|
51
|
+
oneOf: ["name", "email", "username"],
|
|
52
|
+
filterOperator: "like",
|
|
53
|
+
splitBy: " ",
|
|
54
|
+
matchAll: true
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
defaultSort: ["name", "email"],
|
|
58
|
+
autofilter: "public"
|
|
72
59
|
});
|
|
73
60
|
|
|
74
61
|
export { resource };
|
|
@@ -9,6 +9,8 @@ export default Object.freeze({
|
|
|
9
9
|
"@jskit-ai/crud-core",
|
|
10
10
|
"@jskit-ai/database-runtime",
|
|
11
11
|
"@jskit-ai/http-runtime",
|
|
12
|
+
"@jskit-ai/json-rest-api-core",
|
|
13
|
+
"@jskit-ai/resource-crud-core",
|
|
12
14
|
"@jskit-ai/users-core",
|
|
13
15
|
"@jskit-ai/workspaces-core"
|
|
14
16
|
],
|
|
@@ -19,7 +21,8 @@ export default Object.freeze({
|
|
|
19
21
|
requires: [
|
|
20
22
|
"runtime.actions",
|
|
21
23
|
"runtime.database",
|
|
22
|
-
"auth.policy"
|
|
24
|
+
"auth.policy",
|
|
25
|
+
"json-rest-api.core"
|
|
23
26
|
]
|
|
24
27
|
},
|
|
25
28
|
runtime: {
|
|
@@ -35,10 +38,6 @@ export default Object.freeze({
|
|
|
35
38
|
metadata: {
|
|
36
39
|
apiSummary: {
|
|
37
40
|
surfaces: [
|
|
38
|
-
{
|
|
39
|
-
subpath: "./server/actionIds",
|
|
40
|
-
summary: "App-local CRUD public action identifiers."
|
|
41
|
-
},
|
|
42
41
|
{
|
|
43
42
|
subpath: "./shared",
|
|
44
43
|
summary: "App-local CRUD shared resource."
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
2
2
|
import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
|
|
3
3
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
INTERNAL_JSON_REST_API,
|
|
5
|
+
addResourceIfMissing,
|
|
6
|
+
createJsonRestResourceScopeOptions
|
|
7
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
7
8
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
9
|
+
import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
|
|
8
10
|
import { createRepository } from "./repository.js";
|
|
9
|
-
import {
|
|
10
|
-
createService,
|
|
11
|
-
serviceEvents
|
|
12
|
-
} from "./service.js";
|
|
11
|
+
import { createService } from "./service.js";
|
|
13
12
|
import { createActions } from "./actions.js";
|
|
14
13
|
import { registerRoutes } from "./registerRoutes.js";
|
|
14
|
+
import { resource } from "../shared/userResource.js";
|
|
15
15
|
|
|
16
16
|
const CRUD_MODULE_CONFIG = Object.freeze({
|
|
17
17
|
namespace: "users",
|
|
@@ -29,25 +29,17 @@ function resolveCrudPolicyFromApp(app) {
|
|
|
29
29
|
class UsersProvider {
|
|
30
30
|
static id = "crud.users";
|
|
31
31
|
|
|
32
|
-
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main"];
|
|
32
|
+
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "json-rest-api.core"];
|
|
33
33
|
|
|
34
34
|
register(app) {
|
|
35
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
36
|
-
throw new Error("UsersProvider requires application singleton()/service()/actions().");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
35
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
40
36
|
|
|
41
37
|
app.singleton("repository.users", (scope) => {
|
|
38
|
+
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
42
39
|
const knex = scope.make("jskit.database.knex");
|
|
43
|
-
return createRepository(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
app.singleton("lookup.users", (scope) => {
|
|
49
|
-
return createCrudLookup(scope.make("repository.users"), {
|
|
50
|
-
ownershipFilter: crudPolicy.ownershipFilter
|
|
40
|
+
return createRepository({
|
|
41
|
+
api,
|
|
42
|
+
knex
|
|
51
43
|
});
|
|
52
44
|
});
|
|
53
45
|
|
|
@@ -57,9 +49,6 @@ class UsersProvider {
|
|
|
57
49
|
return createService({
|
|
58
50
|
usersRepository: scope.make("repository.users")
|
|
59
51
|
});
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
events: serviceEvents
|
|
63
52
|
}
|
|
64
53
|
);
|
|
65
54
|
|
|
@@ -78,8 +67,18 @@ class UsersProvider {
|
|
|
78
67
|
);
|
|
79
68
|
}
|
|
80
69
|
|
|
81
|
-
boot(app) {
|
|
70
|
+
async boot(app) {
|
|
82
71
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
72
|
+
const api = app.make(INTERNAL_JSON_REST_API);
|
|
73
|
+
await addResourceIfMissing(
|
|
74
|
+
api,
|
|
75
|
+
"users",
|
|
76
|
+
createJsonRestResourceScopeOptions(resource, {
|
|
77
|
+
writeSerializers: {
|
|
78
|
+
"datetime-utc": toDatabaseDateTimeUtc
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
);
|
|
83
82
|
registerRoutes(app, {
|
|
84
83
|
routeOwnershipFilter: crudPolicy.ownershipFilter,
|
|
85
84
|
routeSurface: crudPolicy.surfaceId,
|