@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,396 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* A row-based editor for the two workflow arguments that are keyed on a
|
|
3
|
+
* model's columns: the Query that picks which records a component acts on,
|
|
4
|
+
* and the record of values a create/update writes.
|
|
5
|
+
*
|
|
6
|
+
* Both were bare JSON editors, with the column names — and, for a query, the
|
|
7
|
+
* operator syntax — left entirely to memory. The description on those
|
|
8
|
+
* arguments had to carry a hint about "_id" versus "id" precisely because
|
|
9
|
+
* nothing else told the builder what the columns were called.
|
|
10
|
+
*
|
|
11
|
+
* Everything needed to do better already existed: /model-schema/:tableName
|
|
12
|
+
* knows the columns, and DictionaryForm's operator mode emits exactly the
|
|
13
|
+
* {_type, value} shape that JSONFunctions.deserialize hydrates on the server
|
|
14
|
+
* (FindManyBaseModel and friends). This wires the two together, and keeps a
|
|
15
|
+
* JSON escape hatch for the values rows cannot express.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import CodeEditor from "../CodeEditor/CodeEditor";
|
|
19
|
+
import ComponentLoader from "../ComponentLoader/ComponentLoader";
|
|
20
|
+
import DictionaryForm, { ValueType } from "../Dictionary/Dictionary";
|
|
21
|
+
import { DictionaryEntryValue } from "../Dictionary/DictionaryFilterOperator";
|
|
22
|
+
import Icon from "../Icon/Icon";
|
|
23
|
+
import {
|
|
24
|
+
ModelSchemaColumn,
|
|
25
|
+
ModelSchemaState,
|
|
26
|
+
findColumn,
|
|
27
|
+
useModelSchema,
|
|
28
|
+
} from "./ModelSchema";
|
|
29
|
+
import CodeType from "../../../Types/Code/CodeType";
|
|
30
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
31
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
32
|
+
import { JSONObject, ObjectType } from "../../../Types/JSON";
|
|
33
|
+
import React, {
|
|
34
|
+
FunctionComponent,
|
|
35
|
+
ReactElement,
|
|
36
|
+
useMemo,
|
|
37
|
+
useRef,
|
|
38
|
+
useState,
|
|
39
|
+
} from "react";
|
|
40
|
+
|
|
41
|
+
export enum ModelColumnEditorMode {
|
|
42
|
+
/** Conditions that select records: column, operator, value. */
|
|
43
|
+
Query = "Query",
|
|
44
|
+
/** Values to write: column, value. No operators — you cannot save a ">". */
|
|
45
|
+
Record = "Record",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ComponentProps {
|
|
49
|
+
tableName: string;
|
|
50
|
+
mode: ModelColumnEditorMode;
|
|
51
|
+
initialValue?: string | JSONObject | null | undefined;
|
|
52
|
+
onChange: (value: string) => void;
|
|
53
|
+
error?: string | undefined;
|
|
54
|
+
placeholder?: string | undefined;
|
|
55
|
+
tabIndex?: number | undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type ViewMode = "builder" | "json";
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* Operator wrappers the row editor can show. Anything else in a stored query
|
|
62
|
+
* (an InBetween, a relation sub-object, a raw QueryHelper construct) keeps the
|
|
63
|
+
* JSON editor rather than being silently flattened.
|
|
64
|
+
*/
|
|
65
|
+
const REPRESENTABLE_OPERATOR_TYPES: Array<string> = [
|
|
66
|
+
ObjectType.EqualTo,
|
|
67
|
+
ObjectType.NotEqual,
|
|
68
|
+
ObjectType.Search,
|
|
69
|
+
ObjectType.NotContains,
|
|
70
|
+
ObjectType.StartsWith,
|
|
71
|
+
ObjectType.EndsWith,
|
|
72
|
+
ObjectType.GreaterThan,
|
|
73
|
+
ObjectType.GreaterThanOrEqual,
|
|
74
|
+
ObjectType.LessThan,
|
|
75
|
+
ObjectType.LessThanOrEqual,
|
|
76
|
+
ObjectType.IsNull,
|
|
77
|
+
ObjectType.NotNull,
|
|
78
|
+
ObjectType.Includes,
|
|
79
|
+
ObjectType.IncludesNone,
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
interface ParsedQuery {
|
|
83
|
+
/** The raw text, always kept so the JSON editor can show it verbatim. */
|
|
84
|
+
text: string;
|
|
85
|
+
/** The parsed object, or null when the text is not a JSON object. */
|
|
86
|
+
parsed: JSONObject | null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type NormalizeInitialValueFunction = (
|
|
90
|
+
value: string | JSONObject | null | undefined,
|
|
91
|
+
) => ParsedQuery;
|
|
92
|
+
|
|
93
|
+
const normalizeInitialValue: NormalizeInitialValueFunction = (
|
|
94
|
+
value: string | JSONObject | null | undefined,
|
|
95
|
+
): ParsedQuery => {
|
|
96
|
+
if (value === null || value === undefined || value === "") {
|
|
97
|
+
return { text: "", parsed: {} };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
101
|
+
return { text: JSON.stringify(value, null, 2), parsed: value };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeof value !== "string") {
|
|
105
|
+
return { text: String(value), parsed: null };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const parsed: unknown = JSON.parse(value);
|
|
110
|
+
|
|
111
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
112
|
+
return { text: value, parsed: parsed as JSONObject };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { text: value, parsed: null };
|
|
116
|
+
} catch {
|
|
117
|
+
return { text: value, parsed: null };
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
interface CompatibilityResult {
|
|
122
|
+
compatible: boolean;
|
|
123
|
+
/** Shown to the builder so a locked JSON editor is never unexplained. */
|
|
124
|
+
reasons: Array<string>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
type ClassifyCompatibilityFunction = (
|
|
128
|
+
parsed: JSONObject | null,
|
|
129
|
+
columns: Array<ModelSchemaColumn>,
|
|
130
|
+
mode: ModelColumnEditorMode,
|
|
131
|
+
) => CompatibilityResult;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Can this value be shown as rows without losing anything?
|
|
135
|
+
*
|
|
136
|
+
* Deliberately conservative: when in doubt the JSON editor stays, because a
|
|
137
|
+
* value silently rewritten into something that means something else is far
|
|
138
|
+
* worse than one the builder has to keep editing as text.
|
|
139
|
+
*/
|
|
140
|
+
export const classifyColumnValueCompatibility: ClassifyCompatibilityFunction = (
|
|
141
|
+
parsed: JSONObject | null,
|
|
142
|
+
columns: Array<ModelSchemaColumn>,
|
|
143
|
+
mode: ModelColumnEditorMode,
|
|
144
|
+
): CompatibilityResult => {
|
|
145
|
+
if (parsed === null) {
|
|
146
|
+
return {
|
|
147
|
+
compatible: false,
|
|
148
|
+
reasons: ["The current value isn't a JSON object."],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const reasons: Array<string> = [];
|
|
153
|
+
|
|
154
|
+
for (const key of Object.keys(parsed)) {
|
|
155
|
+
/*
|
|
156
|
+
* Columns are only checked when the schema actually loaded. An unknown key
|
|
157
|
+
* is worth saying out loud — it is the "id" versus "_id" mistake — but it
|
|
158
|
+
* does not force the JSON editor, since the runner accepts column names
|
|
159
|
+
* this endpoint does not surface.
|
|
160
|
+
*/
|
|
161
|
+
if (columns.length > 0 && !findColumn(columns, key)) {
|
|
162
|
+
reasons.push(`"${key}" isn't a known column on this model.`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const value: unknown = parsed[key];
|
|
166
|
+
|
|
167
|
+
if (value === null) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
typeof value === "string" ||
|
|
173
|
+
typeof value === "number" ||
|
|
174
|
+
typeof value === "boolean"
|
|
175
|
+
) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (Array.isArray(value)) {
|
|
180
|
+
reasons.push(
|
|
181
|
+
`"${key}" holds a list. Use the "is any of" operator instead, or keep editing as JSON.`,
|
|
182
|
+
);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (typeof value === "object") {
|
|
187
|
+
const objectType: unknown = (value as JSONObject)["_type"];
|
|
188
|
+
|
|
189
|
+
if (
|
|
190
|
+
mode === ModelColumnEditorMode.Query &&
|
|
191
|
+
typeof objectType === "string" &&
|
|
192
|
+
REPRESENTABLE_OPERATOR_TYPES.includes(objectType)
|
|
193
|
+
) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (typeof objectType === "string") {
|
|
198
|
+
reasons.push(`"${key}" uses ${objectType}, which the rows can't show.`);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
reasons.push(
|
|
203
|
+
`"${key}" holds a nested record, which the rows can't show.`,
|
|
204
|
+
);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
reasons.push(`"${key}" has an unexpected value.`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/*
|
|
212
|
+
* An unknown column name is a warning, not a reason to lock the editor —
|
|
213
|
+
* the builder can still fix it in the row that shows it.
|
|
214
|
+
*/
|
|
215
|
+
const blocking: Array<string> = reasons.filter((reason: string) => {
|
|
216
|
+
return !reason.includes("isn't a known column");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return { compatible: blocking.length === 0, reasons: reasons };
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
type BuildQueryJsonFunction = (
|
|
223
|
+
value: Dictionary<DictionaryEntryValue>,
|
|
224
|
+
) => string;
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The rows, as the string stored on the workflow argument.
|
|
228
|
+
*
|
|
229
|
+
* Empty collapses to "" rather than "{}" so a required argument still reads as
|
|
230
|
+
* empty to form validation and to the graph linter.
|
|
231
|
+
*/
|
|
232
|
+
export const buildColumnValueJson: BuildQueryJsonFunction = (
|
|
233
|
+
value: Dictionary<DictionaryEntryValue>,
|
|
234
|
+
): string => {
|
|
235
|
+
const entries: JSONObject = {};
|
|
236
|
+
|
|
237
|
+
for (const key of Object.keys(value)) {
|
|
238
|
+
if (key.trim() === "") {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
entries[key] = value[key] as never;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (Object.keys(entries).length === 0) {
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/*
|
|
250
|
+
* The operator wrappers serialize themselves through toJSON() into
|
|
251
|
+
* {_type, value}, which is exactly what the server rehydrates with
|
|
252
|
+
* JSONFunctions.deserialize.
|
|
253
|
+
*/
|
|
254
|
+
return JSON.stringify(entries);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const ModelColumnEditor: FunctionComponent<ComponentProps> = (
|
|
258
|
+
props: ComponentProps,
|
|
259
|
+
): ReactElement => {
|
|
260
|
+
const schema: ModelSchemaState = useModelSchema(props.tableName);
|
|
261
|
+
const isQuery: boolean = props.mode === ModelColumnEditorMode.Query;
|
|
262
|
+
|
|
263
|
+
const initial: ParsedQuery = useMemo(() => {
|
|
264
|
+
return normalizeInitialValue(props.initialValue);
|
|
265
|
+
}, []);
|
|
266
|
+
|
|
267
|
+
const [jsonText, setJsonText] = useState<string>(initial.text);
|
|
268
|
+
const [viewMode, setViewMode] = useState<ViewMode>("builder");
|
|
269
|
+
|
|
270
|
+
/*
|
|
271
|
+
* The row editor is uncontrolled after mount (DictionaryForm seeds itself
|
|
272
|
+
* once), so the initial value is captured rather than recomputed.
|
|
273
|
+
*/
|
|
274
|
+
const initialRows: React.MutableRefObject<Dictionary<DictionaryEntryValue>> =
|
|
275
|
+
useRef<Dictionary<DictionaryEntryValue>>(
|
|
276
|
+
(initial.parsed || {}) as Dictionary<DictionaryEntryValue>,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const compatibility: CompatibilityResult = useMemo(() => {
|
|
280
|
+
return classifyColumnValueCompatibility(
|
|
281
|
+
initial.parsed,
|
|
282
|
+
schema.columns || [],
|
|
283
|
+
props.mode,
|
|
284
|
+
);
|
|
285
|
+
}, [schema.columns, props.mode]);
|
|
286
|
+
|
|
287
|
+
const columnKeys: Array<string> = useMemo(() => {
|
|
288
|
+
return (schema.columns || []).map((column: ModelSchemaColumn) => {
|
|
289
|
+
return column.id;
|
|
290
|
+
});
|
|
291
|
+
}, [schema.columns]);
|
|
292
|
+
|
|
293
|
+
if (schema.isLoading) {
|
|
294
|
+
return <ComponentLoader />;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const isLockedToJson: boolean = !compatibility.compatible;
|
|
298
|
+
const effectiveMode: ViewMode = isLockedToJson ? "json" : viewMode;
|
|
299
|
+
|
|
300
|
+
type OnJsonChangeFunction = (value: string) => void;
|
|
301
|
+
|
|
302
|
+
const onJsonChange: OnJsonChangeFunction = (value: string): void => {
|
|
303
|
+
setJsonText(value);
|
|
304
|
+
props.onChange(value);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
type OnRowsChangeFunction = (value: Dictionary<DictionaryEntryValue>) => void;
|
|
308
|
+
|
|
309
|
+
const onRowsChange: OnRowsChangeFunction = (
|
|
310
|
+
value: Dictionary<DictionaryEntryValue>,
|
|
311
|
+
): void => {
|
|
312
|
+
const asJson: string = buildColumnValueJson(value);
|
|
313
|
+
setJsonText(asJson);
|
|
314
|
+
props.onChange(asJson);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<div>
|
|
319
|
+
{schema.error && (
|
|
320
|
+
<p className="text-sm text-amber-600 mb-2">
|
|
321
|
+
Couldn't load this model's columns ({schema.error}). You can
|
|
322
|
+
still write the query as JSON.
|
|
323
|
+
</p>
|
|
324
|
+
)}
|
|
325
|
+
|
|
326
|
+
{isLockedToJson && compatibility.reasons.length > 0 && (
|
|
327
|
+
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50 p-3">
|
|
328
|
+
<p className="text-sm text-amber-800">Editing as JSON, because:</p>
|
|
329
|
+
<ul className="mt-1 list-disc pl-5 text-sm text-amber-700">
|
|
330
|
+
{compatibility.reasons.map((reason: string, i: number) => {
|
|
331
|
+
return <li key={i}>{reason}</li>;
|
|
332
|
+
})}
|
|
333
|
+
</ul>
|
|
334
|
+
</div>
|
|
335
|
+
)}
|
|
336
|
+
|
|
337
|
+
{!isLockedToJson && compatibility.reasons.length > 0 && (
|
|
338
|
+
<div className="mb-2 rounded-md border border-amber-200 bg-amber-50 p-3">
|
|
339
|
+
<ul className="list-disc pl-5 text-sm text-amber-700">
|
|
340
|
+
{compatibility.reasons.map((reason: string, i: number) => {
|
|
341
|
+
return <li key={i}>{reason}</li>;
|
|
342
|
+
})}
|
|
343
|
+
</ul>
|
|
344
|
+
</div>
|
|
345
|
+
)}
|
|
346
|
+
|
|
347
|
+
{effectiveMode === "builder" ? (
|
|
348
|
+
<DictionaryForm
|
|
349
|
+
keys={columnKeys}
|
|
350
|
+
enableOperators={isQuery}
|
|
351
|
+
valueTypes={[ValueType.Text, ValueType.Number, ValueType.Boolean]}
|
|
352
|
+
addButtonSuffix={isQuery ? "condition" : "field"}
|
|
353
|
+
keyPlaceholder="Column"
|
|
354
|
+
valuePlaceholder="Value"
|
|
355
|
+
initialValue={initialRows.current}
|
|
356
|
+
onChange={onRowsChange}
|
|
357
|
+
/>
|
|
358
|
+
) : (
|
|
359
|
+
<CodeEditor
|
|
360
|
+
type={CodeType.JSON}
|
|
361
|
+
tabIndex={props.tabIndex}
|
|
362
|
+
error={props.error}
|
|
363
|
+
initialValue={initial.text}
|
|
364
|
+
value={jsonText}
|
|
365
|
+
placeholder={props.placeholder}
|
|
366
|
+
onChange={onJsonChange}
|
|
367
|
+
/>
|
|
368
|
+
)}
|
|
369
|
+
|
|
370
|
+
{!isLockedToJson && (
|
|
371
|
+
<div className="mt-2">
|
|
372
|
+
<button
|
|
373
|
+
type="button"
|
|
374
|
+
className="text-sm underline text-blue-500 hover:text-blue-600 cursor-pointer inline-flex items-center gap-1"
|
|
375
|
+
onClick={() => {
|
|
376
|
+
setViewMode(effectiveMode === "builder" ? "json" : "builder");
|
|
377
|
+
}}
|
|
378
|
+
>
|
|
379
|
+
<Icon icon={IconProp.Code} className="h-3.5 w-3.5" />
|
|
380
|
+
{effectiveMode === "builder"
|
|
381
|
+
? "Edit as JSON"
|
|
382
|
+
: isQuery
|
|
383
|
+
? "Back to conditions"
|
|
384
|
+
: "Back to fields"}
|
|
385
|
+
</button>
|
|
386
|
+
</div>
|
|
387
|
+
)}
|
|
388
|
+
|
|
389
|
+
{props.error && effectiveMode === "builder" && (
|
|
390
|
+
<p className="mt-1 text-sm text-red-400">{props.error}</p>
|
|
391
|
+
)}
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export default ModelColumnEditor;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared access to the workflow /model-schema/:tableName endpoint.
|
|
3
|
+
*
|
|
4
|
+
* Three builder inputs are backed by a model's column list — the field picker
|
|
5
|
+
* for Select arguments, the query builder for Query arguments, and the record
|
|
6
|
+
* form for create/update arguments — and all three want the same fetch, the
|
|
7
|
+
* same loading and error handling, and the same column shape.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
|
|
11
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
12
|
+
import URL from "../../../Types/API/URL";
|
|
13
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
14
|
+
import { WORKFLOW_URL } from "../../Config";
|
|
15
|
+
import API from "../../Utils/API/API";
|
|
16
|
+
import { useEffect, useState } from "react";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A column as the endpoint describes it. `type` is deliberately a bare string
|
|
20
|
+
* rather than the server-side TableColumnType enum, so this file stays on the
|
|
21
|
+
* browser side of the wire format.
|
|
22
|
+
*/
|
|
23
|
+
export interface ModelSchemaColumn {
|
|
24
|
+
id: string;
|
|
25
|
+
title: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
type: string;
|
|
28
|
+
isRelation: boolean;
|
|
29
|
+
relatedTableName?: string | undefined;
|
|
30
|
+
relatedColumns?: Array<ModelSchemaColumn> | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ModelSchemaState {
|
|
34
|
+
columns: Array<ModelSchemaColumn> | null;
|
|
35
|
+
isLoading: boolean;
|
|
36
|
+
error: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type FetchModelSchemaFunction = (
|
|
40
|
+
tableName: string,
|
|
41
|
+
) => Promise<Array<ModelSchemaColumn>>;
|
|
42
|
+
|
|
43
|
+
export const fetchModelSchema: FetchModelSchemaFunction = async (
|
|
44
|
+
tableName: string,
|
|
45
|
+
): Promise<Array<ModelSchemaColumn>> => {
|
|
46
|
+
const url: URL = URL.fromString(WORKFLOW_URL.toString()).addRoute(
|
|
47
|
+
`/model-schema/${encodeURIComponent(tableName)}`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
51
|
+
await API.get<JSONObject>({ url });
|
|
52
|
+
|
|
53
|
+
if (result instanceof HTTPErrorResponse) {
|
|
54
|
+
throw result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const data: JSONObject = result.data as JSONObject;
|
|
58
|
+
|
|
59
|
+
return (data["columns"] as unknown as Array<ModelSchemaColumn>) || [];
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type UseModelSchemaFunction = (
|
|
63
|
+
tableName: string | undefined,
|
|
64
|
+
) => ModelSchemaState;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Load a model's columns, once per table name.
|
|
68
|
+
*
|
|
69
|
+
* Never throws: a failure leaves `columns` as an empty array and puts the
|
|
70
|
+
* message in `error`, so a caller can fall back to its JSON editor rather than
|
|
71
|
+
* showing the builder a blank panel.
|
|
72
|
+
*/
|
|
73
|
+
export const useModelSchema: UseModelSchemaFunction = (
|
|
74
|
+
tableName: string | undefined,
|
|
75
|
+
): ModelSchemaState => {
|
|
76
|
+
const [columns, setColumns] = useState<Array<ModelSchemaColumn> | null>(null);
|
|
77
|
+
const [isLoading, setIsLoading] = useState<boolean>(Boolean(tableName));
|
|
78
|
+
const [error, setError] = useState<string | null>(null);
|
|
79
|
+
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!tableName) {
|
|
82
|
+
setColumns([]);
|
|
83
|
+
setIsLoading(false);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let cancelled: boolean = false;
|
|
88
|
+
|
|
89
|
+
const load: () => Promise<void> = async (): Promise<void> => {
|
|
90
|
+
setIsLoading(true);
|
|
91
|
+
setError(null);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const loaded: Array<ModelSchemaColumn> =
|
|
95
|
+
await fetchModelSchema(tableName);
|
|
96
|
+
|
|
97
|
+
if (cancelled) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
setColumns(loaded);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (cancelled) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
setError(API.getFriendlyMessage(err));
|
|
108
|
+
setColumns([]);
|
|
109
|
+
} finally {
|
|
110
|
+
if (!cancelled) {
|
|
111
|
+
setIsLoading(false);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
void load();
|
|
117
|
+
|
|
118
|
+
return () => {
|
|
119
|
+
cancelled = true;
|
|
120
|
+
};
|
|
121
|
+
}, [tableName]);
|
|
122
|
+
|
|
123
|
+
return { columns: columns, isLoading: isLoading, error: error };
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/*
|
|
127
|
+
* Column types that hold a single scalar the builder can sensibly type into a
|
|
128
|
+
* box. Everything else (relations, JSON blobs, buffers) needs the JSON editor.
|
|
129
|
+
*/
|
|
130
|
+
const SCALAR_COLUMN_TYPES: Array<string> = [
|
|
131
|
+
"ShortText",
|
|
132
|
+
"LongText",
|
|
133
|
+
"VeryLongText",
|
|
134
|
+
"Slug",
|
|
135
|
+
"Email",
|
|
136
|
+
"Phone",
|
|
137
|
+
"Color",
|
|
138
|
+
"Domain",
|
|
139
|
+
"Name",
|
|
140
|
+
"Description",
|
|
141
|
+
"ObjectID",
|
|
142
|
+
"Number",
|
|
143
|
+
"PositiveNumber",
|
|
144
|
+
"Boolean",
|
|
145
|
+
"Date",
|
|
146
|
+
"Password",
|
|
147
|
+
"HashedString",
|
|
148
|
+
"Port",
|
|
149
|
+
"Hostname",
|
|
150
|
+
"URL",
|
|
151
|
+
"Route",
|
|
152
|
+
"IP",
|
|
153
|
+
"Version",
|
|
154
|
+
"Markdown",
|
|
155
|
+
"HTML",
|
|
156
|
+
"JavaScript",
|
|
157
|
+
"CSS",
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
export type IsScalarColumnFunction = (column: ModelSchemaColumn) => boolean;
|
|
161
|
+
|
|
162
|
+
export const isScalarColumn: IsScalarColumnFunction = (
|
|
163
|
+
column: ModelSchemaColumn,
|
|
164
|
+
): boolean => {
|
|
165
|
+
return !column.isRelation && SCALAR_COLUMN_TYPES.includes(column.type);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export type IsNumericColumnFunction = (column: ModelSchemaColumn) => boolean;
|
|
169
|
+
|
|
170
|
+
export const isNumericColumn: IsNumericColumnFunction = (
|
|
171
|
+
column: ModelSchemaColumn,
|
|
172
|
+
): boolean => {
|
|
173
|
+
return column.type === "Number" || column.type === "PositiveNumber";
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type IsBooleanColumnFunction = (column: ModelSchemaColumn) => boolean;
|
|
177
|
+
|
|
178
|
+
export const isBooleanColumn: IsBooleanColumnFunction = (
|
|
179
|
+
column: ModelSchemaColumn,
|
|
180
|
+
): boolean => {
|
|
181
|
+
return column.type === "Boolean";
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
export type FindColumnFunction = (
|
|
185
|
+
columns: Array<ModelSchemaColumn>,
|
|
186
|
+
columnId: string,
|
|
187
|
+
) => ModelSchemaColumn | undefined;
|
|
188
|
+
|
|
189
|
+
export const findColumn: FindColumnFunction = (
|
|
190
|
+
columns: Array<ModelSchemaColumn>,
|
|
191
|
+
columnId: string,
|
|
192
|
+
): ModelSchemaColumn | undefined => {
|
|
193
|
+
return columns.find((column: ModelSchemaColumn) => {
|
|
194
|
+
return column.id === columnId;
|
|
195
|
+
});
|
|
196
|
+
};
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import ErrorMessage from "../ErrorMessage/ErrorMessage";
|
|
2
2
|
import BasicForm, { FormProps } from "../Forms/BasicForm";
|
|
3
3
|
import FormValues from "../Forms/Types/FormValues";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
componentInputTypeToFormFieldType,
|
|
6
|
+
parseStringDictionaryValue,
|
|
7
|
+
} from "./Utils";
|
|
5
8
|
import { JSONObject } from "../../../Types/JSON";
|
|
6
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
Argument,
|
|
11
|
+
ComponentInputType,
|
|
12
|
+
NodeDataProp,
|
|
13
|
+
} from "../../../Types/Workflow/Component";
|
|
7
14
|
import React, {
|
|
8
15
|
FunctionComponent,
|
|
9
16
|
ReactElement,
|
|
@@ -34,6 +41,29 @@ const RunForm: FunctionComponent<ComponentProps> = (
|
|
|
34
41
|
props.onFormChange(component);
|
|
35
42
|
}, [component]);
|
|
36
43
|
|
|
44
|
+
/*
|
|
45
|
+
* Same hydration ArgumentsForm does: a StringDictionary renders as key/value
|
|
46
|
+
* rows, and those need a real object rather than the JSON string older
|
|
47
|
+
* workflows stored. The field type is chosen from the same value, so the two
|
|
48
|
+
* cannot disagree about which editor is on screen.
|
|
49
|
+
*/
|
|
50
|
+
const formInitialValues: JSONObject = { ...(component.arguments || {}) };
|
|
51
|
+
|
|
52
|
+
for (const argument of component.metadata.runWorkflowManuallyArguments ||
|
|
53
|
+
[]) {
|
|
54
|
+
if (argument.type !== ComponentInputType.StringDictionary) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const parsed: JSONObject | null = parseStringDictionaryValue(
|
|
59
|
+
formInitialValues[argument.id],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
if (parsed !== null) {
|
|
63
|
+
formInitialValues[argument.id] = parsed;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
37
67
|
return (
|
|
38
68
|
<div className="mb-3 mt-3">
|
|
39
69
|
<div className="mt-5 mb-5">
|
|
@@ -57,7 +87,7 @@ const RunForm: FunctionComponent<ComponentProps> = (
|
|
|
57
87
|
hideSubmitButton={true}
|
|
58
88
|
ref={formRef}
|
|
59
89
|
initialValues={{
|
|
60
|
-
...
|
|
90
|
+
...formInitialValues,
|
|
61
91
|
}}
|
|
62
92
|
onChange={(values: FormValues<JSONObject>) => {
|
|
63
93
|
setComponent({
|
|
@@ -86,12 +116,16 @@ const RunForm: FunctionComponent<ComponentProps> = (
|
|
|
86
116
|
},
|
|
87
117
|
required: argument.required,
|
|
88
118
|
placeholder: argument.placeholder,
|
|
119
|
+
/*
|
|
120
|
+
* Decided from the value the form actually renders.
|
|
121
|
+
* This used to read component.returnValues, which is not
|
|
122
|
+
* where RunForm writes — so for a field holding a value
|
|
123
|
+
* the row editor cannot represent, the editor on screen
|
|
124
|
+
* and the value behind it could disagree.
|
|
125
|
+
*/
|
|
89
126
|
...componentInputTypeToFormFieldType(
|
|
90
127
|
argument.type,
|
|
91
|
-
|
|
92
|
-
component.returnValues[argument.id]
|
|
93
|
-
? component.returnValues[argument.id]
|
|
94
|
-
: null,
|
|
128
|
+
formInitialValues[argument.id] ?? null,
|
|
95
129
|
),
|
|
96
130
|
};
|
|
97
131
|
},
|