@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.
Files changed (186) hide show
  1. package/Models/DatabaseModels/TelemetryEntity.ts +45 -0
  2. package/Models/DatabaseModels/TelemetryEntityRelationship.ts +26 -0
  3. package/Models/DatabaseModels/WorkflowLog.ts +38 -0
  4. package/Server/API/UserAPI.ts +16 -1
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  8. package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
  9. package/Server/Services/TelemetryEntityRelationshipService.ts +3 -0
  10. package/Server/Services/TelemetryEntityService.ts +114 -1
  11. package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
  12. package/Server/Types/Workflow/TriggerCode.ts +12 -0
  13. package/Server/Types/Workflow/Workflow.ts +5 -0
  14. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
  15. package/Server/Utils/VM/VMAPI.ts +56 -8
  16. package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
  17. package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
  18. package/Tests/Server/Services/TelemetryEntityManualCreate.test.ts +248 -0
  19. package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
  20. package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
  21. package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
  22. package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
  23. package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
  24. package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
  25. package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
  26. package/Tests/Types/Workflow/TemplateSyntax.test.ts +379 -0
  27. package/Tests/Types/Workflow/Templates.test.ts +403 -0
  28. package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
  29. package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
  30. package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
  31. package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
  32. package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
  33. package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
  34. package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
  35. package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
  36. package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
  37. package/Tests/UI/Components/Workflow/GraphLint.test.ts +821 -0
  38. package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +287 -0
  39. package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +185 -0
  40. package/Tests/UI/Components/Workflow/ModelSchema.test.ts +174 -0
  41. package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +152 -0
  42. package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
  43. package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
  44. package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
  45. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +240 -0
  46. package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
  47. package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
  48. package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
  49. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +31 -0
  50. package/Types/SerializableObjectDictionary.ts +133 -39
  51. package/Types/Telemetry/EntitySource.ts +40 -0
  52. package/Types/Telemetry/EntityType.ts +27 -0
  53. package/Types/Telemetry/EntityTypeGroups.ts +65 -0
  54. package/Types/Workflow/Component.ts +29 -0
  55. package/Types/Workflow/Components/API.ts +35 -0
  56. package/Types/Workflow/Components/BaseModel.ts +7 -3
  57. package/Types/Workflow/StepTrace.ts +181 -0
  58. package/Types/Workflow/TemplateSyntax.ts +499 -0
  59. package/Types/Workflow/Templates.ts +316 -0
  60. package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
  61. package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
  62. package/UI/Components/Dictionary/Dictionary.tsx +23 -6
  63. package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
  64. package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
  65. package/UI/Components/Forms/Types/Field.ts +7 -0
  66. package/UI/Components/Forms/Validation.ts +63 -1
  67. package/UI/Components/Input/Input.tsx +31 -3
  68. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
  69. package/UI/Components/Modal/Modal.tsx +37 -4
  70. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
  71. package/UI/Components/Workflow/ArgumentsForm.tsx +194 -7
  72. package/UI/Components/Workflow/Component.tsx +28 -26
  73. package/UI/Components/Workflow/ComponentSettingsModal.tsx +69 -8
  74. package/UI/Components/Workflow/GraphLint.ts +591 -0
  75. package/UI/Components/Workflow/ModelColumnEditor.tsx +396 -0
  76. package/UI/Components/Workflow/ModelSchema.ts +196 -0
  77. package/UI/Components/Workflow/RunForm.tsx +41 -7
  78. package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
  79. package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
  80. package/UI/Components/Workflow/Utils.ts +93 -1
  81. package/UI/Components/Workflow/Workflow.tsx +77 -4
  82. package/UI/Types/LayeredDismissal.ts +31 -0
  83. package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
  84. package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
  85. package/Utils/Monitor/NetworkTopologyUtil.ts +45 -7
  86. package/Utils/Telemetry/EntityKey.ts +66 -0
  87. package/build/dist/Models/DatabaseModels/TelemetryEntity.js +47 -0
  88. package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +1 -1
  89. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js +27 -0
  90. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +1 -1
  91. package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
  92. package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
  93. package/build/dist/Server/API/UserAPI.js +16 -1
  94. package/build/dist/Server/API/UserAPI.js.map +1 -1
  95. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
  96. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
  97. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
  98. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
  99. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  100. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  101. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
  102. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
  103. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +3 -0
  104. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  105. package/build/dist/Server/Services/TelemetryEntityService.js +91 -1
  106. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  107. package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
  108. package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
  109. package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
  110. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
  111. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
  112. package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
  113. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  114. package/build/dist/Types/SerializableObjectDictionary.js +133 -39
  115. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  116. package/build/dist/Types/Telemetry/EntitySource.js +39 -0
  117. package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
  118. package/build/dist/Types/Telemetry/EntityType.js +27 -0
  119. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  120. package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
  121. package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
  122. package/build/dist/Types/Workflow/Component.js +21 -0
  123. package/build/dist/Types/Workflow/Component.js.map +1 -1
  124. package/build/dist/Types/Workflow/Components/API.js +35 -0
  125. package/build/dist/Types/Workflow/Components/API.js.map +1 -1
  126. package/build/dist/Types/Workflow/Components/BaseModel.js +7 -3
  127. package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
  128. package/build/dist/Types/Workflow/StepTrace.js +104 -0
  129. package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
  130. package/build/dist/Types/Workflow/TemplateSyntax.js +326 -0
  131. package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
  132. package/build/dist/Types/Workflow/Templates.js +218 -0
  133. package/build/dist/Types/Workflow/Templates.js.map +1 -0
  134. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
  135. package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
  136. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
  137. package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
  138. package/build/dist/UI/Components/Dictionary/Dictionary.js +22 -6
  139. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  140. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
  141. package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
  142. package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
  143. package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
  144. package/build/dist/UI/Components/Forms/Validation.js +44 -0
  145. package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
  146. package/build/dist/UI/Components/Input/Input.js +12 -4
  147. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  148. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
  149. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  150. package/build/dist/UI/Components/Modal/Modal.js +31 -5
  151. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  152. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
  153. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  154. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +140 -11
  155. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  156. package/build/dist/UI/Components/Workflow/Component.js +20 -19
  157. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  158. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +31 -3
  159. package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
  160. package/build/dist/UI/Components/Workflow/GraphLint.js +396 -0
  161. package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
  162. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +225 -0
  163. package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
  164. package/build/dist/UI/Components/Workflow/ModelSchema.js +118 -0
  165. package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
  166. package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
  167. package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
  168. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
  169. package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
  170. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
  171. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
  172. package/build/dist/UI/Components/Workflow/Utils.js +73 -1
  173. package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
  174. package/build/dist/UI/Components/Workflow/Workflow.js +51 -6
  175. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  176. package/build/dist/UI/Types/LayeredDismissal.js +21 -0
  177. package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
  178. package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
  179. package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
  180. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
  181. package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
  182. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +35 -6
  183. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  184. package/build/dist/Utils/Telemetry/EntityKey.js +52 -0
  185. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  186. 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 now, but the hint has to say so where the builder is
23
- * actually typing, because the Query argument is a bare JSON editor with
24
- * no field picker behind it.
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
+ };