@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,403 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Every shipped template is checked against the real component registry and
|
|
3
|
+
* against the same linter the builder runs.
|
|
4
|
+
*
|
|
5
|
+
* A template is the first workflow a lot of people will ever see, so one with
|
|
6
|
+
* a mistyped argument id, a reference to a return value that does not exist,
|
|
7
|
+
* or an edge leaving a port the component does not have would be worse than
|
|
8
|
+
* shipping no templates at all — it teaches the wrong thing and looks broken
|
|
9
|
+
* on arrival. Writing these tests caught exactly that: the Log component's
|
|
10
|
+
* argument is "value" (not "log-message"), the Schedule trigger's is
|
|
11
|
+
* "schedule" (not "schedule-at"), the Manual trigger leaves by "success" and
|
|
12
|
+
* the Schedule trigger by "execute" (neither is "out").
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
WorkflowTemplate,
|
|
17
|
+
buildGraphForTemplate,
|
|
18
|
+
getTemplateGraphSpec,
|
|
19
|
+
getWorkflowTemplates,
|
|
20
|
+
} from "../../../Types/Workflow/Templates";
|
|
21
|
+
import Components from "../../../Types/Workflow/Components";
|
|
22
|
+
import ComponentMetadata, {
|
|
23
|
+
Argument,
|
|
24
|
+
ComponentType,
|
|
25
|
+
NodeDataProp,
|
|
26
|
+
Port,
|
|
27
|
+
ReturnValue,
|
|
28
|
+
} from "../../../Types/Workflow/Component";
|
|
29
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
30
|
+
import {
|
|
31
|
+
LintGraphEdge,
|
|
32
|
+
LintGraphNode,
|
|
33
|
+
WorkflowLintIssue,
|
|
34
|
+
WorkflowLintResult,
|
|
35
|
+
WorkflowLintSeverity,
|
|
36
|
+
lintWorkflowGraph,
|
|
37
|
+
} from "../../../UI/Components/Workflow/GraphLint";
|
|
38
|
+
import { describe, expect, test } from "@jest/globals";
|
|
39
|
+
|
|
40
|
+
const templates: Array<WorkflowTemplate> = getWorkflowTemplates();
|
|
41
|
+
|
|
42
|
+
type FindMetadataFunction = (
|
|
43
|
+
metadataId: string,
|
|
44
|
+
) => ComponentMetadata | undefined;
|
|
45
|
+
|
|
46
|
+
const findMetadata: FindMetadataFunction = (
|
|
47
|
+
metadataId: string,
|
|
48
|
+
): ComponentMetadata | undefined => {
|
|
49
|
+
return Components.find((component: ComponentMetadata) => {
|
|
50
|
+
return component.id === metadataId;
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let idCounter: number = 0;
|
|
55
|
+
|
|
56
|
+
type GenerateIdFunction = () => string;
|
|
57
|
+
|
|
58
|
+
const generateId: GenerateIdFunction = (): string => {
|
|
59
|
+
idCounter++;
|
|
60
|
+
return `generated-${idCounter}`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build a template's graph and hydrate each node with its real metadata, the
|
|
65
|
+
* way the builder does on load. Without the metadata the linter cannot check
|
|
66
|
+
* arguments or return values.
|
|
67
|
+
*/
|
|
68
|
+
type HydrateFunction = (templateId: string) => {
|
|
69
|
+
nodes: Array<LintGraphNode>;
|
|
70
|
+
edges: Array<LintGraphEdge>;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const hydrate: HydrateFunction = (
|
|
74
|
+
templateId: string,
|
|
75
|
+
): { nodes: Array<LintGraphNode>; edges: Array<LintGraphEdge> } => {
|
|
76
|
+
const graph: JSONObject = buildGraphForTemplate(
|
|
77
|
+
templateId,
|
|
78
|
+
generateId,
|
|
79
|
+
) as JSONObject;
|
|
80
|
+
|
|
81
|
+
const rawNodes: Array<JSONObject> = graph["nodes"] as Array<JSONObject>;
|
|
82
|
+
const rawEdges: Array<JSONObject> = graph["edges"] as Array<JSONObject>;
|
|
83
|
+
|
|
84
|
+
const nodes: Array<LintGraphNode> = rawNodes.map(
|
|
85
|
+
(node: JSONObject): LintGraphNode => {
|
|
86
|
+
const data: JSONObject = node["data"] as JSONObject;
|
|
87
|
+
const metadata: ComponentMetadata | undefined = findMetadata(
|
|
88
|
+
data["metadataId"] as string,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
id: node["id"] as string,
|
|
93
|
+
data: {
|
|
94
|
+
...(data as unknown as NodeDataProp),
|
|
95
|
+
metadata: metadata as ComponentMetadata,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const edges: Array<LintGraphEdge> = rawEdges.map(
|
|
102
|
+
(edge: JSONObject): LintGraphEdge => {
|
|
103
|
+
return {
|
|
104
|
+
source: edge["source"] as string,
|
|
105
|
+
target: edge["target"] as string,
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
return { nodes: nodes, edges: edges };
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
describe("workflow templates", () => {
|
|
114
|
+
test("there is at least one", () => {
|
|
115
|
+
expect(templates.length).toBeGreaterThan(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("each has a unique id", () => {
|
|
119
|
+
const ids: Array<string> = templates.map((template: WorkflowTemplate) => {
|
|
120
|
+
return template.id;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("each is described well enough to choose between them", () => {
|
|
127
|
+
for (const template of templates) {
|
|
128
|
+
expect(template.name.length).toBeGreaterThan(0);
|
|
129
|
+
expect(template.description.length).toBeGreaterThan(0);
|
|
130
|
+
expect(template.teaches.length).toBeGreaterThan(0);
|
|
131
|
+
expect(template.workflowName.length).toBeGreaterThan(0);
|
|
132
|
+
expect(template.workflowDescription.length).toBeGreaterThan(0);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("an unknown template id yields nothing rather than throwing", () => {
|
|
137
|
+
expect(getTemplateGraphSpec("does-not-exist")).toBeNull();
|
|
138
|
+
expect(buildGraphForTemplate("does-not-exist", generateId)).toBeNull();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe.each(
|
|
143
|
+
templates.map((template: WorkflowTemplate) => {
|
|
144
|
+
return [template.id, template] as [string, WorkflowTemplate];
|
|
145
|
+
}),
|
|
146
|
+
)("template %s", (templateId: string) => {
|
|
147
|
+
test("every component it uses exists in the registry", () => {
|
|
148
|
+
const spec: { nodes: Array<{ metadataId: string }> } = getTemplateGraphSpec(
|
|
149
|
+
templateId,
|
|
150
|
+
) as { nodes: Array<{ metadataId: string }> };
|
|
151
|
+
|
|
152
|
+
for (const node of spec.nodes) {
|
|
153
|
+
expect(findMetadata(node.metadataId)).toBeDefined();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("every argument it sets is a real argument on that component", () => {
|
|
158
|
+
const spec: {
|
|
159
|
+
nodes: Array<{ metadataId: string; args?: JSONObject | undefined }>;
|
|
160
|
+
} = getTemplateGraphSpec(templateId) as {
|
|
161
|
+
nodes: Array<{ metadataId: string; args?: JSONObject | undefined }>;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
for (const node of spec.nodes) {
|
|
165
|
+
const metadata: ComponentMetadata = findMetadata(
|
|
166
|
+
node.metadataId,
|
|
167
|
+
) as ComponentMetadata;
|
|
168
|
+
|
|
169
|
+
for (const argumentId of Object.keys(node.args || {})) {
|
|
170
|
+
const argument: Argument | undefined = metadata.arguments.find(
|
|
171
|
+
(candidate: Argument) => {
|
|
172
|
+
return candidate.id === argumentId;
|
|
173
|
+
},
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(argument).toBeDefined();
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
/*
|
|
182
|
+
* The runner looks up outPorts[sourceHandle] to decide what runs next. A
|
|
183
|
+
* port id that does not exist means the workflow silently stops after the
|
|
184
|
+
* first step.
|
|
185
|
+
*/
|
|
186
|
+
test("every edge leaves a port the component actually has", () => {
|
|
187
|
+
const spec: {
|
|
188
|
+
nodes: Array<{ componentId: string; metadataId: string }>;
|
|
189
|
+
edges: Array<{ fromComponentId: string; fromPort: string }>;
|
|
190
|
+
} = getTemplateGraphSpec(templateId) as {
|
|
191
|
+
nodes: Array<{ componentId: string; metadataId: string }>;
|
|
192
|
+
edges: Array<{ fromComponentId: string; fromPort: string }>;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
for (const edge of spec.edges) {
|
|
196
|
+
const node: { metadataId: string } = spec.nodes.find(
|
|
197
|
+
(candidate: { componentId: string }) => {
|
|
198
|
+
return candidate.componentId === edge.fromComponentId;
|
|
199
|
+
},
|
|
200
|
+
) as { metadataId: string };
|
|
201
|
+
|
|
202
|
+
const metadata: ComponentMetadata = findMetadata(
|
|
203
|
+
node.metadataId,
|
|
204
|
+
) as ComponentMetadata;
|
|
205
|
+
|
|
206
|
+
const port: Port | undefined = metadata.outPorts.find(
|
|
207
|
+
(candidate: Port) => {
|
|
208
|
+
return candidate.id === edge.fromPort;
|
|
209
|
+
},
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
expect(port).toBeDefined();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("every required argument is filled in", () => {
|
|
217
|
+
const spec: {
|
|
218
|
+
nodes: Array<{ metadataId: string; args?: JSONObject | undefined }>;
|
|
219
|
+
} = getTemplateGraphSpec(templateId) as {
|
|
220
|
+
nodes: Array<{ metadataId: string; args?: JSONObject | undefined }>;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
for (const node of spec.nodes) {
|
|
224
|
+
const metadata: ComponentMetadata = findMetadata(
|
|
225
|
+
node.metadataId,
|
|
226
|
+
) as ComponentMetadata;
|
|
227
|
+
|
|
228
|
+
for (const argument of metadata.arguments) {
|
|
229
|
+
if (!argument.required) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
expect(Object.keys(node.args || {})).toContain(argument.id);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
test("every {{...}} reference points at a return value that exists", () => {
|
|
239
|
+
const spec: {
|
|
240
|
+
nodes: Array<{ componentId: string; metadataId: string }>;
|
|
241
|
+
} = getTemplateGraphSpec(templateId) as {
|
|
242
|
+
nodes: Array<{ componentId: string; metadataId: string }>;
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const graph: { nodes: Array<LintGraphNode>; edges: Array<LintGraphEdge> } =
|
|
246
|
+
hydrate(templateId);
|
|
247
|
+
|
|
248
|
+
const references: Array<string> = [];
|
|
249
|
+
|
|
250
|
+
for (const node of graph.nodes) {
|
|
251
|
+
for (const value of Object.values(node.data.arguments || {})) {
|
|
252
|
+
if (typeof value !== "string") {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const matches: Array<string> = value.match(/{{(.*?)}}/g) || [];
|
|
257
|
+
references.push(...matches);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const reference of references) {
|
|
262
|
+
const path: string = reference.slice(2, -2);
|
|
263
|
+
const parts: Array<string> = path.split(".");
|
|
264
|
+
|
|
265
|
+
expect(parts[0]).toBe("local");
|
|
266
|
+
expect(parts[1]).toBe("components");
|
|
267
|
+
|
|
268
|
+
const referencedNode: { metadataId: string } = spec.nodes.find(
|
|
269
|
+
(candidate: { componentId: string }) => {
|
|
270
|
+
return candidate.componentId === parts[2];
|
|
271
|
+
},
|
|
272
|
+
) as { metadataId: string };
|
|
273
|
+
|
|
274
|
+
expect(referencedNode).toBeDefined();
|
|
275
|
+
expect(parts[3]).toBe("returnValues");
|
|
276
|
+
|
|
277
|
+
const metadata: ComponentMetadata = findMetadata(
|
|
278
|
+
referencedNode.metadataId,
|
|
279
|
+
) as ComponentMetadata;
|
|
280
|
+
|
|
281
|
+
const returnValue: ReturnValue | undefined = metadata.returnValues.find(
|
|
282
|
+
(candidate: ReturnValue) => {
|
|
283
|
+
return candidate.id === parts[4];
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(returnValue).toBeDefined();
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
/*
|
|
292
|
+
* The end-to-end check: run the shipped template through the same linter the
|
|
293
|
+
* builder applies to a graph someone is editing. Zero errors is the bar.
|
|
294
|
+
*/
|
|
295
|
+
test("passes the builder's own graph checks with no errors", () => {
|
|
296
|
+
const result: WorkflowLintResult = lintWorkflowGraph(hydrate(templateId));
|
|
297
|
+
|
|
298
|
+
const errors: Array<WorkflowLintIssue> = result.issues.filter(
|
|
299
|
+
(issue: WorkflowLintIssue) => {
|
|
300
|
+
return issue.severity === WorkflowLintSeverity.Error;
|
|
301
|
+
},
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
expect(
|
|
305
|
+
errors.map((issue: WorkflowLintIssue) => {
|
|
306
|
+
return issue.message;
|
|
307
|
+
}),
|
|
308
|
+
).toEqual([]);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("has exactly one trigger, and it is a real trigger component", () => {
|
|
312
|
+
const spec: {
|
|
313
|
+
nodes: Array<{ metadataId: string; componentType: ComponentType }>;
|
|
314
|
+
} = getTemplateGraphSpec(templateId) as {
|
|
315
|
+
nodes: Array<{ metadataId: string; componentType: ComponentType }>;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const triggers: Array<{ metadataId: string }> = spec.nodes.filter(
|
|
319
|
+
(node: { componentType: ComponentType }) => {
|
|
320
|
+
return node.componentType === ComponentType.Trigger;
|
|
321
|
+
},
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
expect(triggers).toHaveLength(1);
|
|
325
|
+
|
|
326
|
+
const metadata: ComponentMetadata = findMetadata(
|
|
327
|
+
triggers[0]?.metadataId as string,
|
|
328
|
+
) as ComponentMetadata;
|
|
329
|
+
|
|
330
|
+
expect(metadata.componentType).toBe(ComponentType.Trigger);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
/*
|
|
334
|
+
* Two workflows created from one template must not share react-flow ids, or
|
|
335
|
+
* editing one would be liable to disturb the other on import/export.
|
|
336
|
+
*/
|
|
337
|
+
test("builds fresh node ids each time", () => {
|
|
338
|
+
const first: JSONObject = buildGraphForTemplate(
|
|
339
|
+
templateId,
|
|
340
|
+
generateId,
|
|
341
|
+
) as JSONObject;
|
|
342
|
+
const second: JSONObject = buildGraphForTemplate(
|
|
343
|
+
templateId,
|
|
344
|
+
generateId,
|
|
345
|
+
) as JSONObject;
|
|
346
|
+
|
|
347
|
+
const firstIds: Array<string> = (first["nodes"] as Array<JSONObject>).map(
|
|
348
|
+
(node: JSONObject) => {
|
|
349
|
+
return node["id"] as string;
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
const secondIds: Array<string> = (second["nodes"] as Array<JSONObject>).map(
|
|
353
|
+
(node: JSONObject) => {
|
|
354
|
+
return node["id"] as string;
|
|
355
|
+
},
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
for (const id of firstIds) {
|
|
359
|
+
expect(secondIds).not.toContain(id);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
test("keeps stable component ids, because references point at them", () => {
|
|
364
|
+
const first: JSONObject = buildGraphForTemplate(
|
|
365
|
+
templateId,
|
|
366
|
+
generateId,
|
|
367
|
+
) as JSONObject;
|
|
368
|
+
const second: JSONObject = buildGraphForTemplate(
|
|
369
|
+
templateId,
|
|
370
|
+
generateId,
|
|
371
|
+
) as JSONObject;
|
|
372
|
+
|
|
373
|
+
type ComponentIdsFunction = (graph: JSONObject) => Array<string>;
|
|
374
|
+
|
|
375
|
+
const componentIds: ComponentIdsFunction = (
|
|
376
|
+
graph: JSONObject,
|
|
377
|
+
): Array<string> => {
|
|
378
|
+
return (graph["nodes"] as Array<JSONObject>).map((node: JSONObject) => {
|
|
379
|
+
return (node["data"] as JSONObject)["id"] as string;
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
expect(componentIds(first)).toEqual(componentIds(second));
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("edges connect nodes that are in the graph", () => {
|
|
387
|
+
const graph: JSONObject = buildGraphForTemplate(
|
|
388
|
+
templateId,
|
|
389
|
+
generateId,
|
|
390
|
+
) as JSONObject;
|
|
391
|
+
|
|
392
|
+
const nodeIds: Array<string> = (graph["nodes"] as Array<JSONObject>).map(
|
|
393
|
+
(node: JSONObject) => {
|
|
394
|
+
return node["id"] as string;
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
for (const edge of graph["edges"] as Array<JSONObject>) {
|
|
399
|
+
expect(nodeIds).toContain(edge["source"] as string);
|
|
400
|
+
expect(nodeIds).toContain(edge["target"] as string);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|