@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,152 @@
1
+ import {
2
+ MAX_RUN_WATCH_POLLS,
3
+ RunWatchDecision,
4
+ decideRunWatch,
5
+ isFailedRunStatus,
6
+ isTerminalRunStatus,
7
+ } from "../../../../UI/Components/Workflow/RunStatusWatcher";
8
+ import WorkflowStatus from "../../../../Types/Workflow/WorkflowStatus";
9
+ import { describe, expect, test } from "@jest/globals";
10
+
11
+ describe("isTerminalRunStatus", () => {
12
+ test("settles on the statuses a run never leaves", () => {
13
+ expect(isTerminalRunStatus(WorkflowStatus.Success)).toBe(true);
14
+ expect(isTerminalRunStatus(WorkflowStatus.Error)).toBe(true);
15
+ expect(isTerminalRunStatus(WorkflowStatus.Timeout)).toBe(true);
16
+ expect(isTerminalRunStatus(WorkflowStatus.WorkflowCountExceeded)).toBe(
17
+ true,
18
+ );
19
+ });
20
+
21
+ test("keeps watching while a run is still moving", () => {
22
+ expect(isTerminalRunStatus(WorkflowStatus.Scheduled)).toBe(false);
23
+ expect(isTerminalRunStatus(WorkflowStatus.Running)).toBe(false);
24
+ });
25
+
26
+ /*
27
+ * Waiting is a Sleep step parking the run. It carries on afterwards, so it
28
+ * is not an ending — but it is also not worth watching, which decideRunWatch
29
+ * handles separately.
30
+ */
31
+ test("does not treat Waiting as an ending", () => {
32
+ expect(isTerminalRunStatus(WorkflowStatus.Waiting)).toBe(false);
33
+ });
34
+
35
+ test("handles nothing at all", () => {
36
+ expect(isTerminalRunStatus(null)).toBe(false);
37
+ expect(isTerminalRunStatus(undefined)).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("isFailedRunStatus", () => {
42
+ test("is true only for the unhappy endings", () => {
43
+ expect(isFailedRunStatus(WorkflowStatus.Error)).toBe(true);
44
+ expect(isFailedRunStatus(WorkflowStatus.Timeout)).toBe(true);
45
+ expect(isFailedRunStatus(WorkflowStatus.WorkflowCountExceeded)).toBe(true);
46
+
47
+ expect(isFailedRunStatus(WorkflowStatus.Success)).toBe(false);
48
+ expect(isFailedRunStatus(WorkflowStatus.Running)).toBe(false);
49
+ expect(isFailedRunStatus(WorkflowStatus.Waiting)).toBe(false);
50
+ expect(isFailedRunStatus(null)).toBe(false);
51
+ expect(isFailedRunStatus(undefined)).toBe(false);
52
+ });
53
+ });
54
+
55
+ describe("decideRunWatch", () => {
56
+ test("keeps waiting while the run has not appeared yet", () => {
57
+ const decision: RunWatchDecision = decideRunWatch({
58
+ run: null,
59
+ pollCount: 0,
60
+ });
61
+
62
+ expect(decision.shouldContinue).toBe(true);
63
+ expect(decision.message).toMatch(/Starting/i);
64
+ });
65
+
66
+ test("keeps watching a running run and says so", () => {
67
+ const decision: RunWatchDecision = decideRunWatch({
68
+ run: { runId: "a", status: WorkflowStatus.Running },
69
+ pollCount: 1,
70
+ });
71
+
72
+ expect(decision.shouldContinue).toBe(true);
73
+ expect(decision.message).toMatch(/running/i);
74
+ });
75
+
76
+ test("keeps watching a scheduled run", () => {
77
+ expect(
78
+ decideRunWatch({
79
+ run: { runId: "a", status: WorkflowStatus.Scheduled },
80
+ pollCount: 1,
81
+ }).shouldContinue,
82
+ ).toBe(true);
83
+ });
84
+
85
+ test("stops on success with a plain statement", () => {
86
+ const decision: RunWatchDecision = decideRunWatch({
87
+ run: { runId: "a", status: WorkflowStatus.Success },
88
+ pollCount: 3,
89
+ });
90
+
91
+ expect(decision.shouldContinue).toBe(false);
92
+ expect(decision.message).toMatch(/finished successfully/i);
93
+ });
94
+
95
+ test("stops on failure and points at the logs", () => {
96
+ const decision: RunWatchDecision = decideRunWatch({
97
+ run: { runId: "a", status: WorkflowStatus.Error },
98
+ pollCount: 3,
99
+ });
100
+
101
+ expect(decision.shouldContinue).toBe(false);
102
+ expect(decision.message).toMatch(/Logs tab/);
103
+ });
104
+
105
+ test("stops on timeout", () => {
106
+ expect(
107
+ decideRunWatch({
108
+ run: { runId: "a", status: WorkflowStatus.Timeout },
109
+ pollCount: 3,
110
+ }).shouldContinue,
111
+ ).toBe(false);
112
+ });
113
+
114
+ test("stops following a run that went to sleep, and explains why", () => {
115
+ const decision: RunWatchDecision = decideRunWatch({
116
+ run: { runId: "a", status: WorkflowStatus.Waiting },
117
+ pollCount: 3,
118
+ });
119
+
120
+ expect(decision.shouldContinue).toBe(false);
121
+ expect(decision.message).toMatch(/sleeping/i);
122
+ expect(decision.message).toMatch(/carry on/i);
123
+ });
124
+
125
+ test("gives up rather than polling forever", () => {
126
+ const decision: RunWatchDecision = decideRunWatch({
127
+ run: { runId: "a", status: WorkflowStatus.Running },
128
+ pollCount: MAX_RUN_WATCH_POLLS,
129
+ });
130
+
131
+ expect(decision.shouldContinue).toBe(false);
132
+ expect(decision.message).toMatch(/taking a while/i);
133
+ });
134
+
135
+ test("gives up even when the run never appeared", () => {
136
+ const decision: RunWatchDecision = decideRunWatch({
137
+ run: null,
138
+ pollCount: MAX_RUN_WATCH_POLLS,
139
+ });
140
+
141
+ expect(decision.shouldContinue).toBe(false);
142
+ });
143
+
144
+ test("polls right up to the limit but not past it", () => {
145
+ expect(
146
+ decideRunWatch({
147
+ run: { runId: "a", status: WorkflowStatus.Running },
148
+ pollCount: MAX_RUN_WATCH_POLLS - 1,
149
+ }).shouldContinue,
150
+ ).toBe(true);
151
+ });
152
+ });
@@ -0,0 +1,192 @@
1
+ /*
2
+ * Utils.ts imports the whole database-model registry for
3
+ * loadComponentsAndCategories, which nothing here exercises. Stubbing it keeps
4
+ * this suite from type-checking several hundred model files to test two pure
5
+ * functions.
6
+ */
7
+ jest.mock("../../../../Models/DatabaseModels/Index", () => {
8
+ return {
9
+ __esModule: true,
10
+ default: [],
11
+ };
12
+ });
13
+
14
+ import {
15
+ componentInputTypeToFormFieldType,
16
+ parseStringDictionaryValue,
17
+ } from "../../../../UI/Components/Workflow/Utils";
18
+ import FormFieldSchemaType from "../../../../UI/Components/Forms/Types/FormFieldSchemaType";
19
+ import { JSONObject } from "../../../../Types/JSON";
20
+ import { ComponentInputType } from "../../../../Types/Workflow/Component";
21
+ import { describe, expect, test } from "@jest/globals";
22
+
23
+ describe("parseStringDictionaryValue", () => {
24
+ test("treats an unset value as an empty dictionary", () => {
25
+ expect(parseStringDictionaryValue(undefined)).toEqual({});
26
+ expect(parseStringDictionaryValue(null)).toEqual({});
27
+ expect(parseStringDictionaryValue("")).toEqual({});
28
+ expect(parseStringDictionaryValue(" ")).toEqual({});
29
+ });
30
+
31
+ test("parses the JSON string shape every existing workflow stores", () => {
32
+ expect(
33
+ parseStringDictionaryValue('{"Authorization": "Bearer abc"}'),
34
+ ).toEqual({
35
+ Authorization: "Bearer abc",
36
+ });
37
+ });
38
+
39
+ test("accepts JSON5, which is what the components themselves accept", () => {
40
+ expect(parseStringDictionaryValue("{header1: 'value1'}")).toEqual({
41
+ header1: "value1",
42
+ });
43
+ expect(parseStringDictionaryValue('{"a": "b",}')).toEqual({ a: "b" });
44
+ });
45
+
46
+ test("passes an already-parsed object straight through", () => {
47
+ const value: JSONObject = { "X-Key": "v" };
48
+
49
+ expect(parseStringDictionaryValue(value)).toEqual({ "X-Key": "v" });
50
+ });
51
+
52
+ test("keeps numbers and booleans, which the row editor can represent", () => {
53
+ expect(parseStringDictionaryValue('{"n": 1, "b": true}')).toEqual({
54
+ n: 1,
55
+ b: true,
56
+ });
57
+ });
58
+
59
+ test("declines a whole-field template, which is not JSON as written", () => {
60
+ expect(
61
+ parseStringDictionaryValue(
62
+ "{{local.components.api-get-1.returnValues.response-headers}}",
63
+ ),
64
+ ).toBeNull();
65
+ });
66
+
67
+ test("declines text that does not parse, so it stays visible and fixable", () => {
68
+ expect(parseStringDictionaryValue('{"a": ')).toBeNull();
69
+ expect(parseStringDictionaryValue("not json")).toBeNull();
70
+ });
71
+
72
+ test("declines nested values the row editor would silently discard", () => {
73
+ expect(parseStringDictionaryValue('{"a": {"b": 1}}')).toBeNull();
74
+ expect(parseStringDictionaryValue('{"a": ["b"]}')).toBeNull();
75
+ expect(parseStringDictionaryValue('{"a": null}')).toBeNull();
76
+ });
77
+
78
+ test("declines arrays and scalars, which are not dictionaries", () => {
79
+ expect(parseStringDictionaryValue("[1, 2]")).toBeNull();
80
+ expect(parseStringDictionaryValue("42")).toBeNull();
81
+ });
82
+
83
+ test("keeps a value containing a template inside a string", () => {
84
+ expect(
85
+ parseStringDictionaryValue(
86
+ '{"Authorization": "Bearer {{local.variables.token}}"}',
87
+ ),
88
+ ).toEqual({
89
+ Authorization: "Bearer {{local.variables.token}}",
90
+ });
91
+ });
92
+ });
93
+
94
+ describe("componentInputTypeToFormFieldType — StringDictionary", () => {
95
+ test("uses the key/value editor for a value it can represent", () => {
96
+ expect(
97
+ componentInputTypeToFormFieldType(
98
+ ComponentInputType.StringDictionary,
99
+ '{"a": "b"}',
100
+ ).fieldType,
101
+ ).toBe(FormFieldSchemaType.Dictionary);
102
+ });
103
+
104
+ test("uses the key/value editor for an unset value", () => {
105
+ expect(
106
+ componentInputTypeToFormFieldType(
107
+ ComponentInputType.StringDictionary,
108
+ null,
109
+ ).fieldType,
110
+ ).toBe(FormFieldSchemaType.Dictionary);
111
+ });
112
+
113
+ test("falls back to the JSON editor for a whole-field template", () => {
114
+ expect(
115
+ componentInputTypeToFormFieldType(
116
+ ComponentInputType.StringDictionary,
117
+ "{{local.components.a.returnValues.response-headers}}",
118
+ ).fieldType,
119
+ ).toBe(FormFieldSchemaType.JSON);
120
+ });
121
+
122
+ test("falls back to the JSON editor for a value that does not parse", () => {
123
+ expect(
124
+ componentInputTypeToFormFieldType(
125
+ ComponentInputType.StringDictionary,
126
+ '{"a": ',
127
+ ).fieldType,
128
+ ).toBe(FormFieldSchemaType.JSON);
129
+ });
130
+
131
+ test("agrees with parseStringDictionaryValue for every case", () => {
132
+ const samples: Array<unknown> = [
133
+ undefined,
134
+ null,
135
+ "",
136
+ '{"a":"b"}',
137
+ "{a:'b'}",
138
+ "{{local.variables.x}}",
139
+ '{"a":{"b":1}}',
140
+ "[1,2]",
141
+ "garbage",
142
+ { a: "b" },
143
+ ];
144
+
145
+ for (const sample of samples) {
146
+ const expected: FormFieldSchemaType =
147
+ parseStringDictionaryValue(sample) === null
148
+ ? FormFieldSchemaType.JSON
149
+ : FormFieldSchemaType.Dictionary;
150
+
151
+ expect(
152
+ componentInputTypeToFormFieldType(
153
+ ComponentInputType.StringDictionary,
154
+ sample,
155
+ ).fieldType,
156
+ ).toBe(expected);
157
+ }
158
+ });
159
+ });
160
+
161
+ describe("componentInputTypeToFormFieldType — unchanged mappings", () => {
162
+ test("JSON-document types still use the code editor", () => {
163
+ const jsonTypes: Array<ComponentInputType> = [
164
+ ComponentInputType.JSON,
165
+ ComponentInputType.JSONArray,
166
+ ComponentInputType.Query,
167
+ ComponentInputType.Select,
168
+ ComponentInputType.BaseModel,
169
+ ComponentInputType.BaseModelArray,
170
+ ];
171
+
172
+ for (const type of jsonTypes) {
173
+ expect(componentInputTypeToFormFieldType(type, null).fieldType).toBe(
174
+ FormFieldSchemaType.JSON,
175
+ );
176
+ }
177
+ });
178
+
179
+ test("simple types are untouched", () => {
180
+ expect(
181
+ componentInputTypeToFormFieldType(ComponentInputType.Email, null)
182
+ .fieldType,
183
+ ).toBe(FormFieldSchemaType.Email);
184
+ expect(
185
+ componentInputTypeToFormFieldType(ComponentInputType.URL, null).fieldType,
186
+ ).toBe(FormFieldSchemaType.URL);
187
+ expect(
188
+ componentInputTypeToFormFieldType(ComponentInputType.Boolean, null)
189
+ .fieldType,
190
+ ).toBe(FormFieldSchemaType.Toggle);
191
+ });
192
+ });