@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
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* How a `TelemetryEntity` registry row came to exist. The registry started
|
|
3
|
+
* as a forward-only catalog populated purely from OTLP resource attributes;
|
|
4
|
+
* two later sources join it, and the three differ in exactly one way that
|
|
5
|
+
* matters — who owns the row's lifecycle.
|
|
6
|
+
*
|
|
7
|
+
* That ownership is what `PruneStaleEntities` keys on. It reaps rows whose
|
|
8
|
+
* `lastSeenAt` has aged past their type's TTL, which is only a correct
|
|
9
|
+
* staleness signal when something is actively re-bumping `lastSeenAt`. Two
|
|
10
|
+
* of the three sources have nothing doing that, so the prune sweep is
|
|
11
|
+
* scoped to `Discovered` alone — see the guard there.
|
|
12
|
+
*/
|
|
13
|
+
enum EntitySource {
|
|
14
|
+
/**
|
|
15
|
+
* Derived from telemetry resource attributes at ingest and re-bumped on
|
|
16
|
+
* every reconcile window for as long as the entity keeps emitting. Silence
|
|
17
|
+
* past the type's TTL genuinely means gone, so these are TTL-pruned.
|
|
18
|
+
*/
|
|
19
|
+
Discovered = "discovered",
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Mirrored from a OneUptime inventory table (NetworkDevice, CloudResource,
|
|
23
|
+
* IoTDevice, ...). These are collected by pollers rather than OTLP, so no
|
|
24
|
+
* ingest path bumps `lastSeenAt` — the owning row IS the liveness signal,
|
|
25
|
+
* and the mirror is kept in step by that table's create/delete hooks. TTL
|
|
26
|
+
* pruning one of these would delete the registry row for a device that is
|
|
27
|
+
* still very much registered.
|
|
28
|
+
*/
|
|
29
|
+
Inventory = "inventory",
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Created by a user for something that emits no telemetry at all — a
|
|
33
|
+
* third-party API, a vendor-managed database, an appliance. Nothing will
|
|
34
|
+
* ever bump `lastSeenAt`, so these are never TTL-pruned; they live until
|
|
35
|
+
* the user deletes them.
|
|
36
|
+
*/
|
|
37
|
+
Manual = "manual",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default EntitySource;
|
|
@@ -51,6 +51,33 @@ enum EntityType {
|
|
|
51
51
|
DockerSwarmService = "docker.swarm.service",
|
|
52
52
|
DockerSwarmTask = "docker.swarm.task",
|
|
53
53
|
TelemetrySdk = "telemetry.sdk",
|
|
54
|
+
/*
|
|
55
|
+
* Inventory-mirrored types. Unlike everything above, these are never
|
|
56
|
+
* derived from an OTLP resource — the estate they describe is collected
|
|
57
|
+
* by pollers (SNMP, cloud APIs, MQTT) into rich typed tables, and the
|
|
58
|
+
* registry row is a mirror of that row so the entity explorer can show
|
|
59
|
+
* one estate instead of one telemetry pipeline. Their identity is the
|
|
60
|
+
* owning row's own project-unique key, and their lifecycle follows it
|
|
61
|
+
* (see InventoryEntityRegistry); they carry EntitySource.Inventory and
|
|
62
|
+
* are deliberately absent from the prune TTL map.
|
|
63
|
+
*/
|
|
64
|
+
NetworkDevice = "network.device",
|
|
65
|
+
CloudResource = "cloud.resource",
|
|
66
|
+
ServerlessFunction = "serverless.function",
|
|
67
|
+
RumApplication = "rum.application",
|
|
68
|
+
IoTDevice = "iot.device",
|
|
69
|
+
DockerHost = "docker.host",
|
|
70
|
+
PodmanHost = "podman.host",
|
|
71
|
+
/*
|
|
72
|
+
* Manual (user-authored) types, for the parts of an estate that emit no
|
|
73
|
+
* telemetry and appear in no inventory table: a third-party payment API,
|
|
74
|
+
* a vendor-managed database, a physical appliance. They exist so a
|
|
75
|
+
* dependency edge can terminate somewhere real instead of dangling at
|
|
76
|
+
* the boundary of what OneUptime can see. Carry EntitySource.Manual.
|
|
77
|
+
*/
|
|
78
|
+
ExternalService = "external.service",
|
|
79
|
+
ExternalDatabase = "external.database",
|
|
80
|
+
Appliance = "appliance",
|
|
54
81
|
}
|
|
55
82
|
|
|
56
83
|
export default EntityType;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import EntityType from "./EntityType";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Partitions of the EntityType vocabulary by who creates and owns a row of
|
|
5
|
+
* that type. Isomorphic (plain Types, no server imports) because all three
|
|
6
|
+
* consumers need the same answer: the service enforcing what a user may
|
|
7
|
+
* create, the dashboard populating the type dropdown, and the tests pinning
|
|
8
|
+
* that the prune sweep cannot reach a type nothing re-bumps.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately expressed as explicit sets rather than a string-prefix test.
|
|
11
|
+
* Prefix matching would silently absorb any future `external.*` or
|
|
12
|
+
* `*.device` type into the set that is exempt from pruning, which is the
|
|
13
|
+
* kind of default that goes unnoticed until a registry fills with rows
|
|
14
|
+
* nothing can ever reap.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Types a user may create by hand. These describe things OneUptime cannot
|
|
19
|
+
* observe — a third-party API, a vendor-managed database, an appliance —
|
|
20
|
+
* so nothing will ever discover them and nothing will ever bump their
|
|
21
|
+
* `lastSeenAt`.
|
|
22
|
+
*
|
|
23
|
+
* Manual creation is confined to these on purpose. A hand-made row of an
|
|
24
|
+
* observable type (say `service`) would mint an entity key from the manual
|
|
25
|
+
* identity attribute, which by construction differs from the key ingest
|
|
26
|
+
* derives from `service.name` — so the manual row would sit beside the
|
|
27
|
+
* discovered one as a permanent near-duplicate rather than merging with it.
|
|
28
|
+
*/
|
|
29
|
+
export const MANUAL_ENTITY_TYPES: ReadonlySet<EntityType> = new Set<EntityType>(
|
|
30
|
+
[
|
|
31
|
+
EntityType.ExternalService,
|
|
32
|
+
EntityType.ExternalDatabase,
|
|
33
|
+
EntityType.Appliance,
|
|
34
|
+
],
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Types mirrored from a OneUptime inventory table. The owning row is the
|
|
39
|
+
* source of truth for both identity and liveness; the registry row is a
|
|
40
|
+
* projection kept in step by that table's create/delete hooks.
|
|
41
|
+
*/
|
|
42
|
+
export const INVENTORY_ENTITY_TYPES: ReadonlySet<EntityType> =
|
|
43
|
+
new Set<EntityType>([
|
|
44
|
+
EntityType.NetworkDevice,
|
|
45
|
+
EntityType.CloudResource,
|
|
46
|
+
EntityType.ServerlessFunction,
|
|
47
|
+
EntityType.RumApplication,
|
|
48
|
+
EntityType.IoTDevice,
|
|
49
|
+
EntityType.DockerHost,
|
|
50
|
+
EntityType.PodmanHost,
|
|
51
|
+
]);
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* True for types whose rows have no telemetry heartbeat, and which are
|
|
55
|
+
* therefore never eligible for `lastSeenAt`-based pruning. The prune job
|
|
56
|
+
* enforces this through the `source` column instead (a row's source is the
|
|
57
|
+
* authority, not its type); this predicate is what the tests assert the TTL
|
|
58
|
+
* map against, so a newly added type cannot quietly acquire a TTL.
|
|
59
|
+
*/
|
|
60
|
+
export function isNonTelemetryEntityType(entityType: EntityType): boolean {
|
|
61
|
+
return (
|
|
62
|
+
MANUAL_ENTITY_TYPES.has(entityType) ||
|
|
63
|
+
INVENTORY_ENTITY_TYPES.has(entityType)
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -30,6 +30,35 @@ export enum ComponentInputType {
|
|
|
30
30
|
WorkflowSelect = "Workflow Select",
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/*
|
|
34
|
+
* Argument types whose value is a JSON document but is read with
|
|
35
|
+
* JSONFunctions.parse — that is, JSON5 — rather than JSON.parse. Unquoted keys,
|
|
36
|
+
* single quotes and trailing commas therefore work in these, and a workflow
|
|
37
|
+
* written that way runs fine today.
|
|
38
|
+
*
|
|
39
|
+
* The distinction matters wherever a JSON document is checked for syntax
|
|
40
|
+
* before it runs: applying JSON.parse's rules to one of these would report a
|
|
41
|
+
* mistake in a workflow that does not have one. The strict set is everything
|
|
42
|
+
* else — RunWorkflow.getComponentArguments calls JSON.parse directly on
|
|
43
|
+
* JSON, Query and Select, so those really must be JSON.
|
|
44
|
+
*/
|
|
45
|
+
const JSON5_TOLERANT_INPUT_TYPES: Array<ComponentInputType> = [
|
|
46
|
+
ComponentInputType.StringDictionary,
|
|
47
|
+
ComponentInputType.JSONArray,
|
|
48
|
+
ComponentInputType.BaseModel,
|
|
49
|
+
ComponentInputType.BaseModelArray,
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
export type IsJSON5ToleratedInputTypeFunction = (
|
|
53
|
+
type: ComponentInputType,
|
|
54
|
+
) => boolean;
|
|
55
|
+
|
|
56
|
+
export const isJSON5ToleratedInputType: IsJSON5ToleratedInputTypeFunction = (
|
|
57
|
+
type: ComponentInputType,
|
|
58
|
+
): boolean => {
|
|
59
|
+
return JSON5_TOLERANT_INPUT_TYPES.includes(type);
|
|
60
|
+
};
|
|
61
|
+
|
|
33
62
|
export enum ComponentType {
|
|
34
63
|
Trigger = "Trigger",
|
|
35
64
|
Component = "Component",
|
|
@@ -37,6 +37,13 @@ const components: Array<ComponentMetadata> = [
|
|
|
37
37
|
type: ComponentInputType.StringDictionary,
|
|
38
38
|
required: false,
|
|
39
39
|
isAdvanced: true,
|
|
40
|
+
/*
|
|
41
|
+
* Headers are where an Authorization bearer token gets typed, and
|
|
42
|
+
* without this the resolved value is written verbatim into the
|
|
43
|
+
* WorkflowLog for anyone with read access to the project. Only values
|
|
44
|
+
* that came from a variable marked secret were ever scrubbed.
|
|
45
|
+
*/
|
|
46
|
+
isSensitive: true,
|
|
40
47
|
placeholder:
|
|
41
48
|
'Example: {"header1": "value1", "header2": "value2", ....}',
|
|
42
49
|
},
|
|
@@ -123,6 +130,13 @@ const components: Array<ComponentMetadata> = [
|
|
|
123
130
|
type: ComponentInputType.StringDictionary,
|
|
124
131
|
required: false,
|
|
125
132
|
isAdvanced: true,
|
|
133
|
+
/*
|
|
134
|
+
* Headers are where an Authorization bearer token gets typed, and
|
|
135
|
+
* without this the resolved value is written verbatim into the
|
|
136
|
+
* WorkflowLog for anyone with read access to the project. Only values
|
|
137
|
+
* that came from a variable marked secret were ever scrubbed.
|
|
138
|
+
*/
|
|
139
|
+
isSensitive: true,
|
|
126
140
|
placeholder:
|
|
127
141
|
'Example: {"header1": "value1", "header2": "value2", ....}',
|
|
128
142
|
},
|
|
@@ -209,6 +223,13 @@ const components: Array<ComponentMetadata> = [
|
|
|
209
223
|
type: ComponentInputType.StringDictionary,
|
|
210
224
|
required: false,
|
|
211
225
|
isAdvanced: true,
|
|
226
|
+
/*
|
|
227
|
+
* Headers are where an Authorization bearer token gets typed, and
|
|
228
|
+
* without this the resolved value is written verbatim into the
|
|
229
|
+
* WorkflowLog for anyone with read access to the project. Only values
|
|
230
|
+
* that came from a variable marked secret were ever scrubbed.
|
|
231
|
+
*/
|
|
232
|
+
isSensitive: true,
|
|
212
233
|
placeholder:
|
|
213
234
|
'Example: {"header1": "value1", "header2": "value2", ....}',
|
|
214
235
|
},
|
|
@@ -295,6 +316,13 @@ const components: Array<ComponentMetadata> = [
|
|
|
295
316
|
type: ComponentInputType.StringDictionary,
|
|
296
317
|
required: false,
|
|
297
318
|
isAdvanced: true,
|
|
319
|
+
/*
|
|
320
|
+
* Headers are where an Authorization bearer token gets typed, and
|
|
321
|
+
* without this the resolved value is written verbatim into the
|
|
322
|
+
* WorkflowLog for anyone with read access to the project. Only values
|
|
323
|
+
* that came from a variable marked secret were ever scrubbed.
|
|
324
|
+
*/
|
|
325
|
+
isSensitive: true,
|
|
298
326
|
placeholder:
|
|
299
327
|
'Example: {"header1": "value1", "header2": "value2", ....}',
|
|
300
328
|
},
|
|
@@ -381,6 +409,13 @@ const components: Array<ComponentMetadata> = [
|
|
|
381
409
|
type: ComponentInputType.StringDictionary,
|
|
382
410
|
required: false,
|
|
383
411
|
isAdvanced: true,
|
|
412
|
+
/*
|
|
413
|
+
* Headers are where an Authorization bearer token gets typed, and
|
|
414
|
+
* without this the resolved value is written verbatim into the
|
|
415
|
+
* WorkflowLog for anyone with read access to the project. Only values
|
|
416
|
+
* that came from a variable marked secret were ever scrubbed.
|
|
417
|
+
*/
|
|
418
|
+
isSensitive: true,
|
|
384
419
|
placeholder:
|
|
385
420
|
'Example: {"header1": "value1", "header2": "value2", ....}',
|
|
386
421
|
},
|
|
@@ -19,9 +19,13 @@ export default class BaseModelComponent {
|
|
|
19
19
|
* Every one of these arguments is keyed on column names, and the single
|
|
20
20
|
* most common way to get one wrong is to write "id" - which is what an ID
|
|
21
21
|
* is called everywhere else in the product - when the column is "_id".
|
|
22
|
-
* Both spellings work
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* Both spellings work.
|
|
23
|
+
*
|
|
24
|
+
* Query and record arguments now get a row editor backed by the model's
|
|
25
|
+
* real column list (ModelColumnEditor), so the names are offered rather
|
|
26
|
+
* than remembered. The hint stays because that editor keeps a JSON escape
|
|
27
|
+
* hatch for the queries rows cannot express, and because it is still the
|
|
28
|
+
* only thing explaining the two spellings.
|
|
25
29
|
*/
|
|
26
30
|
const columnHint: string = `Keys are column names on ${model.singularName}. The ID column is "_id" (the "id" spelling is accepted too).`;
|
|
27
31
|
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A structured record of what each step of a workflow run actually did.
|
|
3
|
+
*
|
|
4
|
+
* The runner has always known this — it logs the arguments it resolved, the
|
|
5
|
+
* values a component returned, and which out port it took — but it flattened
|
|
6
|
+
* all of it into one newline-joined string, so the only way to answer "what did
|
|
7
|
+
* step three receive" was to read the whole blob. Keeping the same information
|
|
8
|
+
* as data lets the run be read as a list of steps, and lets a step be matched
|
|
9
|
+
* back to the node it came from on the canvas.
|
|
10
|
+
*
|
|
11
|
+
* The shapes live in Common/Types because the runner writes them, the API
|
|
12
|
+
* returns them, and the dashboard renders them.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { JSONObject, JSONValue } from "../JSON";
|
|
16
|
+
|
|
17
|
+
export enum WorkflowStepStatus {
|
|
18
|
+
Success = "Success",
|
|
19
|
+
Error = "Error",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface WorkflowStepTraceEntry {
|
|
23
|
+
/** The step's user-facing id ("api-get-1") — matches a node on the canvas. */
|
|
24
|
+
componentId: string;
|
|
25
|
+
/** Which kind of component this was, e.g. "api-post". */
|
|
26
|
+
metadataId: string;
|
|
27
|
+
/** The component's title, so the trace reads without loading metadata. */
|
|
28
|
+
title: string;
|
|
29
|
+
status: WorkflowStepStatus;
|
|
30
|
+
startedAt: string;
|
|
31
|
+
completedAt: string;
|
|
32
|
+
durationInMs: number;
|
|
33
|
+
/** Resolved arguments, already redacted for sensitive fields. */
|
|
34
|
+
argumentValues: JSONObject;
|
|
35
|
+
/** Returned values, already redacted for sensitive fields. */
|
|
36
|
+
returnValues: JSONObject;
|
|
37
|
+
/** The out port taken, or null when the step ended the run. */
|
|
38
|
+
executedPort: string | null;
|
|
39
|
+
/** Present only on a failed step. */
|
|
40
|
+
errorMessage?: string | undefined;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface WorkflowStepTrace {
|
|
44
|
+
steps: Array<WorkflowStepTraceEntry>;
|
|
45
|
+
/**
|
|
46
|
+
* Set when steps were dropped to keep the row bounded. The dashboard says so
|
|
47
|
+
* rather than quietly showing a partial run as if it were complete.
|
|
48
|
+
*/
|
|
49
|
+
truncated?: boolean | undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/*
|
|
53
|
+
* A run is bounded by cycle detection, but a workflow that suspends and resumes
|
|
54
|
+
* accumulates across resumes, and a single value can be an entire HTTP response
|
|
55
|
+
* body. Both are capped: the row is written on every status change, and an
|
|
56
|
+
* unbounded trace would turn each of those writes into a large one.
|
|
57
|
+
*/
|
|
58
|
+
export const MAX_TRACE_STEPS: number = 100;
|
|
59
|
+
export const MAX_TRACE_VALUE_LENGTH: number = 4000;
|
|
60
|
+
|
|
61
|
+
export const TRUNCATED_VALUE_SUFFIX: string = "… (truncated)";
|
|
62
|
+
|
|
63
|
+
export type TruncateTraceValueFunction = (value: JSONValue) => JSONValue;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Shrink one recorded value to something a row can hold.
|
|
67
|
+
*
|
|
68
|
+
* Strings are cut; anything structured is measured by its JSON length and
|
|
69
|
+
* replaced wholesale when it is too big, because cutting a serialized object in
|
|
70
|
+
* half produces text that looks like data but cannot be parsed.
|
|
71
|
+
*/
|
|
72
|
+
export const truncateTraceValue: TruncateTraceValueFunction = (
|
|
73
|
+
value: JSONValue,
|
|
74
|
+
): JSONValue => {
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
if (value.length <= MAX_TRACE_VALUE_LENGTH) {
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return value.slice(0, MAX_TRACE_VALUE_LENGTH) + TRUNCATED_VALUE_SUFFIX;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (value === null || value === undefined) {
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (typeof value !== "object") {
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let serialized: string;
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
serialized = JSON.stringify(value);
|
|
95
|
+
} catch {
|
|
96
|
+
// Circular or otherwise unserializable: say so rather than throwing.
|
|
97
|
+
return "[value could not be recorded]";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (serialized.length <= MAX_TRACE_VALUE_LENGTH) {
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return serialized.slice(0, MAX_TRACE_VALUE_LENGTH) + TRUNCATED_VALUE_SUFFIX;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export type TruncateTraceValuesFunction = (values: JSONObject) => JSONObject;
|
|
108
|
+
|
|
109
|
+
export const truncateTraceValues: TruncateTraceValuesFunction = (
|
|
110
|
+
values: JSONObject,
|
|
111
|
+
): JSONObject => {
|
|
112
|
+
const truncated: JSONObject = {};
|
|
113
|
+
|
|
114
|
+
for (const key of Object.keys(values || {})) {
|
|
115
|
+
truncated[key] = truncateTraceValue(values[key] as JSONValue);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return truncated;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export type AppendTraceStepFunction = (
|
|
122
|
+
trace: WorkflowStepTrace,
|
|
123
|
+
entry: WorkflowStepTraceEntry,
|
|
124
|
+
) => WorkflowStepTrace;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Add a step, keeping the trace within its cap.
|
|
128
|
+
*
|
|
129
|
+
* When the cap is reached the OLDEST steps are dropped: a run that failed is
|
|
130
|
+
* read from the end, and the step that broke it is the last one.
|
|
131
|
+
*/
|
|
132
|
+
export const appendTraceStep: AppendTraceStepFunction = (
|
|
133
|
+
trace: WorkflowStepTrace,
|
|
134
|
+
entry: WorkflowStepTraceEntry,
|
|
135
|
+
): WorkflowStepTrace => {
|
|
136
|
+
const steps: Array<WorkflowStepTraceEntry> = [...(trace.steps || []), entry];
|
|
137
|
+
|
|
138
|
+
if (steps.length <= MAX_TRACE_STEPS) {
|
|
139
|
+
return { ...trace, steps: steps };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
...trace,
|
|
144
|
+
steps: steps.slice(steps.length - MAX_TRACE_STEPS),
|
|
145
|
+
truncated: true,
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export type EmptyTraceFunction = () => WorkflowStepTrace;
|
|
150
|
+
|
|
151
|
+
export const emptyTrace: EmptyTraceFunction = (): WorkflowStepTrace => {
|
|
152
|
+
return { steps: [] };
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export type ParseTraceFunction = (value: JSONValue) => WorkflowStepTrace;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Read a trace back off a row, tolerating anything that is not one.
|
|
159
|
+
*
|
|
160
|
+
* Old runs predate the column and rows can be written by an older build, so
|
|
161
|
+
* this never throws — a trace it cannot understand reads as an empty one and
|
|
162
|
+
* the viewer falls back to the raw log.
|
|
163
|
+
*/
|
|
164
|
+
export const parseTrace: ParseTraceFunction = (
|
|
165
|
+
value: JSONValue,
|
|
166
|
+
): WorkflowStepTrace => {
|
|
167
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
168
|
+
return emptyTrace();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const steps: JSONValue | undefined = (value as JSONObject)["steps"];
|
|
172
|
+
|
|
173
|
+
if (!Array.isArray(steps)) {
|
|
174
|
+
return emptyTrace();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
steps: steps as unknown as Array<WorkflowStepTraceEntry>,
|
|
179
|
+
truncated: Boolean((value as JSONObject)["truncated"]),
|
|
180
|
+
};
|
|
181
|
+
};
|