@dudousxd/nestjs-catalog 0.5.0 → 0.7.0
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/README.md +163 -0
- package/dist/access.controller.js +25 -0
- package/dist/catalog.controller.d.ts +46 -0
- package/dist/catalog.controller.js +199 -23
- package/dist/catalog.decorators.d.ts +17 -0
- package/dist/catalog.decorators.js +17 -0
- package/dist/catalog.environment.d.ts +104 -12
- package/dist/catalog.environment.js +64 -1
- package/dist/catalog.events.d.ts +67 -3
- package/dist/catalog.events.js +15 -2
- package/dist/catalog.pipeline.d.ts +19 -0
- package/dist/catalog.principal.d.ts +49 -1
- package/dist/catalog.principal.js +92 -0
- package/dist/catalog.registry.js +117 -28
- package/dist/catalog.service.d.ts +53 -10
- package/dist/catalog.service.js +180 -15
- package/dist/catalog.types.d.ts +114 -3
- package/dist/catalog.workspace.d.ts +68 -8
- package/dist/catalog.workspace.js +23 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@ exports.createCatalogController = createCatalogController;
|
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
17
|
const catalog_query_cache_1 = require("./catalog.query-cache");
|
|
18
18
|
const catalog_registry_base_1 = require("./catalog.registry.base");
|
|
19
|
+
const catalog_route_auth_1 = require("./catalog.route-auth");
|
|
19
20
|
const catalog_service_1 = require("./catalog.service");
|
|
20
21
|
const catalog_workspace_1 = require("./catalog.workspace");
|
|
21
22
|
/**
|
|
@@ -23,6 +24,52 @@ const catalog_workspace_1 = require("./catalog.workspace");
|
|
|
23
24
|
* guards both come from `forRoot`. A library that hardcodes either one forces
|
|
24
25
|
* every host app to accept its idea of auth, which for an endpoint that
|
|
25
26
|
* enumerates every table in the database is not a reasonable default.
|
|
27
|
+
*
|
|
28
|
+
* ---------------------------------------------------------------------------
|
|
29
|
+
* Every route declares what it needs, and four of the choices are not obvious.
|
|
30
|
+
*
|
|
31
|
+
* Declaring is not enforcing — see `catalog.route-auth.ts` — but an *absent*
|
|
32
|
+
* declaration is a declaration too: it tells a host's guard that authenticated
|
|
33
|
+
* is enough. So a route left bare is not "undecided", it is "open", and for a
|
|
34
|
+
* long time this controller said that about arbitrary SQL and about every
|
|
35
|
+
* curation edit.
|
|
36
|
+
*
|
|
37
|
+
* **Arbitrary SQL is `catalog:admin`, not `catalog:read`.** `catalog:read` is
|
|
38
|
+
* "read object metadata and rows" — rows *of a catalogued type*, through a
|
|
39
|
+
* route that names one. `POST query` is not bounded by the model at all: it is
|
|
40
|
+
* whatever the store's read connection can select, and in the shipped MikroORM
|
|
41
|
+
* store that connection is the catalog's own schema. `SELECT * FROM
|
|
42
|
+
* catalog_principal` returns every principal's scopes, grants and `keyHash` —
|
|
43
|
+
* the SHA-256 of its static key — which is not something a reporting principal
|
|
44
|
+
* should be able to fetch, and is not reachable from any other route here. That
|
|
45
|
+
* is squarely the population `catalog:admin` ("manage principals and grants")
|
|
46
|
+
* describes.
|
|
47
|
+
*
|
|
48
|
+
* **So the two saved-query write routes are `catalog:admin` too.** They accept
|
|
49
|
+
* a `sql` field, and `POST saved-queries` + `POST saved-queries/:id/run` is
|
|
50
|
+
* `POST query` in two requests. Gating one and not the others would make the
|
|
51
|
+
* strict declaration decoration.
|
|
52
|
+
*
|
|
53
|
+
* **Running a saved query is only `catalog:read`.** The capability being held
|
|
54
|
+
* back is *choosing what SQL runs*, not *seeing a result*: a saved query is an
|
|
55
|
+
* artefact somebody with the authoring scope vetted, and running one is reading
|
|
56
|
+
* a report they wrote. Gating execution instead would be worse in both
|
|
57
|
+
* directions — it would stop an analyst opening a dashboard, and it would let an
|
|
58
|
+
* unprivileged caller plant a statement and wait for a privileged one to run it.
|
|
59
|
+
*
|
|
60
|
+
* **`POST reset` is `catalog:curate`, not `catalog:admin`.** It discards exactly
|
|
61
|
+
* what the two `PATCH`es write, catalog-wide, and a curator can already blank
|
|
62
|
+
* every label one request at a time. Requiring admin would deny nothing and
|
|
63
|
+
* would push a routine console action into the scope that manages principals.
|
|
64
|
+
*
|
|
65
|
+
* One consequence is worth stating rather than leaving to be discovered:
|
|
66
|
+
* `shared` rides on the dashboard write routes, so `catalog:curate` carries the
|
|
67
|
+
* power to hand a board to an outside application. That is not an oversight —
|
|
68
|
+
* it is one field on an authoring route, and splitting it would mean a route
|
|
69
|
+
* whose only job is to flip a boolean. What makes it accountable is that the
|
|
70
|
+
* act is audited, in both directions and including by deletion; see the
|
|
71
|
+
* sharing block in `catalog.service.ts`.
|
|
72
|
+
* ---------------------------------------------------------------------------
|
|
26
73
|
*/
|
|
27
74
|
function createCatalogController(path, guards, decorators = []) {
|
|
28
75
|
let CatalogController = class CatalogController {
|
|
@@ -88,11 +135,24 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
88
135
|
snapshot,
|
|
89
136
|
});
|
|
90
137
|
}
|
|
91
|
-
/**
|
|
138
|
+
/**
|
|
139
|
+
* What an ad-hoc query may select from, and whether it can run at all.
|
|
140
|
+
*
|
|
141
|
+
* `catalog:read` and not the scope the query route itself needs: this
|
|
142
|
+
* describes the catalogued types' physical relations and their columns,
|
|
143
|
+
* which is the same model `GET types/:name` already hands a reader. It is a
|
|
144
|
+
* schema panel, not an execution surface.
|
|
145
|
+
*/
|
|
92
146
|
queryRelations() {
|
|
93
147
|
return this.service.queryRelations();
|
|
94
148
|
}
|
|
95
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Run one read-only statement.
|
|
151
|
+
*
|
|
152
|
+
* `catalog:admin`. Read-only is not the same as bounded: this reaches
|
|
153
|
+
* whatever the store's connection reaches, which includes the catalog's own
|
|
154
|
+
* governance tables. See the block above the controller.
|
|
155
|
+
*/
|
|
96
156
|
runQuery(body) {
|
|
97
157
|
return this.service.runQuery(body ?? { sql: '' });
|
|
98
158
|
}
|
|
@@ -106,19 +166,43 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
106
166
|
savedQueries() {
|
|
107
167
|
return this.service.listSavedQueries();
|
|
108
168
|
}
|
|
109
|
-
|
|
110
|
-
|
|
169
|
+
/** Takes a `sql` field, so it is the SQL scope and not a workspace one. */
|
|
170
|
+
saveSavedQuery(body, request) {
|
|
171
|
+
return this.service.saveQuery(body, actorOf(request, body?.createdBy));
|
|
111
172
|
}
|
|
112
173
|
savedQuery(id) {
|
|
113
174
|
return this.service.getSavedQuery(id);
|
|
114
175
|
}
|
|
115
|
-
|
|
116
|
-
|
|
176
|
+
/**
|
|
177
|
+
* The body names no actor, deliberately — unlike the create route above,
|
|
178
|
+
* which has always let one be declared because `createdBy` is a stored
|
|
179
|
+
* field. Here the only consumer of the name is the audit entry a `shared`
|
|
180
|
+
* toggle produces, and a caller that can put any string into the audit trail
|
|
181
|
+
* is worse than one the trail records as the console.
|
|
182
|
+
*
|
|
183
|
+
* `catalog:admin` because the body may carry `sql`. A patch that can rewrite
|
|
184
|
+
* the statement is the authoring capability whatever else it is used for.
|
|
185
|
+
*/
|
|
186
|
+
patchSavedQuery(id, body, request) {
|
|
187
|
+
return this.service.updateSavedQuery(id, body, actorOf(request));
|
|
117
188
|
}
|
|
118
|
-
|
|
119
|
-
|
|
189
|
+
/**
|
|
190
|
+
* `catalog:curate` and not the SQL scope: deleting takes a statement away
|
|
191
|
+
* rather than choosing one. It is the same workspace-authoring act as
|
|
192
|
+
* deleting a board.
|
|
193
|
+
*
|
|
194
|
+
* The principal is passed for the same reason the `PATCH` above takes one —
|
|
195
|
+
* deleting a shared query revokes outside access, and that is recorded.
|
|
196
|
+
*/
|
|
197
|
+
removeSavedQuery(id, request) {
|
|
198
|
+
return this.service.deleteSavedQuery(id, actorOf(request)).then((deleted) => ({ deleted }));
|
|
120
199
|
}
|
|
121
|
-
/**
|
|
200
|
+
/**
|
|
201
|
+
* Run a saved query, honouring the cache TTL it was saved with.
|
|
202
|
+
*
|
|
203
|
+
* `catalog:read`, unlike writing one. What is held back upstream is
|
|
204
|
+
* choosing what SQL runs; this runs a statement somebody already vetted.
|
|
205
|
+
*/
|
|
122
206
|
runSavedQuery(id, body) {
|
|
123
207
|
return this.service.runSavedQuery(id, body?.maxRows);
|
|
124
208
|
}
|
|
@@ -138,23 +222,43 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
138
222
|
dashboards() {
|
|
139
223
|
return this.service.listDashboards();
|
|
140
224
|
}
|
|
141
|
-
|
|
142
|
-
|
|
225
|
+
/**
|
|
226
|
+
* `shared` is declared rather than merely passed through.
|
|
227
|
+
*
|
|
228
|
+
* It is the entire access boundary of the embed API — a board is
|
|
229
|
+
* embeddable because a person said so — and a field a body type does not
|
|
230
|
+
* name is a field a host's whitelisting `ValidationPipe` deletes. The
|
|
231
|
+
* failure is silent in the worst direction: the toggle appears to save and
|
|
232
|
+
* the dashboard is never actually shareable.
|
|
233
|
+
*
|
|
234
|
+
* `catalog:curate`: a board carries no SQL of its own, only cards pointing
|
|
235
|
+
* at queries somebody else vetted. `shared` rides along, which is why the
|
|
236
|
+
* flag is audited rather than merely stored.
|
|
237
|
+
*/
|
|
238
|
+
createDashboard(body, request) {
|
|
239
|
+
return this.service.saveDashboard(body, actorOf(request, body?.createdBy));
|
|
143
240
|
}
|
|
144
241
|
dashboard(id) {
|
|
145
242
|
return this.service.getDashboard(id);
|
|
146
243
|
}
|
|
147
|
-
patchDashboard(id, body) {
|
|
148
|
-
return this.service.updateDashboard(id, body);
|
|
244
|
+
patchDashboard(id, body, request) {
|
|
245
|
+
return this.service.updateDashboard(id, body, actorOf(request));
|
|
149
246
|
}
|
|
150
|
-
|
|
151
|
-
|
|
247
|
+
/** Deleting a shared board revokes outside access, so the actor is passed. */
|
|
248
|
+
removeDashboard(id, request) {
|
|
249
|
+
return this.service.deleteDashboard(id, actorOf(request)).then((deleted) => ({ deleted }));
|
|
152
250
|
}
|
|
153
251
|
/**
|
|
154
252
|
* What this caller may embed.
|
|
155
253
|
*
|
|
156
254
|
* A discovery endpoint so a consuming frontend can list what it is allowed
|
|
157
255
|
* to render rather than being told the ids out of band.
|
|
256
|
+
*
|
|
257
|
+
* `catalog:embed` here as well as on the two fetches, and deliberately the
|
|
258
|
+
* SAME scope rather than a softer one. A caller that cannot fetch anything
|
|
259
|
+
* has no use for the list, and a discovery endpoint open to callers the
|
|
260
|
+
* fetches refuse is an inventory of this catalog's shared dashboards handed
|
|
261
|
+
* to whoever asks.
|
|
158
262
|
*/
|
|
159
263
|
embeddable() {
|
|
160
264
|
return this.service.listEmbeddable();
|
|
@@ -163,11 +267,26 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
163
267
|
embedDashboard(id) {
|
|
164
268
|
return this.service.embedDashboard(id);
|
|
165
269
|
}
|
|
166
|
-
/**
|
|
270
|
+
/**
|
|
271
|
+
* One chart, for a consumer that wants to place it itself.
|
|
272
|
+
*
|
|
273
|
+
* `catalog:embed` and not `catalog:read`: the whole reason the scope exists
|
|
274
|
+
* separately is that an application rendering one chart in its own UI needs
|
|
275
|
+
* nothing else, and a route that accepted `catalog:read` instead would make
|
|
276
|
+
* the narrow grant unusable — every embed consumer would be handed the
|
|
277
|
+
* whole catalog to draw one bar chart.
|
|
278
|
+
*/
|
|
167
279
|
embedChart(id) {
|
|
168
280
|
return this.service.embedChart(id);
|
|
169
281
|
}
|
|
170
|
-
/**
|
|
282
|
+
/**
|
|
283
|
+
* The audit trail: what happened, to what, by whom.
|
|
284
|
+
*
|
|
285
|
+
* `catalog:read`, because attribution here is part of the data rather than a
|
|
286
|
+
* log — "who loaded these rows" is a question a reader asks months later,
|
|
287
|
+
* which is the whole reason the principal is recorded onto every snapshot.
|
|
288
|
+
* The trail carries ids and payloads, never a credential.
|
|
289
|
+
*/
|
|
171
290
|
events(event, typeName, principalId, since, limit) {
|
|
172
291
|
return this.service.listEvents({
|
|
173
292
|
event,
|
|
@@ -216,18 +335,21 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
216
335
|
};
|
|
217
336
|
__decorate([
|
|
218
337
|
(0, common_1.Get)(),
|
|
338
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
219
339
|
__metadata("design:type", Function),
|
|
220
340
|
__metadata("design:paramtypes", []),
|
|
221
341
|
__metadata("design:returntype", void 0)
|
|
222
342
|
], CatalogController.prototype, "snapshot", null);
|
|
223
343
|
__decorate([
|
|
224
344
|
(0, common_1.Get)('graph'),
|
|
345
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
225
346
|
__metadata("design:type", Function),
|
|
226
347
|
__metadata("design:paramtypes", []),
|
|
227
348
|
__metadata("design:returntype", void 0)
|
|
228
349
|
], CatalogController.prototype, "graph", null);
|
|
229
350
|
__decorate([
|
|
230
351
|
(0, common_1.Get)('types/:name'),
|
|
352
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
231
353
|
__param(0, (0, common_1.Param)('name')),
|
|
232
354
|
__metadata("design:type", Function),
|
|
233
355
|
__metadata("design:paramtypes", [String]),
|
|
@@ -235,6 +357,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
235
357
|
], CatalogController.prototype, "type", null);
|
|
236
358
|
__decorate([
|
|
237
359
|
(0, common_1.Patch)('types/:name'),
|
|
360
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
238
361
|
__param(0, (0, common_1.Param)('name')),
|
|
239
362
|
__param(1, (0, common_1.Body)()),
|
|
240
363
|
__metadata("design:type", Function),
|
|
@@ -243,6 +366,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
243
366
|
], CatalogController.prototype, "patchType", null);
|
|
244
367
|
__decorate([
|
|
245
368
|
(0, common_1.Patch)('types/:name/properties/:property'),
|
|
369
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
246
370
|
__param(0, (0, common_1.Param)('name')),
|
|
247
371
|
__param(1, (0, common_1.Param)('property')),
|
|
248
372
|
__param(2, (0, common_1.Body)()),
|
|
@@ -252,12 +376,14 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
252
376
|
], CatalogController.prototype, "patchProperty", null);
|
|
253
377
|
__decorate([
|
|
254
378
|
(0, common_1.Post)('reset'),
|
|
379
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
255
380
|
__metadata("design:type", Function),
|
|
256
381
|
__metadata("design:paramtypes", []),
|
|
257
382
|
__metadata("design:returntype", Promise)
|
|
258
383
|
], CatalogController.prototype, "reset", null);
|
|
259
384
|
__decorate([
|
|
260
385
|
(0, common_1.Get)('objects/:name'),
|
|
386
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
261
387
|
__param(0, (0, common_1.Param)('name')),
|
|
262
388
|
__param(1, (0, common_1.Query)('page')),
|
|
263
389
|
__param(2, (0, common_1.Query)('size')),
|
|
@@ -271,12 +397,14 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
271
397
|
], CatalogController.prototype, "objects", null);
|
|
272
398
|
__decorate([
|
|
273
399
|
(0, common_1.Get)('query/relations'),
|
|
400
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
274
401
|
__metadata("design:type", Function),
|
|
275
402
|
__metadata("design:paramtypes", []),
|
|
276
403
|
__metadata("design:returntype", void 0)
|
|
277
404
|
], CatalogController.prototype, "queryRelations", null);
|
|
278
405
|
__decorate([
|
|
279
406
|
(0, common_1.Post)('query'),
|
|
407
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:admin'),
|
|
280
408
|
__param(0, (0, common_1.Body)()),
|
|
281
409
|
__metadata("design:type", Function),
|
|
282
410
|
__metadata("design:paramtypes", [Object]),
|
|
@@ -284,25 +412,30 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
284
412
|
], CatalogController.prototype, "runQuery", null);
|
|
285
413
|
__decorate([
|
|
286
414
|
(0, common_1.Get)('workspace/capabilities'),
|
|
415
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
287
416
|
__metadata("design:type", Function),
|
|
288
417
|
__metadata("design:paramtypes", []),
|
|
289
418
|
__metadata("design:returntype", void 0)
|
|
290
419
|
], CatalogController.prototype, "workspaceCapabilities", null);
|
|
291
420
|
__decorate([
|
|
292
421
|
(0, common_1.Get)('saved-queries'),
|
|
422
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
293
423
|
__metadata("design:type", Function),
|
|
294
424
|
__metadata("design:paramtypes", []),
|
|
295
425
|
__metadata("design:returntype", void 0)
|
|
296
426
|
], CatalogController.prototype, "savedQueries", null);
|
|
297
427
|
__decorate([
|
|
298
428
|
(0, common_1.Post)('saved-queries'),
|
|
429
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:admin'),
|
|
299
430
|
__param(0, (0, common_1.Body)()),
|
|
431
|
+
__param(1, (0, common_1.Req)()),
|
|
300
432
|
__metadata("design:type", Function),
|
|
301
|
-
__metadata("design:paramtypes", [Object]),
|
|
433
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
302
434
|
__metadata("design:returntype", void 0)
|
|
303
435
|
], CatalogController.prototype, "saveSavedQuery", null);
|
|
304
436
|
__decorate([
|
|
305
437
|
(0, common_1.Get)('saved-queries/:id'),
|
|
438
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
306
439
|
__param(0, (0, common_1.Param)('id')),
|
|
307
440
|
__metadata("design:type", Function),
|
|
308
441
|
__metadata("design:paramtypes", [String]),
|
|
@@ -310,21 +443,26 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
310
443
|
], CatalogController.prototype, "savedQuery", null);
|
|
311
444
|
__decorate([
|
|
312
445
|
(0, common_1.Patch)('saved-queries/:id'),
|
|
446
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:admin'),
|
|
313
447
|
__param(0, (0, common_1.Param)('id')),
|
|
314
448
|
__param(1, (0, common_1.Body)()),
|
|
449
|
+
__param(2, (0, common_1.Req)()),
|
|
315
450
|
__metadata("design:type", Function),
|
|
316
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
451
|
+
__metadata("design:paramtypes", [String, Object, Object]),
|
|
317
452
|
__metadata("design:returntype", void 0)
|
|
318
453
|
], CatalogController.prototype, "patchSavedQuery", null);
|
|
319
454
|
__decorate([
|
|
320
455
|
(0, common_1.Delete)('saved-queries/:id'),
|
|
456
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
321
457
|
__param(0, (0, common_1.Param)('id')),
|
|
458
|
+
__param(1, (0, common_1.Req)()),
|
|
322
459
|
__metadata("design:type", Function),
|
|
323
|
-
__metadata("design:paramtypes", [String]),
|
|
460
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
324
461
|
__metadata("design:returntype", void 0)
|
|
325
462
|
], CatalogController.prototype, "removeSavedQuery", null);
|
|
326
463
|
__decorate([
|
|
327
464
|
(0, common_1.Post)('saved-queries/:id/run'),
|
|
465
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
328
466
|
__param(0, (0, common_1.Param)('id')),
|
|
329
467
|
__param(1, (0, common_1.Body)()),
|
|
330
468
|
__metadata("design:type", Function),
|
|
@@ -333,6 +471,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
333
471
|
], CatalogController.prototype, "runSavedQuery", null);
|
|
334
472
|
__decorate([
|
|
335
473
|
(0, common_1.Get)('saved-queries/:id/export.csv'),
|
|
474
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
336
475
|
__param(0, (0, common_1.Param)('id')),
|
|
337
476
|
__param(1, (0, common_1.Res)({ passthrough: true })),
|
|
338
477
|
__metadata("design:type", Function),
|
|
@@ -341,19 +480,23 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
341
480
|
], CatalogController.prototype, "exportSavedQuery", null);
|
|
342
481
|
__decorate([
|
|
343
482
|
(0, common_1.Get)('dashboards'),
|
|
483
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
344
484
|
__metadata("design:type", Function),
|
|
345
485
|
__metadata("design:paramtypes", []),
|
|
346
486
|
__metadata("design:returntype", void 0)
|
|
347
487
|
], CatalogController.prototype, "dashboards", null);
|
|
348
488
|
__decorate([
|
|
349
489
|
(0, common_1.Post)('dashboards'),
|
|
490
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
350
491
|
__param(0, (0, common_1.Body)()),
|
|
492
|
+
__param(1, (0, common_1.Req)()),
|
|
351
493
|
__metadata("design:type", Function),
|
|
352
|
-
__metadata("design:paramtypes", [Object]),
|
|
494
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
353
495
|
__metadata("design:returntype", void 0)
|
|
354
496
|
], CatalogController.prototype, "createDashboard", null);
|
|
355
497
|
__decorate([
|
|
356
498
|
(0, common_1.Get)('dashboards/:id'),
|
|
499
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
357
500
|
__param(0, (0, common_1.Param)('id')),
|
|
358
501
|
__metadata("design:type", Function),
|
|
359
502
|
__metadata("design:paramtypes", [String]),
|
|
@@ -361,27 +504,33 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
361
504
|
], CatalogController.prototype, "dashboard", null);
|
|
362
505
|
__decorate([
|
|
363
506
|
(0, common_1.Patch)('dashboards/:id'),
|
|
507
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
364
508
|
__param(0, (0, common_1.Param)('id')),
|
|
365
509
|
__param(1, (0, common_1.Body)()),
|
|
510
|
+
__param(2, (0, common_1.Req)()),
|
|
366
511
|
__metadata("design:type", Function),
|
|
367
|
-
__metadata("design:paramtypes", [String, Object]),
|
|
512
|
+
__metadata("design:paramtypes", [String, Object, Object]),
|
|
368
513
|
__metadata("design:returntype", void 0)
|
|
369
514
|
], CatalogController.prototype, "patchDashboard", null);
|
|
370
515
|
__decorate([
|
|
371
516
|
(0, common_1.Delete)('dashboards/:id'),
|
|
517
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:curate'),
|
|
372
518
|
__param(0, (0, common_1.Param)('id')),
|
|
519
|
+
__param(1, (0, common_1.Req)()),
|
|
373
520
|
__metadata("design:type", Function),
|
|
374
|
-
__metadata("design:paramtypes", [String]),
|
|
521
|
+
__metadata("design:paramtypes", [String, Object]),
|
|
375
522
|
__metadata("design:returntype", void 0)
|
|
376
523
|
], CatalogController.prototype, "removeDashboard", null);
|
|
377
524
|
__decorate([
|
|
378
525
|
(0, common_1.Get)('embed'),
|
|
526
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:embed'),
|
|
379
527
|
__metadata("design:type", Function),
|
|
380
528
|
__metadata("design:paramtypes", []),
|
|
381
529
|
__metadata("design:returntype", void 0)
|
|
382
530
|
], CatalogController.prototype, "embeddable", null);
|
|
383
531
|
__decorate([
|
|
384
532
|
(0, common_1.Get)('embed/dashboards/:id'),
|
|
533
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:embed'),
|
|
385
534
|
__param(0, (0, common_1.Param)('id')),
|
|
386
535
|
__metadata("design:type", Function),
|
|
387
536
|
__metadata("design:paramtypes", [String]),
|
|
@@ -389,6 +538,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
389
538
|
], CatalogController.prototype, "embedDashboard", null);
|
|
390
539
|
__decorate([
|
|
391
540
|
(0, common_1.Get)('embed/charts/:id'),
|
|
541
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:embed'),
|
|
392
542
|
__param(0, (0, common_1.Param)('id')),
|
|
393
543
|
__metadata("design:type", Function),
|
|
394
544
|
__metadata("design:paramtypes", [String]),
|
|
@@ -396,6 +546,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
396
546
|
], CatalogController.prototype, "embedChart", null);
|
|
397
547
|
__decorate([
|
|
398
548
|
(0, common_1.Get)('events'),
|
|
549
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
399
550
|
__param(0, (0, common_1.Query)('event')),
|
|
400
551
|
__param(1, (0, common_1.Query)('type')),
|
|
401
552
|
__param(2, (0, common_1.Query)('principal')),
|
|
@@ -407,6 +558,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
407
558
|
], CatalogController.prototype, "events", null);
|
|
408
559
|
__decorate([
|
|
409
560
|
(0, common_1.Get)('events/traces'),
|
|
561
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
410
562
|
__param(0, (0, common_1.Query)('type')),
|
|
411
563
|
__param(1, (0, common_1.Query)('principal')),
|
|
412
564
|
__param(2, (0, common_1.Query)('event')),
|
|
@@ -420,6 +572,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
420
572
|
], CatalogController.prototype, "traceList", null);
|
|
421
573
|
__decorate([
|
|
422
574
|
(0, common_1.Get)('events/traces/:id'),
|
|
575
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
423
576
|
__param(0, (0, common_1.Param)('id')),
|
|
424
577
|
__metadata("design:type", Function),
|
|
425
578
|
__metadata("design:paramtypes", [String]),
|
|
@@ -427,6 +580,7 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
427
580
|
], CatalogController.prototype, "trace", null);
|
|
428
581
|
__decorate([
|
|
429
582
|
(0, common_1.Get)('objects/:name/snapshots'),
|
|
583
|
+
(0, catalog_route_auth_1.RequireScopes)('catalog:read'),
|
|
430
584
|
__param(0, (0, common_1.Param)('name')),
|
|
431
585
|
__metadata("design:type", Function),
|
|
432
586
|
__metadata("design:paramtypes", [String]),
|
|
@@ -447,6 +601,28 @@ function createCatalogController(path, guards, decorators = []) {
|
|
|
447
601
|
}
|
|
448
602
|
return CatalogController;
|
|
449
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Who to record a workspace change against.
|
|
606
|
+
*
|
|
607
|
+
* The host's resolved principal wins over anything the body claimed, and that
|
|
608
|
+
* order is the whole point: a `createdBy` in a request body is a name the caller
|
|
609
|
+
* chose for itself, and letting it beat the principal a guard authenticated
|
|
610
|
+
* would make the audit trail's actor column a free-text field. It is still
|
|
611
|
+
* honoured when there is no principal, because this library does not resolve
|
|
612
|
+
* one — see the enforcement note in `catalog.principal.ts` — and a host that has
|
|
613
|
+
* not wired a guard yet is better served by "console" than by nothing.
|
|
614
|
+
*
|
|
615
|
+
* `principal.id` rather than `applicationId`, matching every other event on this
|
|
616
|
+
* channel: for a delegated caller that string carries the person inside it, and
|
|
617
|
+
* dropping to the application half would attribute a person's decision to the
|
|
618
|
+
* console they used.
|
|
619
|
+
*/
|
|
620
|
+
function actorOf(request, claimed) {
|
|
621
|
+
const resolved = request?.principal?.id?.trim();
|
|
622
|
+
if (resolved)
|
|
623
|
+
return resolved;
|
|
624
|
+
return claimed?.trim() || 'console';
|
|
625
|
+
}
|
|
450
626
|
/**
|
|
451
627
|
* `?outcome=failed`, `?outcome=failed,incomplete`, `?outcome=a&outcome=b`.
|
|
452
628
|
*
|
|
@@ -31,6 +31,23 @@ export declare function CatalogType(options?: CatalogTypeOptions): ClassDecorato
|
|
|
31
31
|
* Enriches one property. Everything it sets is tier 0 — the overlay can
|
|
32
32
|
* override any of it at runtime without a migration, which is precisely why
|
|
33
33
|
* these live in metadata rather than in the column definition.
|
|
34
|
+
*
|
|
35
|
+
* **This is also how a relation is enriched, and there is deliberately no
|
|
36
|
+
* `@CatalogRelation`.** The metadata is keyed by property name, and a
|
|
37
|
+
* `@ManyToOne` is a property; the registry looks the options up before it
|
|
38
|
+
* decides whether the field is a scalar or a link, so
|
|
39
|
+
* `@CatalogProperty({ displayName: 'Home base' })` on `Mvr.base` labels the link
|
|
40
|
+
* exactly as it labels a column. A second decorator would be a synonym for this
|
|
41
|
+
* one.
|
|
42
|
+
*
|
|
43
|
+
* A decorator that declared a relation *outright* — target, kind, join column —
|
|
44
|
+
* was considered and rejected twice over. Everything an ORM models is already
|
|
45
|
+
* derived, and a hand-written line that could restate it is a line that can
|
|
46
|
+
* disagree with the schema, which is the one thing this model does not allow.
|
|
47
|
+
* And the links an ORM genuinely cannot see are, in practice, the ones that
|
|
48
|
+
* cross applications: neither side's ORM holds both ends, so no decorator in
|
|
49
|
+
* either codebase can assert them. That is a curation act in the console, and it
|
|
50
|
+
* needs a route that does not exist yet.
|
|
34
51
|
*/
|
|
35
52
|
export declare function CatalogProperty(options?: CatalogPropertyOptions): PropertyDecorator;
|
|
36
53
|
export declare function readTypeOptions(target: unknown): CatalogTypeOptions;
|
|
@@ -27,6 +27,23 @@ function CatalogType(options = {}) {
|
|
|
27
27
|
* Enriches one property. Everything it sets is tier 0 — the overlay can
|
|
28
28
|
* override any of it at runtime without a migration, which is precisely why
|
|
29
29
|
* these live in metadata rather than in the column definition.
|
|
30
|
+
*
|
|
31
|
+
* **This is also how a relation is enriched, and there is deliberately no
|
|
32
|
+
* `@CatalogRelation`.** The metadata is keyed by property name, and a
|
|
33
|
+
* `@ManyToOne` is a property; the registry looks the options up before it
|
|
34
|
+
* decides whether the field is a scalar or a link, so
|
|
35
|
+
* `@CatalogProperty({ displayName: 'Home base' })` on `Mvr.base` labels the link
|
|
36
|
+
* exactly as it labels a column. A second decorator would be a synonym for this
|
|
37
|
+
* one.
|
|
38
|
+
*
|
|
39
|
+
* A decorator that declared a relation *outright* — target, kind, join column —
|
|
40
|
+
* was considered and rejected twice over. Everything an ORM models is already
|
|
41
|
+
* derived, and a hand-written line that could restate it is a line that can
|
|
42
|
+
* disagree with the schema, which is the one thing this model does not allow.
|
|
43
|
+
* And the links an ORM genuinely cannot see are, in practice, the ones that
|
|
44
|
+
* cross applications: neither side's ORM holds both ends, so no decorator in
|
|
45
|
+
* either codebase can assert them. That is a curation act in the console, and it
|
|
46
|
+
* needs a route that does not exist yet.
|
|
30
47
|
*/
|
|
31
48
|
function CatalogProperty(options = {}) {
|
|
32
49
|
return (target, propertyKey) => {
|