@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,143 +1,90 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
toIsoString
|
|
4
|
-
} from "@jskit-ai/database-runtime/shared";
|
|
5
|
-
import {
|
|
6
|
-
normalizeObjectInput,
|
|
7
|
-
createCursorListValidator,
|
|
8
|
-
recordIdSchema
|
|
9
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
10
|
-
import {
|
|
11
|
-
normalizeText,
|
|
12
|
-
normalizeRecordId,
|
|
13
|
-
normalizeFiniteNumber,
|
|
14
|
-
normalizeIfPresent
|
|
15
|
-
} from "@jskit-ai/kernel/shared/support/normalize";
|
|
1
|
+
import { defineCrudResource } from "@jskit-ai/resource-crud-core/shared/crudResource";
|
|
16
2
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
numberField: Type.Number(),
|
|
25
|
-
createdAt: Type.String({ minLength: 1 }),
|
|
26
|
-
updatedAt: Type.String({ minLength: 1 }),
|
|
27
|
-
[RESOURCE_LOOKUP_CONTAINER_KEY]: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
28
|
-
},
|
|
29
|
-
{ additionalProperties: false }
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const recordBodySchema = Type.Object(
|
|
33
|
-
{
|
|
34
|
-
textField: Type.String({
|
|
3
|
+
const crudResource = defineCrudResource({
|
|
4
|
+
namespace: "crud",
|
|
5
|
+
tableName: "crud",
|
|
6
|
+
schema: {
|
|
7
|
+
textField: {
|
|
8
|
+
type: "string",
|
|
9
|
+
required: true,
|
|
35
10
|
minLength: 1,
|
|
36
11
|
maxLength: 160,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
12
|
+
operations: {
|
|
13
|
+
output: { required: true },
|
|
14
|
+
create: {
|
|
15
|
+
required: true,
|
|
16
|
+
messages: {
|
|
17
|
+
required: "Text field is required.",
|
|
18
|
+
minLength: "Text field is required.",
|
|
19
|
+
maxLength: "Text field must be at most 160 characters.",
|
|
20
|
+
default: "Text field is required."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
patch: {
|
|
24
|
+
required: false,
|
|
25
|
+
messages: {
|
|
26
|
+
minLength: "Text field is required.",
|
|
27
|
+
maxLength: "Text field must be at most 160 characters.",
|
|
28
|
+
default: "Text field is required."
|
|
29
|
+
}
|
|
30
|
+
}
|
|
42
31
|
}
|
|
43
|
-
}
|
|
44
|
-
dateField:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
},
|
|
33
|
+
dateField: {
|
|
34
|
+
type: "dateTime",
|
|
35
|
+
required: true,
|
|
36
|
+
operations: {
|
|
37
|
+
output: { required: true },
|
|
38
|
+
create: {
|
|
39
|
+
required: true,
|
|
40
|
+
messages: {
|
|
41
|
+
required: "Date field is required.",
|
|
42
|
+
default: "Date field is required."
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
patch: {
|
|
46
|
+
required: false,
|
|
47
|
+
messages: {
|
|
48
|
+
default: "Date field is required."
|
|
49
|
+
}
|
|
50
|
+
}
|
|
50
51
|
}
|
|
51
|
-
}
|
|
52
|
-
numberField:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
},
|
|
53
|
+
numberField: {
|
|
54
|
+
type: "number",
|
|
55
|
+
required: true,
|
|
56
|
+
operations: {
|
|
57
|
+
output: { required: true },
|
|
58
|
+
create: {
|
|
59
|
+
required: true,
|
|
60
|
+
messages: {
|
|
61
|
+
required: "Number field is required.",
|
|
62
|
+
default: "Number field must be a valid number."
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
patch: {
|
|
66
|
+
required: false,
|
|
67
|
+
messages: {
|
|
68
|
+
default: "Number field must be a valid number."
|
|
69
|
+
}
|
|
70
|
+
}
|
|
56
71
|
}
|
|
57
|
-
})
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
additionalProperties: false,
|
|
61
|
-
messages: {
|
|
62
|
-
additionalProperties: "Unexpected field.",
|
|
63
|
-
default: "Invalid value."
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const patchBodySchema = Type.Partial(recordBodySchema, { additionalProperties: false });
|
|
69
|
-
|
|
70
|
-
const recordOutputValidator = Object.freeze({
|
|
71
|
-
schema: recordOutputSchema,
|
|
72
|
-
normalize(payload = {}) {
|
|
73
|
-
const source = normalizeObjectInput(payload);
|
|
74
|
-
const normalized = {
|
|
75
|
-
id: normalizeRecordId(source.id, { fallback: "" }),
|
|
76
|
-
textField: normalizeText(source.textField),
|
|
77
|
-
dateField: toIsoString(source.dateField),
|
|
78
|
-
numberField: normalizeFiniteNumber(source.numberField),
|
|
79
|
-
createdAt: normalizeIfPresent(source.createdAt, toIsoString),
|
|
80
|
-
updatedAt: normalizeIfPresent(source.updatedAt, toIsoString)
|
|
81
|
-
};
|
|
82
|
-
if (Object.hasOwn(source, RESOURCE_LOOKUP_CONTAINER_KEY)) {
|
|
83
|
-
normalized[RESOURCE_LOOKUP_CONTAINER_KEY] = source[RESOURCE_LOOKUP_CONTAINER_KEY];
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return normalized;
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const listOutputValidator = createCursorListValidator(recordOutputValidator);
|
|
91
|
-
|
|
92
|
-
const createBodyValidator = Object.freeze({
|
|
93
|
-
schema: recordBodySchema,
|
|
94
|
-
normalize(payload = {}) {
|
|
95
|
-
const source = normalizeObjectInput(payload);
|
|
96
|
-
const normalized = {};
|
|
97
|
-
|
|
98
|
-
if (Object.hasOwn(source, "textField")) {
|
|
99
|
-
normalized.textField = normalizeText(source.textField);
|
|
100
|
-
}
|
|
101
|
-
if (Object.hasOwn(source, "dateField")) {
|
|
102
|
-
normalized.dateField = toIsoString(source.dateField);
|
|
103
|
-
}
|
|
104
|
-
if (Object.hasOwn(source, "numberField")) {
|
|
105
|
-
normalized.numberField = normalizeFiniteNumber(source.numberField);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return normalized;
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
const patchBodyValidator = Object.freeze({
|
|
113
|
-
schema: patchBodySchema,
|
|
114
|
-
normalize: createBodyValidator.normalize
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
const deleteOutputValidator = Object.freeze({
|
|
118
|
-
schema: Type.Object(
|
|
119
|
-
{
|
|
120
|
-
id: recordIdSchema,
|
|
121
|
-
deleted: Type.Literal(true)
|
|
122
72
|
},
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
namespace: "crud",
|
|
139
|
-
tableName: "crud",
|
|
140
|
-
idColumn: "id",
|
|
73
|
+
createdAt: {
|
|
74
|
+
type: "dateTime",
|
|
75
|
+
required: true,
|
|
76
|
+
operations: {
|
|
77
|
+
output: { required: true }
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
updatedAt: {
|
|
81
|
+
type: "dateTime",
|
|
82
|
+
required: true,
|
|
83
|
+
operations: {
|
|
84
|
+
output: { required: true }
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
141
88
|
messages: {
|
|
142
89
|
validation: "Fix invalid CRUD values and try again.",
|
|
143
90
|
saveSuccess: "Record saved.",
|
|
@@ -147,58 +94,11 @@ const crudResource = {
|
|
|
147
94
|
},
|
|
148
95
|
contract: {
|
|
149
96
|
lookup: {
|
|
150
|
-
containerKey:
|
|
151
|
-
defaultInclude: "*",
|
|
152
|
-
maxDepth: 3
|
|
97
|
+
containerKey: "lookups",
|
|
98
|
+
defaultInclude: "*",
|
|
99
|
+
maxDepth: 3
|
|
153
100
|
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
list: {
|
|
157
|
-
realtime: {
|
|
158
|
-
events: ["crud.record.changed"] // Add more events e.g. for lookup records
|
|
159
|
-
},
|
|
160
|
-
method: "GET",
|
|
161
|
-
outputValidator: listOutputValidator
|
|
162
|
-
},
|
|
163
|
-
view: {
|
|
164
|
-
method: "GET",
|
|
165
|
-
outputValidator: recordOutputValidator
|
|
166
|
-
},
|
|
167
|
-
create: {
|
|
168
|
-
method: "POST",
|
|
169
|
-
bodyValidator: createBodyValidator,
|
|
170
|
-
outputValidator: recordOutputValidator
|
|
171
|
-
},
|
|
172
|
-
patch: {
|
|
173
|
-
method: "PATCH",
|
|
174
|
-
bodyValidator: patchBodyValidator,
|
|
175
|
-
outputValidator: recordOutputValidator
|
|
176
|
-
},
|
|
177
|
-
delete: {
|
|
178
|
-
method: "DELETE",
|
|
179
|
-
outputValidator: deleteOutputValidator
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
fieldMeta: CRUD_RESOURCE_FIELD_META
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
void CRUD_RESOURCE_FIELD_META;
|
|
186
|
-
|
|
187
|
-
// Example 1:n collection hydration:
|
|
188
|
-
// CRUD_RESOURCE_FIELD_META.push({
|
|
189
|
-
// key: "pets",
|
|
190
|
-
// relation: {
|
|
191
|
-
// kind: "collection",
|
|
192
|
-
// namespace: "pets",
|
|
193
|
-
// foreignKey: "customerId",
|
|
194
|
-
// parentValueKey: "id",
|
|
195
|
-
// hydrateOnList: false, // list: opt-in with include=pets
|
|
196
|
-
// hydrateOnView: true // view: hydrated by default
|
|
197
|
-
// }
|
|
198
|
-
// });
|
|
199
|
-
//
|
|
200
|
-
// To hydrate child lookups too, request nested include paths:
|
|
201
|
-
// - include=pets
|
|
202
|
-
// - include=pets,pets.breedId
|
|
101
|
+
}
|
|
102
|
+
});
|
|
203
103
|
|
|
204
104
|
export { crudResource };
|
|
@@ -9,8 +9,9 @@ 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",
|
|
12
13
|
"@jskit-ai/realtime",
|
|
13
|
-
"@jskit-ai/
|
|
14
|
+
"@jskit-ai/resource-crud-core",
|
|
14
15
|
],
|
|
15
16
|
capabilities: {
|
|
16
17
|
provides: [
|
|
@@ -20,7 +21,7 @@ export default Object.freeze({
|
|
|
20
21
|
"runtime.actions",
|
|
21
22
|
"runtime.database",
|
|
22
23
|
"auth.policy",
|
|
23
|
-
"
|
|
24
|
+
"json-rest-api.core"
|
|
24
25
|
]
|
|
25
26
|
},
|
|
26
27
|
runtime: {
|
|
@@ -36,10 +37,6 @@ export default Object.freeze({
|
|
|
36
37
|
metadata: {
|
|
37
38
|
apiSummary: {
|
|
38
39
|
surfaces: [
|
|
39
|
-
{
|
|
40
|
-
subpath: "./server/actionIds",
|
|
41
|
-
summary: "App-local CRUD public action identifiers."
|
|
42
|
-
},
|
|
43
40
|
{
|
|
44
41
|
subpath: "./shared",
|
|
45
42
|
summary: "App-local CRUD shared resource."
|
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
2
2
|
import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
|
|
3
|
+
import { createCrudJsonApiServiceEvents } from "@jskit-ai/crud-core/server/serviceEvents";
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
INTERNAL_JSON_REST_API,
|
|
6
|
+
addResourceIfMissing,
|
|
7
|
+
createJsonRestResourceScopeOptions
|
|
8
|
+
} from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
|
|
7
9
|
import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
|
|
10
|
+
import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
|
|
8
11
|
import { createRepository } from "./repository.js";
|
|
9
|
-
import {
|
|
10
|
-
createService,
|
|
11
|
-
serviceEvents
|
|
12
|
-
} from "./service.js";
|
|
12
|
+
import { createService } from "./service.js";
|
|
13
13
|
import { createActions } from "./actions.js";
|
|
14
14
|
import { registerRoutes } from "./registerRoutes.js";
|
|
15
|
+
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
15
16
|
const CRUD_MODULE_CONFIG = Object.freeze({
|
|
16
17
|
namespace: "${option:namespace|snake}",
|
|
17
18
|
surface: __JSKIT_CRUD_SURFACE_ID__,
|
|
18
19
|
ownershipFilter: "__JSKIT_CRUD_RESOLVED_OWNERSHIP_FILTER__",
|
|
19
20
|
relativePath: "/${option:directory-prefix|pathprefix}${option:namespace|kebab}"
|
|
20
21
|
});
|
|
22
|
+
const baseServiceEvents = createCrudJsonApiServiceEvents(CRUD_MODULE_CONFIG.namespace);
|
|
23
|
+
const serviceEvents = {
|
|
24
|
+
...baseServiceEvents
|
|
25
|
+
};
|
|
21
26
|
|
|
22
27
|
function resolveCrudPolicyFromApp(app) {
|
|
23
28
|
return resolveCrudSurfacePolicyFromAppConfig(CRUD_MODULE_CONFIG, resolveAppConfig(app), {
|
|
@@ -28,25 +33,17 @@ function resolveCrudPolicyFromApp(app) {
|
|
|
28
33
|
class ${option:namespace|pascal}Provider {
|
|
29
34
|
static id = "crud.${option:namespace|snake}";
|
|
30
35
|
|
|
31
|
-
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "
|
|
36
|
+
static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "json-rest-api.core"];
|
|
32
37
|
|
|
33
38
|
register(app) {
|
|
34
|
-
if (!app || typeof app.singleton !== "function" || typeof app.service !== "function" || typeof app.actions !== "function") {
|
|
35
|
-
throw new Error("${option:namespace|pascal}Provider requires application singleton()/service()/actions().");
|
|
36
|
-
}
|
|
37
|
-
|
|
38
39
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
39
40
|
|
|
40
41
|
app.singleton("repository.${option:namespace|snake}", (scope) => {
|
|
42
|
+
const api = scope.make(INTERNAL_JSON_REST_API);
|
|
41
43
|
const knex = scope.make("jskit.database.knex");
|
|
42
|
-
return createRepository(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
app.singleton("lookup.${option:namespace|snake}", (scope) => {
|
|
48
|
-
return createCrudLookup(scope.make("repository.${option:namespace|snake}"), {
|
|
49
|
-
ownershipFilter: crudPolicy.ownershipFilter
|
|
44
|
+
return createRepository({
|
|
45
|
+
api,
|
|
46
|
+
knex
|
|
50
47
|
});
|
|
51
48
|
});
|
|
52
49
|
|
|
@@ -77,8 +74,18 @@ class ${option:namespace|pascal}Provider {
|
|
|
77
74
|
);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
|
-
boot(app) {
|
|
77
|
+
async boot(app) {
|
|
81
78
|
const crudPolicy = resolveCrudPolicyFromApp(app);
|
|
79
|
+
const api = app.make(INTERNAL_JSON_REST_API);
|
|
80
|
+
await addResourceIfMissing(
|
|
81
|
+
api,
|
|
82
|
+
__JSKIT_CRUD_JSONREST_SCOPE_NAME__,
|
|
83
|
+
createJsonRestResourceScopeOptions(resource, {
|
|
84
|
+
writeSerializers: {
|
|
85
|
+
"datetime-utc": toDatabaseDateTimeUtc
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
);
|
|
82
89
|
registerRoutes(app, {
|
|
83
90
|
routeOwnershipFilter: crudPolicy.ownershipFilter,
|
|
84
91
|
routeSurface: crudPolicy.surfaceId,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
composeSchemaDefinitions,
|
|
2
3
|
recordIdParamsValidator
|
|
3
4
|
} from "@jskit-ai/kernel/shared/validators";
|
|
4
5
|
import {
|
|
@@ -8,130 +9,117 @@ import {
|
|
|
8
9
|
createCrudParentFilterQueryValidator
|
|
9
10
|
} from "@jskit-ai/crud-core/server/listQueryValidators";
|
|
10
11
|
import { resource } from "../shared/${option:namespace|singular|camel}Resource.js";
|
|
11
|
-
import { actionIds } from "./actionIds.js";
|
|
12
|
-
import { LIST_CONFIG } from "./listConfig.js";
|
|
13
12
|
__JSKIT_CRUD_ACTION_WORKSPACE_VALIDATOR_IMPORT__
|
|
14
13
|
|
|
15
|
-
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator(
|
|
14
|
+
const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator({
|
|
15
|
+
orderBy: resource.defaultSort
|
|
16
|
+
});
|
|
16
17
|
const listParentFilterQueryValidator = createCrudParentFilterQueryValidator(resource);
|
|
17
18
|
__JSKIT_CRUD_ACTION_PERMISSION_SUPPORT__
|
|
18
19
|
|
|
19
|
-
function
|
|
20
|
-
const normalizedSurface = String(surface || "").trim().toLowerCase();
|
|
21
|
-
if (!normalizedSurface) {
|
|
22
|
-
throw new TypeError("createActions requires a non-empty surface.");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return normalizedSurface;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function createActions({ surface = "" } = {}) {
|
|
29
|
-
const actionSurface = requireActionSurface(surface);
|
|
30
|
-
|
|
20
|
+
function createActions({ surface } = {}) {
|
|
31
21
|
return Object.freeze([
|
|
32
22
|
{
|
|
33
|
-
id:
|
|
23
|
+
id: "crud.${option:namespace|snake}.list",
|
|
34
24
|
version: 1,
|
|
35
25
|
kind: "query",
|
|
36
26
|
channels: ["api", "automation", "internal"],
|
|
37
|
-
surfaces: [
|
|
27
|
+
surfaces: [surface],
|
|
38
28
|
permission: __JSKIT_CRUD_LIST_ACTION_PERMISSION__,
|
|
39
|
-
|
|
40
|
-
|
|
29
|
+
input: __JSKIT_CRUD_LIST_ACTION_INPUT__,
|
|
30
|
+
output: null,
|
|
41
31
|
idempotency: "none",
|
|
42
32
|
audit: {
|
|
43
|
-
actionName:
|
|
33
|
+
actionName: "crud.${option:namespace|snake}.list"
|
|
44
34
|
},
|
|
45
35
|
observability: {},
|
|
46
36
|
async execute(input, context, deps) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
const { workspaceSlug, ...query } = input || {};
|
|
38
|
+
return deps.${option:namespace|camel}Service.queryDocuments(query, {
|
|
39
|
+
context
|
|
50
40
|
});
|
|
51
41
|
}
|
|
52
42
|
},
|
|
53
43
|
{
|
|
54
|
-
id:
|
|
44
|
+
id: "crud.${option:namespace|snake}.view",
|
|
55
45
|
version: 1,
|
|
56
46
|
kind: "query",
|
|
57
47
|
channels: ["api", "automation", "internal"],
|
|
58
|
-
surfaces: [
|
|
48
|
+
surfaces: [surface],
|
|
59
49
|
permission: __JSKIT_CRUD_VIEW_ACTION_PERMISSION__,
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
input: __JSKIT_CRUD_VIEW_ACTION_INPUT__,
|
|
51
|
+
output: null,
|
|
62
52
|
idempotency: "none",
|
|
63
53
|
audit: {
|
|
64
|
-
actionName:
|
|
54
|
+
actionName: "crud.${option:namespace|snake}.view"
|
|
65
55
|
},
|
|
66
56
|
observability: {},
|
|
67
57
|
async execute(input, context, deps) {
|
|
68
|
-
return deps.${option:namespace|camel}Service.
|
|
58
|
+
return deps.${option:namespace|camel}Service.getDocumentById(input.recordId, {
|
|
69
59
|
context,
|
|
70
|
-
visibilityContext: context?.visibilityContext,
|
|
71
60
|
include: input.include
|
|
72
61
|
});
|
|
73
62
|
}
|
|
74
63
|
},
|
|
75
64
|
{
|
|
76
|
-
id:
|
|
65
|
+
id: "crud.${option:namespace|snake}.create",
|
|
77
66
|
version: 1,
|
|
78
67
|
kind: "command",
|
|
79
68
|
channels: ["api", "automation", "internal"],
|
|
80
|
-
surfaces: [
|
|
69
|
+
surfaces: [surface],
|
|
81
70
|
permission: __JSKIT_CRUD_CREATE_ACTION_PERMISSION__,
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
input: __JSKIT_CRUD_CREATE_ACTION_INPUT__,
|
|
72
|
+
output: null,
|
|
84
73
|
idempotency: "optional",
|
|
85
74
|
audit: {
|
|
86
|
-
actionName:
|
|
75
|
+
actionName: "crud.${option:namespace|snake}.create"
|
|
87
76
|
},
|
|
88
77
|
observability: {},
|
|
89
78
|
async execute(input, context, deps) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
79
|
+
const { workspaceSlug, ...payload } = input || {};
|
|
80
|
+
return deps.${option:namespace|camel}Service.createDocument(payload, {
|
|
81
|
+
context
|
|
93
82
|
});
|
|
94
83
|
}
|
|
95
84
|
},
|
|
96
85
|
{
|
|
97
|
-
id:
|
|
86
|
+
id: "crud.${option:namespace|snake}.update",
|
|
98
87
|
version: 1,
|
|
99
88
|
kind: "command",
|
|
100
89
|
channels: ["api", "automation", "internal"],
|
|
101
|
-
surfaces: [
|
|
90
|
+
surfaces: [surface],
|
|
102
91
|
permission: __JSKIT_CRUD_UPDATE_ACTION_PERMISSION__,
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
input: __JSKIT_CRUD_UPDATE_ACTION_INPUT__,
|
|
93
|
+
output: null,
|
|
105
94
|
idempotency: "optional",
|
|
106
95
|
audit: {
|
|
107
|
-
actionName:
|
|
96
|
+
actionName: "crud.${option:namespace|snake}.update"
|
|
108
97
|
},
|
|
109
98
|
observability: {},
|
|
110
99
|
async execute(input, context, deps) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
100
|
+
const { workspaceSlug, recordId, ...patch } = input || {};
|
|
101
|
+
return deps.${option:namespace|camel}Service.patchDocumentById(recordId, patch, {
|
|
102
|
+
context
|
|
114
103
|
});
|
|
115
104
|
}
|
|
116
105
|
},
|
|
117
106
|
{
|
|
118
|
-
id:
|
|
107
|
+
id: "crud.${option:namespace|snake}.delete",
|
|
119
108
|
version: 1,
|
|
120
109
|
kind: "command",
|
|
121
110
|
channels: ["api", "automation", "internal"],
|
|
122
|
-
surfaces: [
|
|
111
|
+
surfaces: [surface],
|
|
123
112
|
permission: __JSKIT_CRUD_DELETE_ACTION_PERMISSION__,
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
input: __JSKIT_CRUD_DELETE_ACTION_INPUT__,
|
|
114
|
+
output: null,
|
|
126
115
|
idempotency: "optional",
|
|
127
116
|
audit: {
|
|
128
|
-
actionName:
|
|
117
|
+
actionName: "crud.${option:namespace|snake}.delete"
|
|
129
118
|
},
|
|
130
119
|
observability: {},
|
|
131
120
|
async execute(input, context, deps) {
|
|
132
|
-
return deps.${option:namespace|camel}Service.
|
|
133
|
-
context
|
|
134
|
-
visibilityContext: context?.visibilityContext
|
|
121
|
+
return deps.${option:namespace|camel}Service.deleteDocumentById(input.recordId, {
|
|
122
|
+
context
|
|
135
123
|
});
|
|
136
124
|
}
|
|
137
125
|
}
|