@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
|
@@ -36,7 +36,7 @@ function findRoute(routes, method, path) {
|
|
|
36
36
|
return routes.find((route) => route.method === method && route.path === path) || null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
test("crud routes build create/update action input
|
|
39
|
+
test("crud routes build flattened create/update action input", async () => {
|
|
40
40
|
const registeredRoutes = [];
|
|
41
41
|
const router = {
|
|
42
42
|
register(method, path, route, handler) {
|
|
@@ -100,12 +100,14 @@ test("crud routes build create/update action input with explicit payload and pat
|
|
|
100
100
|
|
|
101
101
|
assert.deepEqual(calls[0].input, {
|
|
102
102
|
workspaceSlug: "acme",
|
|
103
|
-
|
|
103
|
+
textField: "A",
|
|
104
|
+
dateField: "2026-03-11",
|
|
105
|
+
numberField: 2
|
|
104
106
|
});
|
|
105
107
|
assert.deepEqual(calls[1].input, {
|
|
106
108
|
workspaceSlug: "acme",
|
|
107
109
|
recordId: 12,
|
|
108
|
-
|
|
110
|
+
textField: "Renamed"
|
|
109
111
|
});
|
|
110
112
|
});
|
|
111
113
|
|
|
@@ -138,7 +140,7 @@ test("crud routes omit workspaceSlug for non-workspace calls and apply configure
|
|
|
138
140
|
const createRoute = findRoute(registeredRoutes, "POST", "/api/customers");
|
|
139
141
|
assert.ok(createRoute);
|
|
140
142
|
assert.equal(createRoute.route.surface, "console");
|
|
141
|
-
assert.equal(createRoute.route.
|
|
143
|
+
assert.equal(createRoute.route.params, undefined);
|
|
142
144
|
|
|
143
145
|
const calls = [];
|
|
144
146
|
const executeAction = async (payload) => {
|
|
@@ -158,7 +160,9 @@ test("crud routes omit workspaceSlug for non-workspace calls and apply configure
|
|
|
158
160
|
);
|
|
159
161
|
|
|
160
162
|
assert.deepEqual(calls[0].input, {
|
|
161
|
-
|
|
163
|
+
textField: "A",
|
|
164
|
+
dateField: "2026-03-11",
|
|
165
|
+
numberField: 2
|
|
162
166
|
});
|
|
163
167
|
});
|
|
164
168
|
|
|
@@ -195,9 +199,74 @@ test("crud non-workspace record routes validate only recordId params", () => {
|
|
|
195
199
|
assert.ok(viewRoute);
|
|
196
200
|
assert.ok(updateRoute);
|
|
197
201
|
assert.ok(deleteRoute);
|
|
198
|
-
assert.equal(viewRoute.route.
|
|
199
|
-
assert.equal(updateRoute.route.
|
|
200
|
-
assert.equal(deleteRoute.route.
|
|
202
|
+
assert.equal(viewRoute.route.params, recordIdParamsValidator);
|
|
203
|
+
assert.equal(updateRoute.route.params, recordIdParamsValidator);
|
|
204
|
+
assert.equal(deleteRoute.route.params, recordIdParamsValidator);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("crud routes register JSON:API transport contracts and delete replies with 204", async () => {
|
|
208
|
+
const registeredRoutes = [];
|
|
209
|
+
const router = {
|
|
210
|
+
register(method, path, route, handler) {
|
|
211
|
+
registeredRoutes.push({
|
|
212
|
+
method,
|
|
213
|
+
path,
|
|
214
|
+
route,
|
|
215
|
+
handler
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const app = {
|
|
220
|
+
make(token) {
|
|
221
|
+
if (token !== "jskit.http.router") {
|
|
222
|
+
throw new Error(`Unexpected token: ${String(token)}`);
|
|
223
|
+
}
|
|
224
|
+
return router;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
registerWorkspaceRoutes(app, {
|
|
229
|
+
routeRelativePath: "/customers"
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const workspaceRouteBase = resolveScopedApiBasePath({
|
|
233
|
+
routeBase: "/w/:workspaceSlug",
|
|
234
|
+
relativePath: "/customers",
|
|
235
|
+
strictParams: false
|
|
236
|
+
});
|
|
237
|
+
const listRoute = findRoute(registeredRoutes, "GET", workspaceRouteBase);
|
|
238
|
+
const viewRoute = findRoute(registeredRoutes, "GET", `${workspaceRouteBase}/:recordId`);
|
|
239
|
+
const createRoute = findRoute(registeredRoutes, "POST", workspaceRouteBase);
|
|
240
|
+
const updateRoute = findRoute(registeredRoutes, "PATCH", `${workspaceRouteBase}/:recordId`);
|
|
241
|
+
const deleteRoute = findRoute(registeredRoutes, "DELETE", `${workspaceRouteBase}/:recordId`);
|
|
242
|
+
|
|
243
|
+
assert.ok(listRoute);
|
|
244
|
+
assert.ok(viewRoute);
|
|
245
|
+
assert.ok(createRoute);
|
|
246
|
+
assert.ok(updateRoute);
|
|
247
|
+
assert.ok(deleteRoute);
|
|
248
|
+
assert.equal(listRoute.route.transport?.kind, "jsonapi-resource");
|
|
249
|
+
assert.equal(viewRoute.route.transport?.kind, "jsonapi-resource");
|
|
250
|
+
assert.equal(createRoute.route.transport?.kind, "jsonapi-resource");
|
|
251
|
+
assert.equal(updateRoute.route.transport?.kind, "jsonapi-resource");
|
|
252
|
+
assert.equal(deleteRoute.route.transport?.kind, "jsonapi-resource");
|
|
253
|
+
assert.ok(listRoute.route.advanced?.fastifySchema?.querystring);
|
|
254
|
+
assert.ok(viewRoute.route.advanced?.fastifySchema?.querystring);
|
|
255
|
+
assert.ok(createRoute.route.advanced?.fastifySchema?.body);
|
|
256
|
+
assert.ok(updateRoute.route.advanced?.fastifySchema?.body);
|
|
257
|
+
|
|
258
|
+
const reply = createReplyDouble();
|
|
259
|
+
await deleteRoute.handler(
|
|
260
|
+
{
|
|
261
|
+
input: {
|
|
262
|
+
params: { workspaceSlug: "acme", recordId: 7 }
|
|
263
|
+
},
|
|
264
|
+
executeAction: async () => ({ id: 7 })
|
|
265
|
+
},
|
|
266
|
+
reply
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
assert.equal(reply.statusCode, 204);
|
|
201
270
|
});
|
|
202
271
|
|
|
203
272
|
test("crud routes validate route ownership filter values before registering visibility", () => {
|
|
@@ -7,7 +7,7 @@ function resolveTemplatePath(relativePath) {
|
|
|
7
7
|
return fileURLToPath(new URL(`../templates/${relativePath}`, import.meta.url));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
test("actions template
|
|
10
|
+
test("actions template does not emit resource action output validators", async () => {
|
|
11
11
|
const actionsTemplate = await readFile(
|
|
12
12
|
resolveTemplatePath("src/local-package/server/actions.js"),
|
|
13
13
|
"utf8"
|
|
@@ -15,11 +15,11 @@ test("actions template uses resource symbol for view output validator", async ()
|
|
|
15
15
|
|
|
16
16
|
assert.match(
|
|
17
17
|
actionsTemplate,
|
|
18
|
-
/
|
|
18
|
+
/output:\s*null,/
|
|
19
19
|
);
|
|
20
20
|
assert.doesNotMatch(
|
|
21
21
|
actionsTemplate,
|
|
22
|
-
|
|
22
|
+
/createJsonApiResourceActionOutputValidator/
|
|
23
23
|
);
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -38,3 +38,19 @@ test("shared index template re-exports standardized resource symbol", async () =
|
|
|
38
38
|
/export\s*\{\s*\$\{option:namespace\|singular\|camel\}Resource\s*\}/s
|
|
39
39
|
);
|
|
40
40
|
});
|
|
41
|
+
|
|
42
|
+
test("shared resource template uses defineCrudResource export", async () => {
|
|
43
|
+
const sharedResourceTemplate = await readFile(
|
|
44
|
+
resolveTemplatePath("src/local-package/shared/crudResource.js"),
|
|
45
|
+
"utf8"
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
assert.match(
|
|
49
|
+
sharedResourceTemplate,
|
|
50
|
+
/import\s*\{\s*defineCrudResource\s*\}\s*from\s*"@jskit-ai\/resource-crud-core\/shared\/crudResource";/s
|
|
51
|
+
);
|
|
52
|
+
assert.doesNotMatch(
|
|
53
|
+
sharedResourceTemplate,
|
|
54
|
+
/from\s*"json-rest-schema";/
|
|
55
|
+
);
|
|
56
|
+
});
|
|
@@ -64,38 +64,97 @@ function buildTemplateReplacements({
|
|
|
64
64
|
["${option:namespace|camel}", CRUD_NAMESPACE.camel],
|
|
65
65
|
["${option:namespace|singular|camel}", CRUD_NAMESPACE.singularCamel],
|
|
66
66
|
["${option:namespace|pascal}", CRUD_NAMESPACE.pascal],
|
|
67
|
+
["__JSKIT_CRUD_TABLE_NAME__", JSON.stringify("customers")],
|
|
67
68
|
["__JSKIT_CRUD_ID_COLUMN__", JSON.stringify("id")],
|
|
68
69
|
["__JSKIT_CRUD_SURFACE_ID__", JSON.stringify(surfaceId)],
|
|
69
70
|
["__JSKIT_CRUD_ACTION_PERMISSION_SUPPORT__", actionPermissionSupport],
|
|
70
71
|
["__JSKIT_CRUD_ACTION_WORKSPACE_VALIDATOR_IMPORT__", actionWorkspaceValidatorImport],
|
|
72
|
+
["__JSKIT_CRUD_LIST_ACTION_INPUT__", surfaceRequiresWorkspace
|
|
73
|
+
? [
|
|
74
|
+
"composeSchemaDefinitions([",
|
|
75
|
+
" workspaceSlugParamsValidator,",
|
|
76
|
+
" listCursorPaginationQueryValidator,",
|
|
77
|
+
" listSearchQueryValidator,",
|
|
78
|
+
" listParentFilterQueryValidator,",
|
|
79
|
+
" lookupIncludeQueryValidator,",
|
|
80
|
+
"])"
|
|
81
|
+
].join("\n")
|
|
82
|
+
: [
|
|
83
|
+
"composeSchemaDefinitions([",
|
|
84
|
+
" listCursorPaginationQueryValidator,",
|
|
85
|
+
" listSearchQueryValidator,",
|
|
86
|
+
" listParentFilterQueryValidator,",
|
|
87
|
+
" lookupIncludeQueryValidator,",
|
|
88
|
+
"])"
|
|
89
|
+
].join("\n")],
|
|
90
|
+
["__JSKIT_CRUD_VIEW_ACTION_INPUT__", surfaceRequiresWorkspace
|
|
91
|
+
? [
|
|
92
|
+
"composeSchemaDefinitions([",
|
|
93
|
+
" workspaceSlugParamsValidator,",
|
|
94
|
+
" recordIdParamsValidator,",
|
|
95
|
+
" lookupIncludeQueryValidator,",
|
|
96
|
+
"])"
|
|
97
|
+
].join("\n")
|
|
98
|
+
: [
|
|
99
|
+
"composeSchemaDefinitions([",
|
|
100
|
+
" recordIdParamsValidator,",
|
|
101
|
+
" lookupIncludeQueryValidator,",
|
|
102
|
+
"])"
|
|
103
|
+
].join("\n")],
|
|
104
|
+
["__JSKIT_CRUD_CREATE_ACTION_INPUT__", surfaceRequiresWorkspace
|
|
105
|
+
? [
|
|
106
|
+
"composeSchemaDefinitions([",
|
|
107
|
+
" workspaceSlugParamsValidator,",
|
|
108
|
+
" resource.operations.create.body,",
|
|
109
|
+
'], {',
|
|
110
|
+
' mode: "create"',
|
|
111
|
+
"})"
|
|
112
|
+
].join("\n")
|
|
113
|
+
: "resource.operations.create.body"],
|
|
114
|
+
["__JSKIT_CRUD_UPDATE_ACTION_INPUT__", surfaceRequiresWorkspace
|
|
115
|
+
? [
|
|
116
|
+
"composeSchemaDefinitions([",
|
|
117
|
+
" workspaceSlugParamsValidator,",
|
|
118
|
+
" recordIdParamsValidator,",
|
|
119
|
+
" resource.operations.patch.body,",
|
|
120
|
+
"])"
|
|
121
|
+
].join("\n")
|
|
122
|
+
: [
|
|
123
|
+
"composeSchemaDefinitions([",
|
|
124
|
+
" recordIdParamsValidator,",
|
|
125
|
+
" resource.operations.patch.body,",
|
|
126
|
+
"])"
|
|
127
|
+
].join("\n")],
|
|
128
|
+
["__JSKIT_CRUD_DELETE_ACTION_INPUT__", surfaceRequiresWorkspace
|
|
129
|
+
? [
|
|
130
|
+
"composeSchemaDefinitions([",
|
|
131
|
+
" workspaceSlugParamsValidator,",
|
|
132
|
+
" recordIdParamsValidator,",
|
|
133
|
+
"])"
|
|
134
|
+
].join("\n")
|
|
135
|
+
: "recordIdParamsValidator"],
|
|
71
136
|
["__JSKIT_CRUD_LIST_ACTION_PERMISSION__", listActionPermission],
|
|
72
|
-
["__JSKIT_CRUD_LIST_ACTION_INPUT_VALIDATOR__", surfaceRequiresWorkspace
|
|
73
|
-
? "[workspaceSlugParamsValidator, listCursorPaginationQueryValidator, listSearchQueryValidator, listParentFilterQueryValidator, lookupIncludeQueryValidator]"
|
|
74
|
-
: "[listCursorPaginationQueryValidator, listSearchQueryValidator, listParentFilterQueryValidator, lookupIncludeQueryValidator]"],
|
|
75
137
|
["__JSKIT_CRUD_VIEW_ACTION_PERMISSION__", viewActionPermission],
|
|
76
|
-
["__JSKIT_CRUD_VIEW_ACTION_INPUT_VALIDATOR__", surfaceRequiresWorkspace
|
|
77
|
-
? "[workspaceSlugParamsValidator, recordIdParamsValidator, lookupIncludeQueryValidator]"
|
|
78
|
-
: "[recordIdParamsValidator, lookupIncludeQueryValidator]"],
|
|
79
138
|
["__JSKIT_CRUD_CREATE_ACTION_PERMISSION__", createActionPermission],
|
|
80
|
-
["__JSKIT_CRUD_CREATE_ACTION_INPUT_VALIDATOR__", surfaceRequiresWorkspace
|
|
81
|
-
? '[workspaceSlugParamsValidator, { payload: resource.operations.create.bodyValidator }]'
|
|
82
|
-
: "{ payload: resource.operations.create.bodyValidator }"],
|
|
83
139
|
["__JSKIT_CRUD_UPDATE_ACTION_PERMISSION__", updateActionPermission],
|
|
84
|
-
["__JSKIT_CRUD_UPDATE_ACTION_INPUT_VALIDATOR__", surfaceRequiresWorkspace
|
|
85
|
-
? '[workspaceSlugParamsValidator, recordIdParamsValidator, { patch: resource.operations.patch.bodyValidator }]'
|
|
86
|
-
: "[recordIdParamsValidator, { patch: resource.operations.patch.bodyValidator }]"],
|
|
87
140
|
["__JSKIT_CRUD_DELETE_ACTION_PERMISSION__", deleteActionPermission],
|
|
88
|
-
["__JSKIT_CRUD_DELETE_ACTION_INPUT_VALIDATOR__", surfaceRequiresWorkspace
|
|
89
|
-
? "[workspaceSlugParamsValidator, recordIdParamsValidator]"
|
|
90
|
-
: "recordIdParamsValidator"],
|
|
91
141
|
["__JSKIT_CRUD_ROUTE_SURFACE_REQUIRES_WORKSPACE__", String(surfaceRequiresWorkspace === true)],
|
|
92
142
|
["__JSKIT_CRUD_ROUTE_BASE__", JSON.stringify(surfaceRequiresWorkspace ? "/w/:workspaceSlug" : "/")],
|
|
93
143
|
["__JSKIT_CRUD_ROUTE_WORKSPACE_SUPPORT_IMPORTS__", routeWorkspaceSupportImports],
|
|
94
|
-
["
|
|
95
|
-
["
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
144
|
+
["__JSKIT_CRUD_ROUTE_CONTRACTS_RESOURCE_ARGS__", surfaceRequiresWorkspace ? ",\n routeParamsValidator" : ""],
|
|
145
|
+
["__JSKIT_CRUD_ROUTE_VALIDATOR_CONSTANTS__", surfaceRequiresWorkspace
|
|
146
|
+
? [
|
|
147
|
+
"const recordRouteParamsValidator = composeSchemaDefinitions([",
|
|
148
|
+
" routeParamsValidator,",
|
|
149
|
+
" recordIdParamsValidator",
|
|
150
|
+
"]);"
|
|
151
|
+
].join("\n")
|
|
152
|
+
: ""],
|
|
153
|
+
["__JSKIT_CRUD_LIST_ROUTE_PARAMS_VALIDATOR_LINE__", surfaceRequiresWorkspace ? " params: routeParamsValidator," : ""],
|
|
154
|
+
["__JSKIT_CRUD_VIEW_ROUTE_PARAMS_VALIDATOR_LINE__", " params: recordRouteParamsValidator,"],
|
|
155
|
+
["__JSKIT_CRUD_CREATE_ROUTE_PARAMS_VALIDATOR_LINE__", surfaceRequiresWorkspace ? " params: routeParamsValidator," : ""],
|
|
156
|
+
["__JSKIT_CRUD_UPDATE_ROUTE_PARAMS_VALIDATOR_LINE__", " params: recordRouteParamsValidator,"],
|
|
157
|
+
["__JSKIT_CRUD_DELETE_ROUTE_PARAMS_VALIDATOR_LINE__", " params: recordRouteParamsValidator,"],
|
|
99
158
|
["__JSKIT_CRUD_LIST_ROUTE_INPUT_LINES__", surfaceRequiresWorkspace
|
|
100
159
|
? [" ...buildWorkspaceInputFromRouteParams(request.input.params),", " ...(request.input.query || {})"].join("\n")
|
|
101
160
|
: " ...(request.input.query || {})"],
|
|
@@ -112,18 +171,18 @@ function buildTemplateReplacements({
|
|
|
112
171
|
["__JSKIT_CRUD_CREATE_ROUTE_INPUT_LINES__", surfaceRequiresWorkspace
|
|
113
172
|
? [
|
|
114
173
|
" ...buildWorkspaceInputFromRouteParams(request.input.params),",
|
|
115
|
-
"
|
|
174
|
+
" ...(request.input.body || {})"
|
|
116
175
|
].join("\n")
|
|
117
|
-
: "
|
|
176
|
+
: " ...(request.input.body || {})"],
|
|
118
177
|
["__JSKIT_CRUD_UPDATE_ROUTE_INPUT_LINES__", surfaceRequiresWorkspace
|
|
119
178
|
? [
|
|
120
179
|
" ...buildWorkspaceInputFromRouteParams(request.input.params),",
|
|
121
180
|
" recordId: request.input.params.recordId,",
|
|
122
|
-
"
|
|
181
|
+
" ...(request.input.body || {})"
|
|
123
182
|
].join("\n")
|
|
124
183
|
: [
|
|
125
184
|
" recordId: request.input.params.recordId,",
|
|
126
|
-
"
|
|
185
|
+
" ...(request.input.body || {})"
|
|
127
186
|
].join("\n")],
|
|
128
187
|
["__JSKIT_CRUD_DELETE_ROUTE_INPUT_LINES__", surfaceRequiresWorkspace
|
|
129
188
|
? [
|
|
@@ -131,14 +190,41 @@ function buildTemplateReplacements({
|
|
|
131
190
|
" recordId: request.input.params.recordId"
|
|
132
191
|
].join("\n")
|
|
133
192
|
: " recordId: request.input.params.recordId"],
|
|
134
|
-
[
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
193
|
+
["__JSKIT_CRUD_JSONREST_SCOPE_NAME__", JSON.stringify("customers")],
|
|
194
|
+
["__JSKIT_CRUD_RESOURCE_AUTOFILTER__", JSON.stringify(surfaceRequiresWorkspace ? "workspace" : "public")],
|
|
195
|
+
["__JSKIT_CRUD_RESOURCE_SEARCH_SCHEMA_LINES__", [
|
|
196
|
+
' id: { type: "id", actualField: "id" },',
|
|
197
|
+
' q: { type: "string", oneOf: ["name"], filterOperator: "like", splitBy: " ", matchAll: true },'
|
|
198
|
+
].join("\n")],
|
|
199
|
+
["__JSKIT_CRUD_RESOURCE_DEFAULT_SORT__", '["-createdAt"]'],
|
|
200
|
+
["__JSKIT_CRUD_RESOURCE_SCHEMA_PROPERTIES__", [
|
|
201
|
+
" name: {",
|
|
202
|
+
' type: "string",',
|
|
203
|
+
" required: true,",
|
|
204
|
+
" search: true,",
|
|
205
|
+
" operations: {",
|
|
206
|
+
' output: { required: true },',
|
|
207
|
+
' create: { required: true },',
|
|
208
|
+
' patch: { required: false }',
|
|
209
|
+
" }",
|
|
210
|
+
" },",
|
|
211
|
+
" createdAt: {",
|
|
212
|
+
' type: "dateTime",',
|
|
213
|
+
' default: "now()",',
|
|
214
|
+
' storage: { writeSerializer: "datetime-utc" },',
|
|
215
|
+
" operations: {",
|
|
216
|
+
' output: { required: true }',
|
|
217
|
+
" }",
|
|
218
|
+
" },",
|
|
219
|
+
" updatedAt: {",
|
|
220
|
+
' type: "dateTime",',
|
|
221
|
+
' default: "now()",',
|
|
222
|
+
' storage: { writeSerializer: "datetime-utc" },',
|
|
223
|
+
" operations: {",
|
|
224
|
+
' output: { required: true }',
|
|
225
|
+
" }",
|
|
226
|
+
" },"
|
|
227
|
+
].join("\n")]
|
|
142
228
|
]);
|
|
143
229
|
}
|
|
144
230
|
|
|
@@ -151,93 +237,50 @@ function applyTemplateReplacements(sourceText = "", options = {}) {
|
|
|
151
237
|
}
|
|
152
238
|
|
|
153
239
|
function buildResourceStubSource() {
|
|
154
|
-
return `
|
|
155
|
-
schema: {
|
|
156
|
-
properties: {
|
|
157
|
-
id: {},
|
|
158
|
-
name: {}
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
normalize(payload = {}) {
|
|
162
|
-
return payload;
|
|
163
|
-
}
|
|
164
|
-
});
|
|
240
|
+
return `import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
165
241
|
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
name: {},
|
|
170
|
-
contactId: {}
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
normalize(payload = {}) {
|
|
174
|
-
return payload;
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const patchBodyValidator = Object.freeze({
|
|
242
|
+
const resource = defineCrudResource({
|
|
243
|
+
namespace: "customers",
|
|
244
|
+
tableName: "customers",
|
|
179
245
|
schema: {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
246
|
+
name: {
|
|
247
|
+
type: "string",
|
|
248
|
+
required: true,
|
|
249
|
+
search: true,
|
|
250
|
+
operations: {
|
|
251
|
+
output: { required: true },
|
|
252
|
+
create: { required: true },
|
|
253
|
+
patch: { required: false }
|
|
183
254
|
}
|
|
184
255
|
},
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
operations: {
|
|
193
|
-
list: {
|
|
194
|
-
outputValidator: Object.freeze({
|
|
195
|
-
schema: {
|
|
196
|
-
properties: {
|
|
197
|
-
items: {},
|
|
198
|
-
nextCursor: {}
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
normalize(payload = {}) {
|
|
202
|
-
return payload;
|
|
203
|
-
}
|
|
204
|
-
})
|
|
205
|
-
},
|
|
206
|
-
view: {
|
|
207
|
-
outputValidator: recordOutputValidator
|
|
256
|
+
contactId: {
|
|
257
|
+
type: "id",
|
|
258
|
+
nullable: true,
|
|
259
|
+
relation: {
|
|
260
|
+
kind: "lookup",
|
|
261
|
+
namespace: "contacts",
|
|
262
|
+
valueKey: "id"
|
|
208
263
|
},
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
patch: {
|
|
214
|
-
bodyValidator: patchBodyValidator,
|
|
215
|
-
outputValidator: recordOutputValidator
|
|
216
|
-
},
|
|
217
|
-
delete: {
|
|
218
|
-
outputValidator: Object.freeze({
|
|
219
|
-
schema: {
|
|
220
|
-
properties: {
|
|
221
|
-
id: {},
|
|
222
|
-
deleted: {}
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
normalize(payload = {}) {
|
|
226
|
-
return payload;
|
|
227
|
-
}
|
|
228
|
-
})
|
|
264
|
+
operations: {
|
|
265
|
+
output: { required: true },
|
|
266
|
+
create: { required: false },
|
|
267
|
+
patch: { required: false }
|
|
229
268
|
}
|
|
269
|
+
}
|
|
230
270
|
},
|
|
231
|
-
|
|
232
|
-
{
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
271
|
+
searchSchema: {
|
|
272
|
+
id: { type: "id", actualField: "id" },
|
|
273
|
+
q: { type: "string", oneOf: ["name"], filterOperator: "like", splitBy: " ", matchAll: true }
|
|
274
|
+
},
|
|
275
|
+
defaultSort: ["-createdAt"],
|
|
276
|
+
autofilter: "workspace",
|
|
277
|
+
contract: {
|
|
278
|
+
lookup: {
|
|
279
|
+
containerKey: "lookups",
|
|
280
|
+
defaultInclude: "*",
|
|
281
|
+
maxDepth: 3
|
|
239
282
|
}
|
|
240
|
-
|
|
283
|
+
}
|
|
241
284
|
});
|
|
242
285
|
|
|
243
286
|
export { resource };
|
|
@@ -266,7 +309,7 @@ async function createTemplateServerFixture(options = {}) {
|
|
|
266
309
|
);
|
|
267
310
|
await writeFile(path.join(sharedRoot, "customerResource.js"), buildResourceStubSource(), "utf8");
|
|
268
311
|
|
|
269
|
-
for (const fileName of ["
|
|
312
|
+
for (const fileName of ["actions.js", "registerRoutes.js", "repository.js", "service.js"]) {
|
|
270
313
|
await renderServerTemplateFile(serverRoot, fileName, options);
|
|
271
314
|
}
|
|
272
315
|
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const actionIds = Object.freeze({
|
|
2
|
-
list: "crud.${option:namespace|snake}.list",
|
|
3
|
-
view: "crud.${option:namespace|snake}.view",
|
|
4
|
-
create: "crud.${option:namespace|snake}.create",
|
|
5
|
-
update: "crud.${option:namespace|snake}.update",
|
|
6
|
-
delete: "crud.${option:namespace|snake}.delete"
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
export { actionIds };
|