@oneuptime/common 12.0.7 → 12.0.8
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/Models/DatabaseModels/TelemetryEntity.ts +45 -0
- package/Models/DatabaseModels/TelemetryEntityRelationship.ts +26 -0
- package/Models/DatabaseModels/WorkflowLog.ts +38 -0
- package/Server/API/UserAPI.ts +16 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- package/Server/Services/TelemetryEntityRelationshipService.ts +3 -0
- package/Server/Services/TelemetryEntityService.ts +114 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
- package/Server/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
- package/Server/Utils/VM/VMAPI.ts +56 -8
- package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
- package/Tests/Server/Services/TelemetryEntityManualCreate.test.ts +248 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
- package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +379 -0
- package/Tests/Types/Workflow/Templates.test.ts +403 -0
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +821 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +287 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +185 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +174 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +152 -0
- package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
- package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +240 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +31 -0
- package/Types/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +27 -0
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +7 -3
- package/Types/Workflow/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +499 -0
- package/Types/Workflow/Templates.ts +316 -0
- package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +23 -6
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Input/Input.tsx +31 -3
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
- package/UI/Components/Modal/Modal.tsx +37 -4
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +194 -7
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +69 -8
- package/UI/Components/Workflow/GraphLint.ts +591 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +396 -0
- package/UI/Components/Workflow/ModelSchema.ts +196 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/Workflow.tsx +77 -4
- package/UI/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +45 -7
- package/Utils/Telemetry/EntityKey.ts +66 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js +47 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js +27 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +3 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +91 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +27 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +7 -3
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +326 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +218 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +22 -6
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +12 -4
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +140 -11
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +31 -3
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +396 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +225 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +118 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +51 -6
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +35 -6
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityKey.js +52 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ import IconProp from "../../Types/Icon/IconProp";
|
|
|
15
15
|
import { JSONObject } from "../../Types/JSON";
|
|
16
16
|
import ObjectID from "../../Types/ObjectID";
|
|
17
17
|
import Permission from "../../Types/Permission";
|
|
18
|
+
import EntitySource from "../../Types/Telemetry/EntitySource";
|
|
18
19
|
import EntityType from "../../Types/Telemetry/EntityType";
|
|
19
20
|
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
20
21
|
import DatabaseBaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
@@ -187,6 +188,50 @@ export default class TelemetryEntity extends DatabaseBaseModel {
|
|
|
187
188
|
})
|
|
188
189
|
public displayName?: string = undefined;
|
|
189
190
|
|
|
191
|
+
/*
|
|
192
|
+
* Immutable after create (`update: []`): the value decides whether
|
|
193
|
+
* PruneStaleEntities may reap the row, so letting it be edited would let a
|
|
194
|
+
* user flip a manual CI into a TTL-pruned one — or quietly make a
|
|
195
|
+
* discovered row immortal. Re-create the row instead.
|
|
196
|
+
*/
|
|
197
|
+
@ColumnAccessControl({ create: CREATE_PERMS, read: READ_PERMS, update: [] })
|
|
198
|
+
@TableColumn({
|
|
199
|
+
type: TableColumnType.ShortText,
|
|
200
|
+
required: true,
|
|
201
|
+
title: "Source",
|
|
202
|
+
description:
|
|
203
|
+
"How this row came to exist: discovered from telemetry, mirrored from a OneUptime inventory table, or created manually by a user. Determines whether stale-entity pruning applies.",
|
|
204
|
+
example: "discovered",
|
|
205
|
+
})
|
|
206
|
+
@Index()
|
|
207
|
+
@Column({
|
|
208
|
+
type: ColumnType.ShortText,
|
|
209
|
+
length: ColumnLength.ShortText,
|
|
210
|
+
nullable: false,
|
|
211
|
+
default: EntitySource.Discovered,
|
|
212
|
+
})
|
|
213
|
+
public source?: EntitySource = undefined;
|
|
214
|
+
|
|
215
|
+
@ColumnAccessControl({
|
|
216
|
+
create: CREATE_PERMS,
|
|
217
|
+
read: READ_PERMS,
|
|
218
|
+
update: UPDATE_PERMS,
|
|
219
|
+
})
|
|
220
|
+
@TableColumn({
|
|
221
|
+
type: TableColumnType.LongText,
|
|
222
|
+
required: false,
|
|
223
|
+
title: "Description",
|
|
224
|
+
description:
|
|
225
|
+
"Free-text description. Primarily for manually created entities, where there are no telemetry attributes to explain what the thing is.",
|
|
226
|
+
example: "Stripe payments API - vendor managed, no telemetry.",
|
|
227
|
+
})
|
|
228
|
+
@Column({
|
|
229
|
+
type: ColumnType.LongText,
|
|
230
|
+
length: ColumnLength.LongText,
|
|
231
|
+
nullable: true,
|
|
232
|
+
})
|
|
233
|
+
public description?: string = undefined;
|
|
234
|
+
|
|
190
235
|
@ColumnAccessControl({
|
|
191
236
|
create: CREATE_PERMS,
|
|
192
237
|
read: READ_PERMS,
|
|
@@ -15,6 +15,7 @@ import IconProp from "../../Types/Icon/IconProp";
|
|
|
15
15
|
import ObjectID from "../../Types/ObjectID";
|
|
16
16
|
import Permission from "../../Types/Permission";
|
|
17
17
|
import EntityRelationshipType from "../../Types/Telemetry/EntityRelationshipType";
|
|
18
|
+
import EntitySource from "../../Types/Telemetry/EntitySource";
|
|
18
19
|
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
19
20
|
import DatabaseBaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
20
21
|
|
|
@@ -173,6 +174,31 @@ export default class TelemetryEntityRelationship extends DatabaseBaseModel {
|
|
|
173
174
|
})
|
|
174
175
|
public relationshipType?: EntityRelationshipType = undefined;
|
|
175
176
|
|
|
177
|
+
/*
|
|
178
|
+
* Immutable after create, for the same reason as TelemetryEntity.source:
|
|
179
|
+
* it is the predicate the edge prune keys on. An edge drawn by hand — the
|
|
180
|
+
* only way to connect a manual CI to anything — has nothing re-bumping its
|
|
181
|
+
* `lastSeenAt`, so without this it would be reaped by the 30-day sweep
|
|
182
|
+
* while the entities at both ends survived.
|
|
183
|
+
*/
|
|
184
|
+
@ColumnAccessControl({ create: CREATE_PERMS, read: READ_PERMS, update: [] })
|
|
185
|
+
@TableColumn({
|
|
186
|
+
type: TableColumnType.ShortText,
|
|
187
|
+
required: true,
|
|
188
|
+
title: "Source",
|
|
189
|
+
description:
|
|
190
|
+
"Whether this edge was derived from telemetry or drawn manually by a user. Determines whether stale-edge pruning applies.",
|
|
191
|
+
example: "discovered",
|
|
192
|
+
})
|
|
193
|
+
@Index()
|
|
194
|
+
@Column({
|
|
195
|
+
type: ColumnType.ShortText,
|
|
196
|
+
length: ColumnLength.ShortText,
|
|
197
|
+
nullable: false,
|
|
198
|
+
default: EntitySource.Discovered,
|
|
199
|
+
})
|
|
200
|
+
public source?: EntitySource = undefined;
|
|
201
|
+
|
|
176
202
|
@ColumnAccessControl({
|
|
177
203
|
create: CREATE_PERMS,
|
|
178
204
|
read: READ_PERMS,
|
|
@@ -348,6 +348,44 @@ export default class WorkflowLog extends BaseModel {
|
|
|
348
348
|
})
|
|
349
349
|
public resumeData?: JSONObject = undefined;
|
|
350
350
|
|
|
351
|
+
/*
|
|
352
|
+
* What each step of the run did, as data rather than as lines in `logs`:
|
|
353
|
+
* the component, its resolved arguments, what it returned, which out port it
|
|
354
|
+
* took and how long it took. Readable by anyone who can read the log itself,
|
|
355
|
+
* because it is the same information in a shape that can be rendered as a
|
|
356
|
+
* list of steps instead of a wall of text.
|
|
357
|
+
*
|
|
358
|
+
* Sensitive arguments and return values are redacted before they get here,
|
|
359
|
+
* by the same rules that redact them in `logs`.
|
|
360
|
+
*/
|
|
361
|
+
@ColumnAccessControl({
|
|
362
|
+
create: [],
|
|
363
|
+
read: [
|
|
364
|
+
Permission.ProjectOwner,
|
|
365
|
+
Permission.ProjectAdmin,
|
|
366
|
+
Permission.ProjectMember,
|
|
367
|
+
Permission.Viewer,
|
|
368
|
+
Permission.WorkflowAdmin,
|
|
369
|
+
Permission.WorkflowMember,
|
|
370
|
+
Permission.WorkflowViewer,
|
|
371
|
+
Permission.ReadWorkflowLog,
|
|
372
|
+
],
|
|
373
|
+
update: [],
|
|
374
|
+
})
|
|
375
|
+
@TableColumn({
|
|
376
|
+
required: false,
|
|
377
|
+
isDefaultValueColumn: false,
|
|
378
|
+
type: TableColumnType.JSON,
|
|
379
|
+
title: "Step Trace",
|
|
380
|
+
description:
|
|
381
|
+
"Structured per-step record of this run: arguments, return values, the port taken and timing.",
|
|
382
|
+
})
|
|
383
|
+
@Column({
|
|
384
|
+
type: ColumnType.JSON,
|
|
385
|
+
nullable: true,
|
|
386
|
+
})
|
|
387
|
+
public stepTrace?: JSONObject = undefined;
|
|
388
|
+
|
|
351
389
|
@ColumnAccessControl({
|
|
352
390
|
create: [],
|
|
353
391
|
read: [],
|
package/Server/API/UserAPI.ts
CHANGED
|
@@ -53,10 +53,25 @@ export default class UserAPI extends BaseAPI<User, UserServiceType> {
|
|
|
53
53
|
* the gate; the read below runs as root because a master admin is not a
|
|
54
54
|
* member of the projects being listed and so has no tenant permissions to
|
|
55
55
|
* read them with.
|
|
56
|
+
*
|
|
57
|
+
* The gate accepts the instance-wide master API key as well as a
|
|
58
|
+
* master-admin session, because the caller asking "which projects is this
|
|
59
|
+
* person in?" is typically a script rather than somebody with a browser
|
|
60
|
+
* open. That is a widening, so it is worth being explicit about why it is
|
|
61
|
+
* the right one HERE and not on the routes below: this endpoint only reads,
|
|
62
|
+
* and it reads nothing the key does not already reach through
|
|
63
|
+
* POST /team-member/get-list, which any master-key caller can call today
|
|
64
|
+
* (untenanted, so cross-project) to assemble the same answer one membership
|
|
65
|
+
* at a time. The key buys convenience and correct paging, not new reach.
|
|
66
|
+
*
|
|
67
|
+
* The writes below stay on the session-only middleware. A leaked static key
|
|
68
|
+
* that can list memberships is a disclosure of what it could already
|
|
69
|
+
* disclose; one that can remove a user from a project, set their password or
|
|
70
|
+
* mail them a reset link is a headless account takeover.
|
|
56
71
|
*/
|
|
57
72
|
this.router.post(
|
|
58
73
|
`${new this.entityType().getCrudApiPath()?.toString()}/:userId/projects`,
|
|
59
|
-
MasterAdminAuthorization.
|
|
74
|
+
MasterAdminAuthorization.isAuthorizedMasterAdminOrMasterApiKeyMiddleware,
|
|
60
75
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
61
76
|
try {
|
|
62
77
|
const userId: ObjectID = UserAPI.getUserIdFromParams(req);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1786551733814 implements MigrationInterface {
|
|
4
|
+
public name: string = "MigrationName1786551733814";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "TelemetryEntity" ADD "source" character varying(100) NOT NULL DEFAULT 'discovered'`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "TelemetryEntity" ADD "description" character varying(500)`,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "TelemetryEntityRelationship" ADD "source" character varying(100) NOT NULL DEFAULT 'discovered'`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`CREATE INDEX "IDX_8106b1d6443d982b12e3318318" ON "TelemetryEntity" ("source") `,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`CREATE INDEX "IDX_26ea726e2ee8fa622348581ac1" ON "TelemetryEntityRelationship" ("source") `,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`DROP INDEX "public"."IDX_26ea726e2ee8fa622348581ac1"`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`DROP INDEX "public"."IDX_8106b1d6443d982b12e3318318"`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "TelemetryEntityRelationship" DROP COLUMN "source"`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "TelemetryEntity" DROP COLUMN "description"`,
|
|
36
|
+
);
|
|
37
|
+
await queryRunner.query(
|
|
38
|
+
`ALTER TABLE "TelemetryEntity" DROP COLUMN "source"`,
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}
|
package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddWorkflowLogStepTrace1786559879134
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string = "AddWorkflowLogStepTrace1786559879134";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(`ALTER TABLE "WorkflowLog" ADD "stepTrace" jsonb`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "WorkflowLog" DROP COLUMN "stepTrace"`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -515,6 +515,8 @@ import { AddDataSourceTable1786303472848 } from "./1786303472848-AddDataSourceTa
|
|
|
515
515
|
import { MigrationName1786446545142 } from "./1786446545142-MigrationName";
|
|
516
516
|
import { AddMonitorDependency1786449497966 } from "./1786449497966-AddMonitorDependency";
|
|
517
517
|
import { AddDeletedProjectTable1786461136170 } from "./1786461136170-AddDeletedProjectTable";
|
|
518
|
+
import { MigrationName1786551733814 } from "./1786551733814-MigrationName";
|
|
519
|
+
import { AddWorkflowLogStepTrace1786559879134 } from "./1786559879134-AddWorkflowLogStepTrace";
|
|
518
520
|
|
|
519
521
|
export default [
|
|
520
522
|
InitialMigration,
|
|
@@ -1034,4 +1036,6 @@ export default [
|
|
|
1034
1036
|
MigrationName1786446545142,
|
|
1035
1037
|
AddMonitorDependency1786449497966,
|
|
1036
1038
|
AddDeletedProjectTable1786461136170,
|
|
1039
|
+
MigrationName1786551733814,
|
|
1040
|
+
AddWorkflowLogStepTrace1786559879134,
|
|
1037
1041
|
];
|
|
@@ -59,13 +59,18 @@ export default class MasterAdminAuthorization {
|
|
|
59
59
|
* Same as isAuthorizedMasterAdminMiddleware, but ALSO accepts the instance-wide
|
|
60
60
|
* master API key (Admin Dashboard → Settings → API Key) supplied in the
|
|
61
61
|
* `apikey` header. The master key has root/master-admin access, so this lets
|
|
62
|
-
* automated callers reach
|
|
63
|
-
*
|
|
62
|
+
* automated callers reach master-admin endpoints with the key instead of a
|
|
63
|
+
* logged-in master-admin session.
|
|
64
64
|
*
|
|
65
|
-
* Deliberately scoped to the
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* Deliberately scoped to READ-ONLY routes: the OneUptime Health / diagnostics
|
|
66
|
+
* endpoints, and the cross-tenant read of one user's projects
|
|
67
|
+
* (POST /user/:userId/projects). It is NOT used on master-admin routes that
|
|
68
|
+
* change anything — the read/write query console, broadcast email, removing a
|
|
69
|
+
* user from a project, setting a password — which stay on the JWT-only
|
|
70
|
+
* middleware above so that a leaked static key cannot trigger them headlessly.
|
|
71
|
+
*
|
|
72
|
+
* Read-only is the line to hold when adding a route here. A key that can read
|
|
73
|
+
* discloses; a key that can write takes over.
|
|
69
74
|
*/
|
|
70
75
|
public static async isAuthorizedMasterAdminOrMasterApiKeyMiddleware(
|
|
71
76
|
req: ExpressRequest,
|
|
@@ -5,6 +5,7 @@ import OneUptimeDate from "../../Types/Date";
|
|
|
5
5
|
import logger from "../Utils/Logger";
|
|
6
6
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
7
7
|
import { EntityRelationshipEdge } from "../../Utils/Telemetry/EntityRelationship";
|
|
8
|
+
import EntitySource from "../../Types/Telemetry/EntitySource";
|
|
8
9
|
import { reconcileByNaturalKey } from "../Utils/Telemetry/EntityRegistry";
|
|
9
10
|
import QueryDeepPartialEntity from "../../Types/Database/PartialEntity";
|
|
10
11
|
|
|
@@ -62,6 +63,8 @@ export class TelemetryEntityRelationshipService extends DatabaseService<Model> {
|
|
|
62
63
|
model.fromEntityKey = edge.fromEntityKey;
|
|
63
64
|
model.toEntityKey = edge.toEntityKey;
|
|
64
65
|
model.relationshipType = edge.relationshipType;
|
|
66
|
+
// Explicit: this is what makes the edge eligible for stale pruning.
|
|
67
|
+
model.source = EntitySource.Discovered;
|
|
65
68
|
model.firstSeenAt = now;
|
|
66
69
|
model.lastSeenAt = now;
|
|
67
70
|
if (edge.metrics) {
|
|
@@ -8,8 +8,19 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
8
8
|
import OneUptimeDate from "../../Types/Date";
|
|
9
9
|
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
10
10
|
import QueryDeepPartialEntity from "../../Types/Database/PartialEntity";
|
|
11
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
12
|
+
import { OnCreate } from "../Types/Database/Hooks";
|
|
13
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
14
|
+
import Dictionary from "../../Types/Dictionary";
|
|
11
15
|
import { JSONObject } from "../../Types/JSON";
|
|
16
|
+
import EntitySource from "../../Types/Telemetry/EntitySource";
|
|
12
17
|
import EntityType from "../../Types/Telemetry/EntityType";
|
|
18
|
+
import { MANUAL_ENTITY_TYPES } from "../../Types/Telemetry/EntityTypeGroups";
|
|
19
|
+
import {
|
|
20
|
+
canonicalizeEntityValue,
|
|
21
|
+
computeEntityKey,
|
|
22
|
+
MANUAL_ENTITY_IDENTITY_ATTRIBUTE,
|
|
23
|
+
} from "../../Utils/Telemetry/EntityKey";
|
|
13
24
|
import logger from "../Utils/Logger";
|
|
14
25
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
26
|
import { ExtractedEntity } from "../Utils/Telemetry/TelemetryEntity";
|
|
@@ -25,6 +36,91 @@ export class TelemetryEntityService extends DatabaseService<Model> {
|
|
|
25
36
|
super(Model);
|
|
26
37
|
}
|
|
27
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Completes a manually created CI.
|
|
41
|
+
*
|
|
42
|
+
* Every machine writer — the ingest reconciler and the inventory mirror —
|
|
43
|
+
* computes `entityKey` itself and passes an explicit `source`, because
|
|
44
|
+
* both derive identity from something the user never sees (a canonicalized
|
|
45
|
+
* semconv attribute set, or the owning row's id). A create arriving
|
|
46
|
+
* WITHOUT a key is therefore a user create by construction, and this hook
|
|
47
|
+
* is what turns the two fields a human can reasonably supply (type and
|
|
48
|
+
* name) into the identity the registry needs.
|
|
49
|
+
*
|
|
50
|
+
* Deriving the key here rather than in the dashboard keeps it enforced for
|
|
51
|
+
* API clients too, and keeps the one identity rule — canonicalize, then
|
|
52
|
+
* hash — in the module that owns it.
|
|
53
|
+
*/
|
|
54
|
+
@CaptureSpan()
|
|
55
|
+
protected override async onBeforeCreate(
|
|
56
|
+
createBy: CreateBy<Model>,
|
|
57
|
+
): Promise<OnCreate<Model>> {
|
|
58
|
+
const data: Model = createBy.data;
|
|
59
|
+
|
|
60
|
+
if (!data.entityKey) {
|
|
61
|
+
data.source = EntitySource.Manual;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (data.source !== EntitySource.Manual) {
|
|
65
|
+
return { createBy, carryForward: null };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!data.projectId) {
|
|
69
|
+
throw new BadDataException("Project ID is required.");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!data.entityType) {
|
|
73
|
+
throw new BadDataException("Entity Type is required.");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Confined to the manual-only vocabulary. A hand-made row of an
|
|
78
|
+
* observable type would key off the manual identity attribute and so
|
|
79
|
+
* could never converge with the row ingest derives for the same thing —
|
|
80
|
+
* it would just sit beside it forever. See MANUAL_ENTITY_TYPES.
|
|
81
|
+
*/
|
|
82
|
+
if (!MANUAL_ENTITY_TYPES.has(data.entityType)) {
|
|
83
|
+
throw new BadDataException(
|
|
84
|
+
`Entities of type ${data.entityType} are discovered automatically and cannot be created manually. Manually creatable types are: ${Array.from(
|
|
85
|
+
MANUAL_ENTITY_TYPES,
|
|
86
|
+
).join(", ")}.`,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const displayName: string = (data.displayName || "").trim();
|
|
91
|
+
|
|
92
|
+
if (!displayName) {
|
|
93
|
+
throw new BadDataException(
|
|
94
|
+
"Name is required for a manually created entity.",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
data.displayName = displayName.substring(0, ColumnLength.ShortText);
|
|
99
|
+
|
|
100
|
+
const identifyingAttributes: Dictionary<string> = {
|
|
101
|
+
[MANUAL_ENTITY_IDENTITY_ATTRIBUTE]: canonicalizeEntityValue(displayName),
|
|
102
|
+
};
|
|
103
|
+
data.identifyingAttributes = identifyingAttributes as JSONObject;
|
|
104
|
+
|
|
105
|
+
data.entityKey = computeEntityKey({
|
|
106
|
+
projectId: data.projectId.toString(),
|
|
107
|
+
entityType: data.entityType,
|
|
108
|
+
identifyingAttributes,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
/*
|
|
112
|
+
* Stamped so the explorer can sort manual CIs alongside discovered ones
|
|
113
|
+
* on the same columns. `lastSeenAt` never moves again for these rows —
|
|
114
|
+
* it means "created", not "observed" — which is exactly why the prune
|
|
115
|
+
* sweep excludes them.
|
|
116
|
+
*/
|
|
117
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
118
|
+
data.firstSeenAt = data.firstSeenAt || now;
|
|
119
|
+
data.lastSeenAt = data.lastSeenAt || now;
|
|
120
|
+
|
|
121
|
+
return { createBy, carryForward: null };
|
|
122
|
+
}
|
|
123
|
+
|
|
28
124
|
/**
|
|
29
125
|
* Forward-only registry reconciliation: upsert a catalog row per
|
|
30
126
|
* discovered entity, bump `lastSeenAt`, merge descriptive attributes
|
|
@@ -169,7 +265,18 @@ export class TelemetryEntityService extends DatabaseService<Model> {
|
|
|
169
265
|
if (count === undefined) {
|
|
170
266
|
count = (
|
|
171
267
|
await this.countBy({
|
|
172
|
-
query: {
|
|
268
|
+
query: {
|
|
269
|
+
projectId,
|
|
270
|
+
entityType: entity.entityType,
|
|
271
|
+
/*
|
|
272
|
+
* The budget exists to bound rows minted by ingest, so it
|
|
273
|
+
* counts only those. Manual CIs and inventory mirrors are
|
|
274
|
+
* bounded by their own creators and must not consume it —
|
|
275
|
+
* otherwise registering enough devices could stop new
|
|
276
|
+
* telemetry entities being recorded at all.
|
|
277
|
+
*/
|
|
278
|
+
source: EntitySource.Discovered,
|
|
279
|
+
},
|
|
173
280
|
props: { isRoot: true },
|
|
174
281
|
})
|
|
175
282
|
).toNumber();
|
|
@@ -198,6 +305,12 @@ export class TelemetryEntityService extends DatabaseService<Model> {
|
|
|
198
305
|
model.projectId = projectId;
|
|
199
306
|
model.entityType = entity.entityType;
|
|
200
307
|
model.entityKey = entity.entityKey;
|
|
308
|
+
/*
|
|
309
|
+
* Explicit rather than leaning on the column default: this is the
|
|
310
|
+
* flag that makes the row eligible for stale-entity pruning, and a
|
|
311
|
+
* silently-defaulted value is a poor way to carry that.
|
|
312
|
+
*/
|
|
313
|
+
model.source = EntitySource.Discovered;
|
|
201
314
|
model.identifyingAttributes = entity.identifyingAttributes;
|
|
202
315
|
if (
|
|
203
316
|
entity.descriptiveAttributes &&
|
|
@@ -4,7 +4,7 @@ import HTTPResponse from "../../../../../Types/API/HTTPResponse";
|
|
|
4
4
|
import URL from "../../../../../Types/API/URL";
|
|
5
5
|
import BadDataException from "../../../../../Types/Exception/BadDataException";
|
|
6
6
|
import Exception from "../../../../../Types/Exception/Exception";
|
|
7
|
-
import { JSONObject } from "../../../../../Types/JSON";
|
|
7
|
+
import { JSONObject, JSONValue } from "../../../../../Types/JSON";
|
|
8
8
|
import JSONFunctions from "../../../../../Types/JSONFunctions";
|
|
9
9
|
import ComponentMetadata, {
|
|
10
10
|
Port,
|
|
@@ -82,6 +82,49 @@ export class ApiComponentUtils {
|
|
|
82
82
|
);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/*
|
|
86
|
+
* Headers reach here from a key/value grid that can emit numbers and
|
|
87
|
+
* booleans, from a JSON document someone typed, or from a {{...}}
|
|
88
|
+
* substitution — and every consumer below blind-casts the result to
|
|
89
|
+
* Dictionary<string> before spreading it into the outgoing request. An
|
|
90
|
+
* array or a bare scalar spreads into per-index junk headers rather than
|
|
91
|
+
* failing, so check the shape and flatten the values to strings here.
|
|
92
|
+
*/
|
|
93
|
+
if (args["request-headers"]) {
|
|
94
|
+
const headers: JSONValue = args["request-headers"] as JSONValue;
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
typeof headers !== "object" ||
|
|
98
|
+
headers === null ||
|
|
99
|
+
Array.isArray(headers)
|
|
100
|
+
) {
|
|
101
|
+
throw options.onError(
|
|
102
|
+
new BadDataException(
|
|
103
|
+
"Request headers must be a JSON object of header names and values.",
|
|
104
|
+
),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const stringifiedHeaders: JSONObject = {};
|
|
109
|
+
|
|
110
|
+
for (const headerName of Object.keys(headers as JSONObject)) {
|
|
111
|
+
const headerValue: JSONValue = (headers as JSONObject)[
|
|
112
|
+
headerName
|
|
113
|
+
] as JSONValue;
|
|
114
|
+
|
|
115
|
+
if (headerValue === null || headerValue === undefined) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
stringifiedHeaders[headerName] =
|
|
120
|
+
typeof headerValue === "object"
|
|
121
|
+
? JSON.stringify(headerValue)
|
|
122
|
+
: String(headerValue);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
args["request-headers"] = stringifiedHeaders;
|
|
126
|
+
}
|
|
127
|
+
|
|
85
128
|
if (!args["url"]) {
|
|
86
129
|
throw options.onError(new BadDataException("URL not found"));
|
|
87
130
|
}
|
|
@@ -20,6 +20,18 @@ export interface ExecuteWorkflowType {
|
|
|
20
20
|
* should leave this undefined.
|
|
21
21
|
*/
|
|
22
22
|
callChain?: Array<string>;
|
|
23
|
+
/**
|
|
24
|
+
* When set, the run executes only this one component instead of starting at
|
|
25
|
+
* the trigger and following the graph. Backs the builder's "run just this
|
|
26
|
+
* step" button.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately routed through the queue like any other run, so it inherits
|
|
29
|
+
* every gate a run has: the workflow must be enabled, the subscription must
|
|
30
|
+
* be paid, the plan's run limit applies, a WorkflowLog is written for the
|
|
31
|
+
* audit trail, and the component executes on a worker rather than inside the
|
|
32
|
+
* API process.
|
|
33
|
+
*/
|
|
34
|
+
runOnlyComponentId?: string | undefined;
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
export interface InitProps {
|
|
@@ -19,4 +19,9 @@ export interface RunProps {
|
|
|
19
19
|
* depth across workflow boundaries.
|
|
20
20
|
*/
|
|
21
21
|
callChain?: Array<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Execute only this component (by its user-facing id) instead of walking the
|
|
24
|
+
* graph from the trigger. See ExecuteWorkflowType.runOnlyComponentId.
|
|
25
|
+
*/
|
|
26
|
+
runOnlyComponentId?: string | undefined;
|
|
22
27
|
}
|