@jskit-ai/crud-server-generator 0.1.26 → 0.1.28
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 +19 -14
- package/package.json +9 -7
- package/src/server/{CrudServiceProvider.js → CrudProvider.js} +2 -2
- package/src/server/buildTemplateContext.js +278 -32
- package/src/server/subcommands/addField.js +238 -0
- package/src/server/subcommands/resourceAst.js +632 -0
- package/src/shared/crud/crudResource.js +93 -98
- package/templates/migrations/crud_initial.cjs +1 -0
- package/templates/src/local-package/package.descriptor.mjs +2 -2
- package/templates/src/local-package/server/{CrudServiceProvider.js → CrudProvider.js} +13 -8
- package/templates/src/local-package/server/actions.js +24 -10
- package/templates/src/local-package/server/registerRoutes.js +32 -33
- package/templates/src/local-package/server/repository.js +33 -132
- package/templates/src/local-package/server/service.js +88 -47
- package/templates/src/local-package/shared/crudResource.js +77 -45
- package/test/addFieldSubcommand.test.js +167 -0
- package/test/buildTemplateContext.test.js +198 -4
- package/test/crudResource.test.js +6 -0
- package/test/crudServerGuards.test.js +43 -49
- package/test/crudService.test.js +93 -5
- package/test/routeInputContracts.test.js +144 -41
- package/test-support/templateServerFixture.js +169 -0
- package/src/server/actionIds.js +0 -22
- package/src/server/actions.js +0 -152
- package/src/server/registerRoutes.js +0 -234
- package/src/server/repository.js +0 -162
- package/src/server/service.js +0 -96
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import { registerRoutes } from "../src/server/registerRoutes.js";
|
|
2
|
+
import test, { after } from "node:test";
|
|
4
3
|
import { resolveApiBasePath } from "@jskit-ai/users-core/shared/support/usersApiPaths";
|
|
4
|
+
import { createTemplateServerFixture } from "../test-support/templateServerFixture.js";
|
|
5
|
+
|
|
6
|
+
const fixture = await createTemplateServerFixture();
|
|
7
|
+
const { registerRoutes } = await fixture.importServerModule("registerRoutes.js");
|
|
8
|
+
|
|
9
|
+
after(async () => {
|
|
10
|
+
await fixture.cleanup();
|
|
11
|
+
});
|
|
5
12
|
|
|
6
13
|
function createReplyDouble() {
|
|
7
14
|
return {
|
|
@@ -45,14 +52,7 @@ test("crud routes build create/update action input with explicit payload and pat
|
|
|
45
52
|
|
|
46
53
|
registerRoutes(app, {
|
|
47
54
|
routeRelativePath: "/customers",
|
|
48
|
-
routeSurfaceRequiresWorkspace: true
|
|
49
|
-
actionIds: {
|
|
50
|
-
list: "crud.customers.list",
|
|
51
|
-
view: "crud.customers.view",
|
|
52
|
-
create: "crud.customers.create",
|
|
53
|
-
update: "crud.customers.update",
|
|
54
|
-
delete: "crud.customers.delete"
|
|
55
|
-
}
|
|
55
|
+
routeSurfaceRequiresWorkspace: true
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
const workspaceRouteBase = resolveApiBasePath({
|
|
@@ -125,14 +125,7 @@ test("crud routes omit workspaceSlug for non-workspace calls and apply configure
|
|
|
125
125
|
|
|
126
126
|
registerRoutes(app, {
|
|
127
127
|
routeRelativePath: "/customers",
|
|
128
|
-
routeSurface: "console"
|
|
129
|
-
actionIds: {
|
|
130
|
-
list: "crud.customers.list",
|
|
131
|
-
view: "crud.customers.view",
|
|
132
|
-
create: "crud.customers.create",
|
|
133
|
-
update: "crud.customers.update",
|
|
134
|
-
delete: "crud.customers.delete"
|
|
135
|
-
}
|
|
128
|
+
routeSurface: "console"
|
|
136
129
|
});
|
|
137
130
|
|
|
138
131
|
const createRoute = findRoute(registeredRoutes, "POST", "/api/customers");
|
|
@@ -161,7 +154,7 @@ test("crud routes omit workspaceSlug for non-workspace calls and apply configure
|
|
|
161
154
|
});
|
|
162
155
|
});
|
|
163
156
|
|
|
164
|
-
test("crud routes
|
|
157
|
+
test("crud routes validate route ownership filter values before registering visibility", () => {
|
|
165
158
|
const registeredRoutes = [];
|
|
166
159
|
const router = {
|
|
167
160
|
register(method, path, route, handler) {
|
|
@@ -184,32 +177,142 @@ test("crud routes normalize route ownership filter values before registering vis
|
|
|
184
177
|
|
|
185
178
|
registerRoutes(app, {
|
|
186
179
|
routeRelativePath: "/customers",
|
|
187
|
-
routeOwnershipFilter: " Workspace_User "
|
|
188
|
-
actionIds: {
|
|
189
|
-
list: "crud.customers.list",
|
|
190
|
-
view: "crud.customers.view",
|
|
191
|
-
create: "crud.customers.create",
|
|
192
|
-
update: "crud.customers.update",
|
|
193
|
-
delete: "crud.customers.delete"
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
registerRoutes(app, {
|
|
197
|
-
routeRelativePath: "/customers-public",
|
|
198
|
-
routeOwnershipFilter: "not-a-real-filter",
|
|
199
|
-
actionIds: {
|
|
200
|
-
list: "crud.customers-public.list",
|
|
201
|
-
view: "crud.customers-public.view",
|
|
202
|
-
create: "crud.customers-public.create",
|
|
203
|
-
update: "crud.customers-public.update",
|
|
204
|
-
delete: "crud.customers-public.delete"
|
|
205
|
-
}
|
|
180
|
+
routeOwnershipFilter: " Workspace_User "
|
|
206
181
|
});
|
|
182
|
+
assert.throws(
|
|
183
|
+
() =>
|
|
184
|
+
registerRoutes(app, {
|
|
185
|
+
routeRelativePath: "/customers-public",
|
|
186
|
+
routeOwnershipFilter: "not-a-real-filter"
|
|
187
|
+
}),
|
|
188
|
+
/must be one of/
|
|
189
|
+
);
|
|
207
190
|
|
|
208
191
|
const workspaceUserRoute = findRoute(registeredRoutes, "GET", "/api/customers");
|
|
209
|
-
const fallbackPublicRoute = findRoute(registeredRoutes, "GET", "/api/customers-public");
|
|
210
192
|
|
|
211
193
|
assert.ok(workspaceUserRoute);
|
|
212
|
-
assert.ok(fallbackPublicRoute);
|
|
213
194
|
assert.equal(workspaceUserRoute.route.visibility, "workspace_user");
|
|
214
|
-
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("crud list route forwards normalized query input from list query validators", async () => {
|
|
198
|
+
const registeredRoutes = [];
|
|
199
|
+
const router = {
|
|
200
|
+
register(method, path, route, handler) {
|
|
201
|
+
registeredRoutes.push({
|
|
202
|
+
method,
|
|
203
|
+
path,
|
|
204
|
+
route,
|
|
205
|
+
handler
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
const app = {
|
|
210
|
+
make(token) {
|
|
211
|
+
if (token !== "jskit.http.router") {
|
|
212
|
+
throw new Error(`Unexpected token: ${String(token)}`);
|
|
213
|
+
}
|
|
214
|
+
return router;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
registerRoutes(app, {
|
|
219
|
+
routeRelativePath: "/customers",
|
|
220
|
+
routeSurfaceRequiresWorkspace: true
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const workspaceRouteBase = resolveApiBasePath({
|
|
224
|
+
surfaceRequiresWorkspace: true,
|
|
225
|
+
relativePath: "/customers"
|
|
226
|
+
});
|
|
227
|
+
const listRoute = findRoute(registeredRoutes, "GET", workspaceRouteBase);
|
|
228
|
+
assert.ok(listRoute);
|
|
229
|
+
|
|
230
|
+
const calls = [];
|
|
231
|
+
const executeAction = async (payload) => {
|
|
232
|
+
calls.push(payload);
|
|
233
|
+
return {};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
await listRoute.handler(
|
|
237
|
+
{
|
|
238
|
+
input: {
|
|
239
|
+
params: { workspaceSlug: "acme" },
|
|
240
|
+
query: {
|
|
241
|
+
cursor: 3,
|
|
242
|
+
limit: 25,
|
|
243
|
+
q: "to",
|
|
244
|
+
contactId: "2971",
|
|
245
|
+
include: "vetId"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
executeAction
|
|
249
|
+
},
|
|
250
|
+
createReplyDouble()
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
assert.deepEqual(calls[0].input, {
|
|
254
|
+
workspaceSlug: "acme",
|
|
255
|
+
cursor: 3,
|
|
256
|
+
limit: 25,
|
|
257
|
+
q: "to",
|
|
258
|
+
contactId: "2971",
|
|
259
|
+
include: "vetId"
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("crud view route forwards include query input", async () => {
|
|
264
|
+
const registeredRoutes = [];
|
|
265
|
+
const router = {
|
|
266
|
+
register(method, path, route, handler) {
|
|
267
|
+
registeredRoutes.push({
|
|
268
|
+
method,
|
|
269
|
+
path,
|
|
270
|
+
route,
|
|
271
|
+
handler
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const app = {
|
|
276
|
+
make(token) {
|
|
277
|
+
if (token !== "jskit.http.router") {
|
|
278
|
+
throw new Error(`Unexpected token: ${String(token)}`);
|
|
279
|
+
}
|
|
280
|
+
return router;
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
registerRoutes(app, {
|
|
285
|
+
routeRelativePath: "/customers",
|
|
286
|
+
routeSurfaceRequiresWorkspace: true
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const workspaceRouteBase = resolveApiBasePath({
|
|
290
|
+
surfaceRequiresWorkspace: true,
|
|
291
|
+
relativePath: "/customers"
|
|
292
|
+
});
|
|
293
|
+
const viewRoute = findRoute(registeredRoutes, "GET", `${workspaceRouteBase}/:recordId`);
|
|
294
|
+
assert.ok(viewRoute);
|
|
295
|
+
|
|
296
|
+
const calls = [];
|
|
297
|
+
const executeAction = async (payload) => {
|
|
298
|
+
calls.push(payload);
|
|
299
|
+
return {};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
await viewRoute.handler(
|
|
303
|
+
{
|
|
304
|
+
input: {
|
|
305
|
+
params: { workspaceSlug: "acme", recordId: 7 },
|
|
306
|
+
query: { include: "vetId" }
|
|
307
|
+
},
|
|
308
|
+
executeAction
|
|
309
|
+
},
|
|
310
|
+
createReplyDouble()
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
assert.deepEqual(calls[0].input, {
|
|
314
|
+
workspaceSlug: "acme",
|
|
315
|
+
recordId: 7,
|
|
316
|
+
include: "vetId"
|
|
317
|
+
});
|
|
215
318
|
});
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
|
|
5
|
+
const testSupportDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const packageRoot = path.resolve(testSupportDirectory, "..");
|
|
7
|
+
const serverTemplateRoot = path.join(packageRoot, "templates", "src", "local-package", "server");
|
|
8
|
+
|
|
9
|
+
const CRUD_NAMESPACE = Object.freeze({
|
|
10
|
+
snake: "customers",
|
|
11
|
+
camel: "customers",
|
|
12
|
+
singularCamel: "customer",
|
|
13
|
+
pascal: "Customers"
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const TEMPLATE_REPLACEMENTS = Object.freeze([
|
|
17
|
+
["${option:namespace|snake}", CRUD_NAMESPACE.snake],
|
|
18
|
+
["${option:namespace|camel}", CRUD_NAMESPACE.camel],
|
|
19
|
+
["${option:namespace|singular|camel}", CRUD_NAMESPACE.singularCamel],
|
|
20
|
+
["${option:namespace|pascal}", CRUD_NAMESPACE.pascal],
|
|
21
|
+
["__JSKIT_CRUD_ID_COLUMN__", JSON.stringify("id")]
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
function applyTemplateReplacements(sourceText = "") {
|
|
25
|
+
let rendered = String(sourceText || "");
|
|
26
|
+
for (const [needle, replacement] of TEMPLATE_REPLACEMENTS) {
|
|
27
|
+
rendered = rendered.split(needle).join(replacement);
|
|
28
|
+
}
|
|
29
|
+
return rendered;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildResourceStubSource() {
|
|
33
|
+
return `const recordOutputValidator = Object.freeze({
|
|
34
|
+
schema: {
|
|
35
|
+
properties: {
|
|
36
|
+
id: {},
|
|
37
|
+
name: {}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
normalize(payload = {}) {
|
|
41
|
+
return payload;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const createBodyValidator = Object.freeze({
|
|
46
|
+
schema: {
|
|
47
|
+
properties: {
|
|
48
|
+
name: {},
|
|
49
|
+
contactId: {}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
normalize(payload = {}) {
|
|
53
|
+
return payload;
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const patchBodyValidator = Object.freeze({
|
|
58
|
+
schema: {
|
|
59
|
+
properties: {
|
|
60
|
+
name: {},
|
|
61
|
+
contactId: {}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
normalize: createBodyValidator.normalize
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const resource = Object.freeze({
|
|
68
|
+
resource: "customers",
|
|
69
|
+
tableName: "customers",
|
|
70
|
+
idColumn: "id",
|
|
71
|
+
operations: {
|
|
72
|
+
list: {
|
|
73
|
+
outputValidator: Object.freeze({
|
|
74
|
+
schema: {
|
|
75
|
+
properties: {
|
|
76
|
+
items: {},
|
|
77
|
+
nextCursor: {}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
normalize(payload = {}) {
|
|
81
|
+
return payload;
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
},
|
|
85
|
+
view: {
|
|
86
|
+
outputValidator: recordOutputValidator
|
|
87
|
+
},
|
|
88
|
+
create: {
|
|
89
|
+
bodyValidator: createBodyValidator,
|
|
90
|
+
outputValidator: recordOutputValidator
|
|
91
|
+
},
|
|
92
|
+
patch: {
|
|
93
|
+
bodyValidator: patchBodyValidator,
|
|
94
|
+
outputValidator: recordOutputValidator
|
|
95
|
+
},
|
|
96
|
+
delete: {
|
|
97
|
+
outputValidator: Object.freeze({
|
|
98
|
+
schema: {
|
|
99
|
+
properties: {
|
|
100
|
+
id: {},
|
|
101
|
+
deleted: {}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
normalize(payload = {}) {
|
|
105
|
+
return payload;
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
fieldMeta: [
|
|
111
|
+
{
|
|
112
|
+
key: "contactId",
|
|
113
|
+
relation: {
|
|
114
|
+
kind: "lookup",
|
|
115
|
+
namespace: "contacts",
|
|
116
|
+
valueKey: "id"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export { resource };
|
|
123
|
+
`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function renderServerTemplateFile(targetServerDirectory, fileName) {
|
|
127
|
+
const templatePath = path.join(serverTemplateRoot, fileName);
|
|
128
|
+
const templateSource = await readFile(templatePath, "utf8");
|
|
129
|
+
const renderedSource = applyTemplateReplacements(templateSource);
|
|
130
|
+
await writeFile(path.join(targetServerDirectory, fileName), renderedSource, "utf8");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function createTemplateServerFixture() {
|
|
134
|
+
const fixtureRoot = await mkdtemp(path.join(packageRoot, ".tmp-crud-server-template-fixture-"));
|
|
135
|
+
const srcRoot = path.join(fixtureRoot, "src");
|
|
136
|
+
const serverRoot = path.join(srcRoot, "server");
|
|
137
|
+
const sharedRoot = path.join(srcRoot, "shared");
|
|
138
|
+
|
|
139
|
+
await mkdir(serverRoot, { recursive: true });
|
|
140
|
+
await mkdir(sharedRoot, { recursive: true });
|
|
141
|
+
await writeFile(
|
|
142
|
+
path.join(fixtureRoot, "package.json"),
|
|
143
|
+
JSON.stringify({ name: "crud-server-template-fixture", private: true, type: "module" }, null, 2),
|
|
144
|
+
"utf8"
|
|
145
|
+
);
|
|
146
|
+
await writeFile(path.join(sharedRoot, "customerResource.js"), buildResourceStubSource(), "utf8");
|
|
147
|
+
|
|
148
|
+
for (const fileName of ["actionIds.js", "actions.js", "registerRoutes.js", "repository.js", "service.js"]) {
|
|
149
|
+
await renderServerTemplateFile(serverRoot, fileName);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function importServerModule(fileName) {
|
|
153
|
+
const absolutePath = path.join(serverRoot, fileName);
|
|
154
|
+
const href = pathToFileURL(absolutePath).href;
|
|
155
|
+
return import(`${href}?t=${Date.now()}_${Math.random()}`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function cleanup() {
|
|
159
|
+
await rm(fixtureRoot, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return Object.freeze({
|
|
163
|
+
fixtureRoot,
|
|
164
|
+
importServerModule,
|
|
165
|
+
cleanup
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { createTemplateServerFixture };
|
package/src/server/actionIds.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
function requireActionIdPrefix(actionIdPrefix) {
|
|
2
|
-
const prefix = String(actionIdPrefix || "").trim();
|
|
3
|
-
if (!prefix) {
|
|
4
|
-
throw new TypeError("createActionIds requires actionIdPrefix.");
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
return prefix;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function createActionIds(actionIdPrefix) {
|
|
11
|
-
const prefix = requireActionIdPrefix(actionIdPrefix);
|
|
12
|
-
|
|
13
|
-
return Object.freeze({
|
|
14
|
-
list: `${prefix}.list`,
|
|
15
|
-
view: `${prefix}.view`,
|
|
16
|
-
create: `${prefix}.create`,
|
|
17
|
-
update: `${prefix}.update`,
|
|
18
|
-
delete: `${prefix}.delete`
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export { requireActionIdPrefix, createActionIds };
|
package/src/server/actions.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
cursorPaginationQueryValidator,
|
|
3
|
-
recordIdParamsValidator
|
|
4
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
5
|
-
import { workspaceSlugParamsValidator } from "@jskit-ai/users-core/server/validators/routeParamsValidator";
|
|
6
|
-
import { crudResource } from "../shared/crud/crudResource.js";
|
|
7
|
-
import { createActionIds } from "./actionIds.js";
|
|
8
|
-
|
|
9
|
-
function requireActionSurface(surface = "") {
|
|
10
|
-
const normalizedSurface = String(surface || "").trim().toLowerCase();
|
|
11
|
-
if (!normalizedSurface) {
|
|
12
|
-
throw new TypeError("createActions requires a non-empty surface.");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return normalizedSurface;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function createActions({ actionIdPrefix, surface = "" } = {}) {
|
|
19
|
-
const actionIds = createActionIds(actionIdPrefix);
|
|
20
|
-
const actionSurface = requireActionSurface(surface);
|
|
21
|
-
|
|
22
|
-
return Object.freeze([
|
|
23
|
-
{
|
|
24
|
-
id: actionIds.list,
|
|
25
|
-
version: 1,
|
|
26
|
-
kind: "query",
|
|
27
|
-
channels: ["api", "automation", "internal"],
|
|
28
|
-
surfaces: [actionSurface],
|
|
29
|
-
permission: {
|
|
30
|
-
require: "authenticated"
|
|
31
|
-
},
|
|
32
|
-
inputValidator: [workspaceSlugParamsValidator, cursorPaginationQueryValidator],
|
|
33
|
-
outputValidator: crudResource.operations.list.outputValidator,
|
|
34
|
-
idempotency: "none",
|
|
35
|
-
audit: {
|
|
36
|
-
actionName: actionIds.list
|
|
37
|
-
},
|
|
38
|
-
observability: {},
|
|
39
|
-
async execute(input, context, deps) {
|
|
40
|
-
return deps.crudService.listRecords(input, {
|
|
41
|
-
context,
|
|
42
|
-
visibilityContext: context?.visibilityContext
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: actionIds.view,
|
|
48
|
-
version: 1,
|
|
49
|
-
kind: "query",
|
|
50
|
-
channels: ["api", "automation", "internal"],
|
|
51
|
-
surfaces: [actionSurface],
|
|
52
|
-
permission: {
|
|
53
|
-
require: "authenticated"
|
|
54
|
-
},
|
|
55
|
-
inputValidator: [workspaceSlugParamsValidator, recordIdParamsValidator],
|
|
56
|
-
outputValidator: crudResource.operations.view.outputValidator,
|
|
57
|
-
idempotency: "none",
|
|
58
|
-
audit: {
|
|
59
|
-
actionName: actionIds.view
|
|
60
|
-
},
|
|
61
|
-
observability: {},
|
|
62
|
-
async execute(input, context, deps) {
|
|
63
|
-
return deps.crudService.getRecord(input.recordId, {
|
|
64
|
-
context,
|
|
65
|
-
visibilityContext: context?.visibilityContext
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
id: actionIds.create,
|
|
71
|
-
version: 1,
|
|
72
|
-
kind: "command",
|
|
73
|
-
channels: ["api", "automation", "internal"],
|
|
74
|
-
surfaces: [actionSurface],
|
|
75
|
-
permission: {
|
|
76
|
-
require: "authenticated"
|
|
77
|
-
},
|
|
78
|
-
inputValidator: [
|
|
79
|
-
workspaceSlugParamsValidator,
|
|
80
|
-
{
|
|
81
|
-
payload: crudResource.operations.create.bodyValidator
|
|
82
|
-
}
|
|
83
|
-
],
|
|
84
|
-
outputValidator: crudResource.operations.create.outputValidator,
|
|
85
|
-
idempotency: "optional",
|
|
86
|
-
audit: {
|
|
87
|
-
actionName: actionIds.create
|
|
88
|
-
},
|
|
89
|
-
observability: {},
|
|
90
|
-
async execute(input, context, deps) {
|
|
91
|
-
return deps.crudService.createRecord(input.payload, {
|
|
92
|
-
context,
|
|
93
|
-
visibilityContext: context?.visibilityContext
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
id: actionIds.update,
|
|
99
|
-
version: 1,
|
|
100
|
-
kind: "command",
|
|
101
|
-
channels: ["api", "automation", "internal"],
|
|
102
|
-
surfaces: [actionSurface],
|
|
103
|
-
permission: {
|
|
104
|
-
require: "authenticated"
|
|
105
|
-
},
|
|
106
|
-
inputValidator: [
|
|
107
|
-
workspaceSlugParamsValidator,
|
|
108
|
-
recordIdParamsValidator,
|
|
109
|
-
{
|
|
110
|
-
patch: crudResource.operations.patch.bodyValidator
|
|
111
|
-
}
|
|
112
|
-
],
|
|
113
|
-
outputValidator: crudResource.operations.patch.outputValidator,
|
|
114
|
-
idempotency: "optional",
|
|
115
|
-
audit: {
|
|
116
|
-
actionName: actionIds.update
|
|
117
|
-
},
|
|
118
|
-
observability: {},
|
|
119
|
-
async execute(input, context, deps) {
|
|
120
|
-
return deps.crudService.updateRecord(input.recordId, input.patch, {
|
|
121
|
-
context,
|
|
122
|
-
visibilityContext: context?.visibilityContext
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
id: actionIds.delete,
|
|
128
|
-
version: 1,
|
|
129
|
-
kind: "command",
|
|
130
|
-
channels: ["api", "automation", "internal"],
|
|
131
|
-
surfaces: [actionSurface],
|
|
132
|
-
permission: {
|
|
133
|
-
require: "authenticated"
|
|
134
|
-
},
|
|
135
|
-
inputValidator: [workspaceSlugParamsValidator, recordIdParamsValidator],
|
|
136
|
-
outputValidator: crudResource.operations.delete.outputValidator,
|
|
137
|
-
idempotency: "optional",
|
|
138
|
-
audit: {
|
|
139
|
-
actionName: actionIds.delete
|
|
140
|
-
},
|
|
141
|
-
observability: {},
|
|
142
|
-
async execute(input, context, deps) {
|
|
143
|
-
return deps.crudService.deleteRecord(input.recordId, {
|
|
144
|
-
context,
|
|
145
|
-
visibilityContext: context?.visibilityContext
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
]);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export { createActions };
|