@jskit-ai/crud-server-generator 0.1.63 → 0.1.64
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 +13 -30
- package/package.json +8 -8
- package/src/server/buildTemplateContext.js +449 -496
- package/src/server/subcommands/addField.js +8 -80
- package/src/server/subcommands/resourceAst.js +40 -393
- package/src/shared/crud/crudResource.js +85 -185
- package/templates/src/local-package/package.descriptor.mjs +3 -6
- package/templates/src/local-package/package.json +0 -1
- package/templates/src/local-package/server/CrudProvider.js +28 -21
- package/templates/src/local-package/server/actions.js +42 -54
- package/templates/src/local-package/server/registerRoutes.js +22 -50
- package/templates/src/local-package/server/repository.js +82 -38
- package/templates/src/local-package/server/service.js +45 -73
- package/templates/src/local-package/shared/crudResource.js +15 -140
- package/test/addFieldSubcommand.test.js +100 -77
- package/test/buildTemplateContext.test.js +139 -203
- package/test/crudResource.test.js +26 -31
- package/test/crudServerGuards.test.js +157 -42
- package/test/crudService.test.js +91 -173
- package/test/packageDescriptor.test.js +3 -11
- package/test/routeInputContracts.test.js +77 -8
- package/test/templateSymbolConsistency.test.js +19 -3
- package/test-support/templateServerFixture.js +155 -112
- package/templates/src/local-package/server/actionIds.js +0 -9
- package/templates/src/local-package/server/listConfig.js +0 -5
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
|
|
2
1
|
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
3
|
-
import {
|
|
4
|
-
createCrudCursorPaginationQueryValidator,
|
|
5
|
-
listSearchQueryValidator,
|
|
6
|
-
lookupIncludeQueryValidator,
|
|
7
|
-
createCrudParentFilterQueryValidator
|
|
8
|
-
} from "@jskit-ai/crud-core/server/listQueryValidators";
|
|
9
|
-
import {
|
|
10
|
-
recordIdParamsValidator
|
|
11
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
2
|
+
import { createCrudJsonApiRouteContracts } from "@jskit-ai/crud-core/server/routeContracts";
|
|
12
3
|
import { checkRouteVisibility } from "@jskit-ai/kernel/shared/support/visibility";
|
|
13
4
|
import { resolveScopedApiBasePath } from "@jskit-ai/kernel/shared/surface";
|
|
14
|
-
import { actionIds } from "./actionIds.js";
|
|
15
5
|
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
16
|
-
import { LIST_CONFIG } from "./listConfig.js";
|
|
17
6
|
__JSKIT_CRUD_ROUTE_WORKSPACE_SUPPORT_IMPORTS__
|
|
18
7
|
|
|
19
|
-
const
|
|
20
|
-
|
|
8
|
+
const {
|
|
9
|
+
listRouteContract,
|
|
10
|
+
viewRouteContract,
|
|
11
|
+
createRouteContract,
|
|
12
|
+
updateRouteContract,
|
|
13
|
+
deleteRouteContract,
|
|
14
|
+
recordRouteParamsValidator
|
|
15
|
+
} = createCrudJsonApiRouteContracts({
|
|
16
|
+
resource__JSKIT_CRUD_ROUTE_CONTRACTS_RESOURCE_ARGS__
|
|
17
|
+
});
|
|
21
18
|
|
|
22
19
|
function registerRoutes(
|
|
23
20
|
app,
|
|
@@ -46,23 +43,15 @@ function registerRoutes(
|
|
|
46
43
|
tags: ["crud"],
|
|
47
44
|
summary: "List records."
|
|
48
45
|
},
|
|
46
|
+
...listRouteContract,
|
|
49
47
|
__JSKIT_CRUD_LIST_ROUTE_PARAMS_VALIDATOR_LINE__
|
|
50
|
-
queryValidator: [
|
|
51
|
-
listCursorPaginationQueryValidator,
|
|
52
|
-
listSearchQueryValidator,
|
|
53
|
-
listParentFilterQueryValidator,
|
|
54
|
-
lookupIncludeQueryValidator
|
|
55
|
-
],
|
|
56
|
-
responseValidators: withStandardErrorResponses({
|
|
57
|
-
200: resource.operations.list.outputValidator
|
|
58
|
-
})
|
|
59
48
|
},
|
|
60
49
|
async function (request, reply) {
|
|
61
50
|
const listInput = {
|
|
62
51
|
__JSKIT_CRUD_LIST_ROUTE_INPUT_LINES__
|
|
63
52
|
};
|
|
64
53
|
const response = await request.executeAction({
|
|
65
|
-
actionId:
|
|
54
|
+
actionId: "crud.${option:namespace|snake}.list",
|
|
66
55
|
input: listInput
|
|
67
56
|
});
|
|
68
57
|
reply.code(200).send(response);
|
|
@@ -80,15 +69,12 @@ __JSKIT_CRUD_LIST_ROUTE_INPUT_LINES__
|
|
|
80
69
|
tags: ["crud"],
|
|
81
70
|
summary: "View a record."
|
|
82
71
|
},
|
|
72
|
+
...viewRouteContract,
|
|
83
73
|
__JSKIT_CRUD_VIEW_ROUTE_PARAMS_VALIDATOR_LINE__
|
|
84
|
-
queryValidator: [lookupIncludeQueryValidator],
|
|
85
|
-
responseValidators: withStandardErrorResponses({
|
|
86
|
-
200: resource.operations.view.outputValidator
|
|
87
|
-
})
|
|
88
74
|
},
|
|
89
75
|
async function (request, reply) {
|
|
90
76
|
const response = await request.executeAction({
|
|
91
|
-
actionId:
|
|
77
|
+
actionId: "crud.${option:namespace|snake}.view",
|
|
92
78
|
input: {
|
|
93
79
|
__JSKIT_CRUD_VIEW_ROUTE_INPUT_LINES__
|
|
94
80
|
}
|
|
@@ -108,18 +94,12 @@ __JSKIT_CRUD_VIEW_ROUTE_INPUT_LINES__
|
|
|
108
94
|
tags: ["crud"],
|
|
109
95
|
summary: "Create a record."
|
|
110
96
|
},
|
|
97
|
+
...createRouteContract,
|
|
111
98
|
__JSKIT_CRUD_CREATE_ROUTE_PARAMS_VALIDATOR_LINE__
|
|
112
|
-
bodyValidator: resource.operations.create.bodyValidator,
|
|
113
|
-
responseValidators: withStandardErrorResponses(
|
|
114
|
-
{
|
|
115
|
-
201: resource.operations.create.outputValidator
|
|
116
|
-
},
|
|
117
|
-
{ includeValidation400: true }
|
|
118
|
-
)
|
|
119
99
|
},
|
|
120
100
|
async function (request, reply) {
|
|
121
101
|
const response = await request.executeAction({
|
|
122
|
-
actionId:
|
|
102
|
+
actionId: "crud.${option:namespace|snake}.create",
|
|
123
103
|
input: {
|
|
124
104
|
__JSKIT_CRUD_CREATE_ROUTE_INPUT_LINES__
|
|
125
105
|
}
|
|
@@ -139,18 +119,12 @@ __JSKIT_CRUD_CREATE_ROUTE_INPUT_LINES__
|
|
|
139
119
|
tags: ["crud"],
|
|
140
120
|
summary: "Update a record."
|
|
141
121
|
},
|
|
122
|
+
...updateRouteContract,
|
|
142
123
|
__JSKIT_CRUD_UPDATE_ROUTE_PARAMS_VALIDATOR_LINE__
|
|
143
|
-
bodyValidator: resource.operations.patch.bodyValidator,
|
|
144
|
-
responseValidators: withStandardErrorResponses(
|
|
145
|
-
{
|
|
146
|
-
200: resource.operations.patch.outputValidator
|
|
147
|
-
},
|
|
148
|
-
{ includeValidation400: true }
|
|
149
|
-
)
|
|
150
124
|
},
|
|
151
125
|
async function (request, reply) {
|
|
152
126
|
const response = await request.executeAction({
|
|
153
|
-
actionId:
|
|
127
|
+
actionId: "crud.${option:namespace|snake}.update",
|
|
154
128
|
input: {
|
|
155
129
|
__JSKIT_CRUD_UPDATE_ROUTE_INPUT_LINES__
|
|
156
130
|
}
|
|
@@ -170,19 +144,17 @@ __JSKIT_CRUD_UPDATE_ROUTE_INPUT_LINES__
|
|
|
170
144
|
tags: ["crud"],
|
|
171
145
|
summary: "Delete a record."
|
|
172
146
|
},
|
|
147
|
+
...deleteRouteContract,
|
|
173
148
|
__JSKIT_CRUD_DELETE_ROUTE_PARAMS_VALIDATOR_LINE__
|
|
174
|
-
responseValidators: withStandardErrorResponses({
|
|
175
|
-
200: resource.operations.delete.outputValidator
|
|
176
|
-
})
|
|
177
149
|
},
|
|
178
150
|
async function (request, reply) {
|
|
179
151
|
const response = await request.executeAction({
|
|
180
|
-
actionId:
|
|
152
|
+
actionId: "crud.${option:namespace|snake}.delete",
|
|
181
153
|
input: {
|
|
182
154
|
__JSKIT_CRUD_DELETE_ROUTE_INPUT_LINES__
|
|
183
155
|
}
|
|
184
156
|
});
|
|
185
|
-
reply.code(
|
|
157
|
+
reply.code(204).send(response);
|
|
186
158
|
}
|
|
187
159
|
);
|
|
188
160
|
}
|
|
@@ -1,57 +1,101 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createWithTransaction } from "@jskit-ai/database-runtime/shared";
|
|
2
|
+
import {
|
|
3
|
+
buildJsonRestQueryParams,
|
|
4
|
+
createJsonApiInputRecord,
|
|
5
|
+
createJsonRestContext,
|
|
6
|
+
returnNullWhenJsonRestResourceMissing
|
|
7
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
2
8
|
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
3
|
-
|
|
9
|
+
const RESOURCE_TYPE = resource.namespace;
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
function createRepository({ api, knex } = {}) {
|
|
12
|
+
const withTransaction = createWithTransaction(knex);
|
|
6
13
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
async function list(query = {}, callOptions = {}) {
|
|
19
|
-
return resourceRuntime.list(query, callOptions);
|
|
14
|
+
async function queryDocuments(query = {}, options = {}) {
|
|
15
|
+
return api.resources.${option:namespace|camel}.query(
|
|
16
|
+
{
|
|
17
|
+
queryParams: buildJsonRestQueryParams(RESOURCE_TYPE, query),
|
|
18
|
+
transaction: options?.trx || null,
|
|
19
|
+
simplified: false
|
|
20
|
+
},
|
|
21
|
+
createJsonRestContext(options?.context || null)
|
|
22
|
+
);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
async function
|
|
23
|
-
return
|
|
25
|
+
async function getDocumentById(recordId, options = {}) {
|
|
26
|
+
return returnNullWhenJsonRestResourceMissing(() =>
|
|
27
|
+
api.resources.${option:namespace|camel}.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
|
+
);
|
|
24
39
|
}
|
|
25
40
|
|
|
26
|
-
async function
|
|
27
|
-
return
|
|
41
|
+
async function createDocument(payload = {}, options = {}) {
|
|
42
|
+
return api.resources.${option:namespace|camel}.post(
|
|
43
|
+
{
|
|
44
|
+
inputRecord: createJsonApiInputRecord(RESOURCE_TYPE, payload),
|
|
45
|
+
transaction: options?.trx || null,
|
|
46
|
+
simplified: false
|
|
47
|
+
},
|
|
48
|
+
createJsonRestContext(options?.context || null)
|
|
49
|
+
);
|
|
28
50
|
}
|
|
29
51
|
|
|
30
|
-
async function
|
|
31
|
-
|
|
32
|
-
|
|
52
|
+
async function patchDocumentById(recordId, patch = {}, options = {}) {
|
|
53
|
+
const sourcePatch = patch && typeof patch === "object" && !Array.isArray(patch) ? patch : {};
|
|
54
|
+
if (Object.keys(sourcePatch).length < 1) {
|
|
55
|
+
return getDocumentById(recordId, options);
|
|
56
|
+
}
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
return returnNullWhenJsonRestResourceMissing(() =>
|
|
59
|
+
api.resources.${option:namespace|camel}.patch(
|
|
60
|
+
{
|
|
61
|
+
id: recordId,
|
|
62
|
+
inputRecord: createJsonApiInputRecord(
|
|
63
|
+
RESOURCE_TYPE,
|
|
64
|
+
{
|
|
65
|
+
...sourcePatch,
|
|
66
|
+
updatedAt: new Date()
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
transaction: options?.trx || null,
|
|
70
|
+
simplified: false
|
|
71
|
+
},
|
|
72
|
+
createJsonRestContext(options?.context || null)
|
|
73
|
+
)
|
|
74
|
+
);
|
|
36
75
|
}
|
|
37
76
|
|
|
38
|
-
async function
|
|
39
|
-
return
|
|
40
|
-
|
|
77
|
+
async function deleteDocumentById(recordId, options = {}) {
|
|
78
|
+
return returnNullWhenJsonRestResourceMissing(async () => {
|
|
79
|
+
await api.resources.${option:namespace|camel}.delete(
|
|
80
|
+
{
|
|
81
|
+
id: recordId,
|
|
82
|
+
transaction: options?.trx || null,
|
|
83
|
+
simplified: false
|
|
84
|
+
},
|
|
85
|
+
createJsonRestContext(options?.context || null)
|
|
86
|
+
);
|
|
41
87
|
|
|
42
|
-
|
|
43
|
-
|
|
88
|
+
return true;
|
|
89
|
+
});
|
|
44
90
|
}
|
|
45
91
|
|
|
46
92
|
return Object.freeze({
|
|
47
|
-
withTransaction
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
updateById,
|
|
54
|
-
deleteById
|
|
93
|
+
withTransaction,
|
|
94
|
+
queryDocuments,
|
|
95
|
+
getDocumentById,
|
|
96
|
+
createDocument,
|
|
97
|
+
patchDocumentById,
|
|
98
|
+
deleteDocumentById
|
|
55
99
|
});
|
|
56
100
|
}
|
|
57
101
|
|
|
@@ -1,90 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
createCrudServiceRuntime,
|
|
4
|
-
crudServiceListRecords,
|
|
5
|
-
crudServiceGetRecord,
|
|
6
|
-
crudServiceCreateRecord,
|
|
7
|
-
crudServiceUpdateRecord,
|
|
8
|
-
crudServiceDeleteRecord
|
|
9
|
-
} from "@jskit-ai/crud-core/server/serviceMethods";
|
|
10
|
-
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
2
|
+
import { returnJsonApiDocument } from "@jskit-ai/http-runtime/shared";
|
|
11
3
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const serviceEvents = Object.freeze({
|
|
20
|
-
createRecord: [...baseServiceEvents.createRecord],
|
|
21
|
-
updateRecord: [...baseServiceEvents.updateRecord],
|
|
22
|
-
deleteRecord: [...baseServiceEvents.deleteRecord]
|
|
23
|
-
});
|
|
4
|
+
function return404IfNotFound(document = null) {
|
|
5
|
+
if (!document) {
|
|
6
|
+
throw new AppError(404, "Document not found.");
|
|
7
|
+
}
|
|
8
|
+
return document;
|
|
9
|
+
}
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// default: {
|
|
30
|
-
// readable: { list: ["id", "name"], view: ["id", "name", "email"] },
|
|
31
|
-
// writable: { create: ["name", "email"], update: ["name"] }
|
|
32
|
-
// },
|
|
33
|
-
// admin: {
|
|
34
|
-
// readable: "*",
|
|
35
|
-
// writable: "*"
|
|
36
|
-
// },
|
|
37
|
-
// writeMode: "throw" // or "strip"
|
|
38
|
-
// });
|
|
39
|
-
// readable: ({ action, context }) => ["id", "name"], // null/"*" means no read filtering
|
|
40
|
-
// Read redaction behavior: drop optional fields; use null/default for required fields.
|
|
41
|
-
// writable: ({ action, context }) => ["name"], // null/"*" means no write filtering
|
|
42
|
-
// writeMode: "throw" // "throw" (default) or "strip"
|
|
43
|
-
});
|
|
11
|
+
function createService({ ${option:namespace|camel}Repository } = {}) {
|
|
12
|
+
if (!${option:namespace|camel}Repository) {
|
|
13
|
+
throw new TypeError("createService requires ${option:namespace|camel}Repository.");
|
|
14
|
+
}
|
|
44
15
|
|
|
45
|
-
function
|
|
46
|
-
|
|
47
|
-
|
|
16
|
+
async function queryDocuments(query = {}, options = {}) {
|
|
17
|
+
return returnJsonApiDocument(await ${option:namespace|camel}Repository.queryDocuments(query, {
|
|
18
|
+
trx: options?.trx || null,
|
|
19
|
+
context: options?.context || null
|
|
20
|
+
}));
|
|
48
21
|
}
|
|
49
22
|
|
|
50
|
-
async function
|
|
51
|
-
return
|
|
23
|
+
async function getDocumentById(recordId, options = {}) {
|
|
24
|
+
return returnJsonApiDocument(return404IfNotFound(await ${option:namespace|camel}Repository.getDocumentById(recordId, {
|
|
25
|
+
trx: options?.trx || null,
|
|
26
|
+
context: options?.context || null,
|
|
27
|
+
include: options?.include
|
|
28
|
+
})));
|
|
52
29
|
}
|
|
53
30
|
|
|
54
|
-
async function
|
|
55
|
-
return
|
|
31
|
+
async function createDocument(payload = {}, options = {}) {
|
|
32
|
+
return returnJsonApiDocument(await ${option:namespace|camel}Repository.createDocument(payload, {
|
|
33
|
+
trx: options?.trx || null,
|
|
34
|
+
context: options?.context || null
|
|
35
|
+
}));
|
|
56
36
|
}
|
|
57
37
|
|
|
58
|
-
async function
|
|
59
|
-
return
|
|
38
|
+
async function patchDocumentById(recordId, payload = {}, options = {}) {
|
|
39
|
+
return returnJsonApiDocument(return404IfNotFound(await ${option:namespace|camel}Repository.patchDocumentById(recordId, payload, {
|
|
40
|
+
trx: options?.trx || null,
|
|
41
|
+
context: options?.context || null
|
|
42
|
+
})));
|
|
60
43
|
}
|
|
61
44
|
|
|
62
|
-
async function
|
|
63
|
-
|
|
45
|
+
async function deleteDocumentById(recordId, options = {}) {
|
|
46
|
+
return404IfNotFound(await ${option:namespace|camel}Repository.deleteDocumentById(recordId, {
|
|
47
|
+
trx: options?.trx || null,
|
|
48
|
+
context: options?.context || null
|
|
49
|
+
}));
|
|
50
|
+
return null;
|
|
64
51
|
}
|
|
65
52
|
|
|
66
53
|
return Object.freeze({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
54
|
+
queryDocuments,
|
|
55
|
+
getDocumentById,
|
|
56
|
+
createDocument,
|
|
57
|
+
patchDocumentById,
|
|
58
|
+
deleteDocumentById
|
|
72
59
|
});
|
|
73
60
|
}
|
|
74
61
|
|
|
75
|
-
|
|
76
|
-
// const serviceEvents = {
|
|
77
|
-
// ...baseServiceEvents,
|
|
78
|
-
// createRecord: [
|
|
79
|
-
// ...baseServiceEvents.createRecord,
|
|
80
|
-
// {
|
|
81
|
-
// type: "${option:namespace|snake}.custom",
|
|
82
|
-
// source: "custom",
|
|
83
|
-
// entity: "record",
|
|
84
|
-
// operation: "created",
|
|
85
|
-
// entityId: ({ result }) => result?.id
|
|
86
|
-
// }
|
|
87
|
-
// ]
|
|
88
|
-
// };
|
|
89
|
-
|
|
90
|
-
export { createService, serviceEvents };
|
|
62
|
+
export { createService };
|
|
@@ -1,91 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__JSKIT_CRUD_RESOURCE_DATABASE_RUNTIME_IMPORT__
|
|
3
|
-
__JSKIT_CRUD_RESOURCE_VALIDATORS_IMPORT__
|
|
4
|
-
__JSKIT_CRUD_RESOURCE_NORMALIZE_SUPPORT_IMPORT__
|
|
5
|
-
__JSKIT_CRUD_RESOURCE_JSON_IMPORT__
|
|
1
|
+
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
6
2
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const recordOutputSchema = Type.Object(
|
|
10
|
-
{
|
|
11
|
-
__JSKIT_CRUD_RESOURCE_OUTPUT_SCHEMA_PROPERTIES__
|
|
12
|
-
[RESOURCE_LOOKUP_CONTAINER_KEY]: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
13
|
-
},
|
|
14
|
-
{ additionalProperties: false }
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
const createBodySchema = Type.Object(
|
|
18
|
-
{
|
|
19
|
-
__JSKIT_CRUD_RESOURCE_CREATE_SCHEMA_PROPERTIES__
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
additionalProperties: false,
|
|
23
|
-
required: __JSKIT_CRUD_RESOURCE_CREATE_REQUIRED_FIELDS__
|
|
24
|
-
}
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const patchBodySchema = Type.Partial(createBodySchema, {
|
|
28
|
-
additionalProperties: false
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const recordOutputValidator = Object.freeze({
|
|
32
|
-
schema: recordOutputSchema,
|
|
33
|
-
normalize(payload = {}) {
|
|
34
|
-
const source = normalizeObjectInput(payload);
|
|
35
|
-
const normalized = {
|
|
36
|
-
__JSKIT_CRUD_RESOURCE_OUTPUT_NORMALIZATION_LINES__
|
|
37
|
-
};
|
|
38
|
-
if (Object.hasOwn(source, RESOURCE_LOOKUP_CONTAINER_KEY)) {
|
|
39
|
-
normalized[RESOURCE_LOOKUP_CONTAINER_KEY] = source[RESOURCE_LOOKUP_CONTAINER_KEY];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return normalized;
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const listOutputValidator = createCursorListValidator(recordOutputValidator);
|
|
47
|
-
|
|
48
|
-
const createBodyValidator = Object.freeze({
|
|
49
|
-
schema: createBodySchema,
|
|
50
|
-
normalize(payload = {}) {
|
|
51
|
-
const source = normalizeObjectInput(payload);
|
|
52
|
-
const normalized = {};
|
|
53
|
-
|
|
54
|
-
__JSKIT_CRUD_RESOURCE_INPUT_NORMALIZATION_LINES__
|
|
55
|
-
|
|
56
|
-
return normalized;
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const patchBodyValidator = Object.freeze({
|
|
61
|
-
schema: patchBodySchema,
|
|
62
|
-
normalize: createBodyValidator.normalize
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const deleteOutputValidator = Object.freeze({
|
|
66
|
-
schema: Type.Object(
|
|
67
|
-
{
|
|
68
|
-
id: recordIdSchema,
|
|
69
|
-
deleted: Type.Literal(true)
|
|
70
|
-
},
|
|
71
|
-
{ additionalProperties: false }
|
|
72
|
-
),
|
|
73
|
-
normalize(payload = {}) {
|
|
74
|
-
const source = normalizeObjectInput(payload);
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
id: normalizeRecordId(source.id, { fallback: "" }),
|
|
78
|
-
deleted: true
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
const RESOURCE_FIELD_META = [];
|
|
84
|
-
|
|
85
|
-
const resource = {
|
|
3
|
+
const resource = defineCrudResource({
|
|
86
4
|
namespace: "${option:namespace|snake}",
|
|
87
5
|
tableName: __JSKIT_CRUD_TABLE_NAME__,
|
|
88
|
-
|
|
6
|
+
schema: {
|
|
7
|
+
__JSKIT_CRUD_RESOURCE_SCHEMA_PROPERTIES__
|
|
8
|
+
},
|
|
9
|
+
searchSchema: {
|
|
10
|
+
__JSKIT_CRUD_RESOURCE_SEARCH_SCHEMA_LINES__
|
|
11
|
+
},
|
|
12
|
+
defaultSort: __JSKIT_CRUD_RESOURCE_DEFAULT_SORT__,
|
|
13
|
+
autofilter: __JSKIT_CRUD_RESOURCE_AUTOFILTER__,
|
|
89
14
|
messages: {
|
|
90
15
|
validation: "Fix invalid values and try again.",
|
|
91
16
|
saveSuccess: "Record saved.",
|
|
@@ -95,61 +20,11 @@ const resource = {
|
|
|
95
20
|
},
|
|
96
21
|
contract: {
|
|
97
22
|
lookup: {
|
|
98
|
-
containerKey:
|
|
99
|
-
defaultInclude: "*",
|
|
100
|
-
maxDepth: 3
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
operations: {
|
|
104
|
-
list: {
|
|
105
|
-
realtime: {
|
|
106
|
-
events: ["${option:namespace|snake}.record.changed"] // Add more events e.g. for lookup records
|
|
107
|
-
},
|
|
108
|
-
method: "GET",
|
|
109
|
-
outputValidator: listOutputValidator
|
|
110
|
-
},
|
|
111
|
-
view: {
|
|
112
|
-
method: "GET",
|
|
113
|
-
outputValidator: recordOutputValidator
|
|
114
|
-
},
|
|
115
|
-
create: {
|
|
116
|
-
method: "POST",
|
|
117
|
-
bodyValidator: createBodyValidator,
|
|
118
|
-
outputValidator: recordOutputValidator
|
|
119
|
-
},
|
|
120
|
-
patch: {
|
|
121
|
-
method: "PATCH",
|
|
122
|
-
bodyValidator: patchBodyValidator,
|
|
123
|
-
outputValidator: recordOutputValidator
|
|
124
|
-
},
|
|
125
|
-
delete: {
|
|
126
|
-
method: "DELETE",
|
|
127
|
-
outputValidator: deleteOutputValidator
|
|
23
|
+
containerKey: "lookups",
|
|
24
|
+
defaultInclude: "*",
|
|
25
|
+
maxDepth: 3
|
|
128
26
|
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
132
29
|
|
|
133
30
|
export { resource };
|
|
134
|
-
|
|
135
|
-
// @jskit-contract crud.resource.field-meta.${option:namespace|snake}.v1
|
|
136
|
-
void RESOURCE_FIELD_META;
|
|
137
|
-
|
|
138
|
-
// Example 1:n collection hydration:
|
|
139
|
-
// RESOURCE_FIELD_META.push({
|
|
140
|
-
// key: "pets",
|
|
141
|
-
// relation: {
|
|
142
|
-
// kind: "collection",
|
|
143
|
-
// namespace: "pets",
|
|
144
|
-
// foreignKey: "customerId",
|
|
145
|
-
// parentValueKey: "id",
|
|
146
|
-
// hydrateOnList: false, // list: opt-in with include=pets
|
|
147
|
-
// hydrateOnView: true // view: hydrated by default
|
|
148
|
-
// }
|
|
149
|
-
// });
|
|
150
|
-
//
|
|
151
|
-
// To hydrate child lookups too, request nested include paths:
|
|
152
|
-
// - include=pets
|
|
153
|
-
// - include=pets,pets.breedId
|
|
154
|
-
|
|
155
|
-
__JSKIT_CRUD_RESOURCE_FIELD_META_PUSH_LINES__
|