mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-22 16:02:24 +00:00
feat(frontend): Catch flow errors in the UI (#4429)
* feat(frontend): Catch flow errors in the UI * feat(frontend): typo
This commit is contained in:
@@ -121,7 +121,7 @@
|
||||
</Alert>
|
||||
{/if}
|
||||
{/if}
|
||||
{#each steps as [args, filter, m] (m.id)}
|
||||
{#each steps as [args, filter, m], index (m.id + index)}
|
||||
{#if filter.length > 0}
|
||||
<div class="relative h-full border-t p-4">
|
||||
<h2 class="sticky w-full top-0 z-10 inline-flex items-center py-2">
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
import { encodeState } from '$lib/utils'
|
||||
import BranchOneStart from './renderers/nodes/BranchOneStart.svelte'
|
||||
import NoBranchNode from './renderers/nodes/NoBranchNode.svelte'
|
||||
import { Alert, Drawer } from '../common'
|
||||
import Button from '../common/button/Button.svelte'
|
||||
import FlowYamlEditor from '../flows/header/FlowYamlEditor.svelte'
|
||||
|
||||
export let success: boolean | undefined = undefined
|
||||
export let modules: FlowModule[] | undefined = []
|
||||
@@ -178,6 +181,10 @@
|
||||
let height = 0
|
||||
|
||||
function updateStores() {
|
||||
if (graph.error) {
|
||||
return
|
||||
}
|
||||
|
||||
$nodes = layoutNodes(graph?.nodes)
|
||||
$edges = graph.edges
|
||||
|
||||
@@ -236,69 +243,87 @@
|
||||
onMount(() => {
|
||||
centerViewport(width)
|
||||
})
|
||||
let yamlEditorDrawer: Drawer | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div style={`height: ${height}px; max-height: ${maxHeight}px;`} bind:clientWidth={width}>
|
||||
<SvelteFlow
|
||||
{nodes}
|
||||
{edges}
|
||||
{edgeTypes}
|
||||
{nodeTypes}
|
||||
{viewport}
|
||||
{height}
|
||||
minZoom={0.5}
|
||||
connectionLineType={ConnectionLineType.SmoothStep}
|
||||
defaultEdgeOptions={{ type: 'smoothstep' }}
|
||||
preventScrolling={scroll}
|
||||
zoomOnDoubleClick={false}
|
||||
elementsSelectable={false}
|
||||
{proOptions}
|
||||
nodesDraggable={false}
|
||||
>
|
||||
<div class="absolute inset-0 !bg-surface-secondary" />
|
||||
<Controls position="top-right" orientation="horizontal" showLock={false}>
|
||||
{#if download}
|
||||
<ControlButton
|
||||
on:click={() => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
'svelvet',
|
||||
encodeState({ modules, failureModule, preprocessorModule })
|
||||
)
|
||||
} catch (e) {
|
||||
console.error('error interacting with local storage', e)
|
||||
}
|
||||
window.open('/view_graph', '_blank')
|
||||
}}
|
||||
class="!bg-surface"
|
||||
>
|
||||
<Expand size="14" />
|
||||
</ControlButton>
|
||||
{/if}
|
||||
</Controls>
|
||||
<FlowYamlEditor bind:drawer={yamlEditorDrawer} />
|
||||
|
||||
<Controls
|
||||
position="top-left"
|
||||
orientation="horizontal"
|
||||
showLock={false}
|
||||
showZoom={false}
|
||||
showFitView={false}
|
||||
class="!shadow-none"
|
||||
>
|
||||
{#if showDataflow}
|
||||
<Toggle
|
||||
value={$useDataflow}
|
||||
on:change={() => {
|
||||
$useDataflow = !$useDataflow
|
||||
}}
|
||||
<div style={`height: ${height}px; max-height: ${maxHeight}px;`} bind:clientWidth={width}>
|
||||
{#if graph?.error}
|
||||
<div class="center-center">
|
||||
<Alert title="Error parsing the flow" type="error" class="max-w-1/2">
|
||||
{graph.error}
|
||||
|
||||
<Button
|
||||
color="red"
|
||||
size="xs"
|
||||
options={{
|
||||
right: 'Dataflow'
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</Controls>
|
||||
</SvelteFlow>
|
||||
btnClasses="mt-2 w-min"
|
||||
on:click={() => yamlEditorDrawer?.openDrawer()}>Open YAML editor</Button
|
||||
>
|
||||
</Alert>
|
||||
</div>
|
||||
{:else}
|
||||
<SvelteFlow
|
||||
{nodes}
|
||||
{edges}
|
||||
{edgeTypes}
|
||||
{nodeTypes}
|
||||
{viewport}
|
||||
{height}
|
||||
minZoom={0.5}
|
||||
connectionLineType={ConnectionLineType.SmoothStep}
|
||||
defaultEdgeOptions={{ type: 'smoothstep' }}
|
||||
preventScrolling={scroll}
|
||||
zoomOnDoubleClick={false}
|
||||
elementsSelectable={false}
|
||||
{proOptions}
|
||||
nodesDraggable={false}
|
||||
>
|
||||
<div class="absolute inset-0 !bg-surface-secondary" />
|
||||
<Controls position="top-right" orientation="horizontal" showLock={false}>
|
||||
{#if download}
|
||||
<ControlButton
|
||||
on:click={() => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
'svelvet',
|
||||
encodeState({ modules, failureModule, preprocessorModule })
|
||||
)
|
||||
} catch (e) {
|
||||
console.error('error interacting with local storage', e)
|
||||
}
|
||||
window.open('/view_graph', '_blank')
|
||||
}}
|
||||
class="!bg-surface"
|
||||
>
|
||||
<Expand size="14" />
|
||||
</ControlButton>
|
||||
{/if}
|
||||
</Controls>
|
||||
|
||||
<Controls
|
||||
position="top-left"
|
||||
orientation="horizontal"
|
||||
showLock={false}
|
||||
showZoom={false}
|
||||
showFitView={false}
|
||||
class="!shadow-none"
|
||||
>
|
||||
{#if showDataflow}
|
||||
<Toggle
|
||||
value={$useDataflow}
|
||||
on:change={() => {
|
||||
$useDataflow = !$useDataflow
|
||||
}}
|
||||
size="xs"
|
||||
options={{
|
||||
right: 'Dataflow'
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</Controls>
|
||||
</SvelteFlow>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
@@ -29,190 +29,205 @@ export default function graphBuilder(
|
||||
): {
|
||||
nodes: Node[]
|
||||
edges: Edge[]
|
||||
error?: string | undefined
|
||||
} {
|
||||
const nodes: Node[] = []
|
||||
const edges: Edge[] = []
|
||||
|
||||
if (!modules) {
|
||||
return { nodes, edges }
|
||||
}
|
||||
try {
|
||||
if (!modules) {
|
||||
return { nodes, edges }
|
||||
}
|
||||
|
||||
function addNode(module: FlowModule, offset: number, type: string, subModules?: FlowModule[]) {
|
||||
nodes.push({
|
||||
id: module.id,
|
||||
function addNode(module: FlowModule, offset: number, type: string, subModules?: FlowModule[]) {
|
||||
if (nodes.some((n) => n.id === module.id)) {
|
||||
throw new Error(`Duplicated node detected: ${module.id}`)
|
||||
}
|
||||
|
||||
nodes.push({
|
||||
id: module.id,
|
||||
data: {
|
||||
value: module.value,
|
||||
offset: offset,
|
||||
module: module,
|
||||
modules: subModules ?? modules,
|
||||
parentIds: [],
|
||||
eventHandlers: eventHandlers,
|
||||
moving: moving,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: type
|
||||
})
|
||||
|
||||
return module.id
|
||||
}
|
||||
|
||||
const parents: { [key: string]: string[] } = {}
|
||||
|
||||
//
|
||||
function detectCycle(nodeId: string, visited: Set<string>, currentPath: Set<string>): boolean {
|
||||
// If the node hasn't been visited yet
|
||||
if (!visited.has(nodeId)) {
|
||||
visited.add(nodeId)
|
||||
currentPath.add(nodeId)
|
||||
|
||||
// If the current node has parent nodes
|
||||
if (parents[nodeId]) {
|
||||
// Check each parent node
|
||||
// Nodes can have multiple parents: the node that gathers the result for branches or loops for instance
|
||||
for (const parentNode of parents[nodeId]) {
|
||||
// If the parentNode hasn't been visited and a cycle is detected in its path
|
||||
if (!visited.has(parentNode) && detectCycle(parentNode, visited, currentPath)) {
|
||||
return true // Cycle detected
|
||||
}
|
||||
// If the parentNode is already in the current path, it's a cycle
|
||||
else if (currentPath.has(parentNode)) {
|
||||
return true // Cycle detected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove the node from the current path as we're done processing it
|
||||
currentPath.delete(nodeId)
|
||||
// No cycle detected for this path
|
||||
return false
|
||||
}
|
||||
|
||||
function addEdge(
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
options?: {
|
||||
customId?: string
|
||||
type?: string
|
||||
subModules?: FlowModule[]
|
||||
disableMoveIds?: string[]
|
||||
}
|
||||
) {
|
||||
parents[targetId] = [...(parents[targetId] ?? []), sourceId]
|
||||
|
||||
const mods = options?.subModules ?? modules
|
||||
|
||||
let index = mods?.findIndex((m) => m.id === targetId) ?? -1
|
||||
|
||||
const visited = new Set<string>()
|
||||
const recStack = new Set<string>()
|
||||
|
||||
if (detectCycle(sourceId, visited, recStack)) {
|
||||
throw new Error(
|
||||
`Cycle detected: adding edge from '${sourceId}' to '${targetId}' would create a cycle.`
|
||||
)
|
||||
}
|
||||
|
||||
edges.push({
|
||||
id: options?.customId || `edge:${sourceId}->${targetId}`,
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
type: options?.type ?? 'edge',
|
||||
data: {
|
||||
insertable: extra.insertable,
|
||||
modules: options?.subModules ?? modules,
|
||||
sourceId,
|
||||
targetId,
|
||||
moving,
|
||||
eventHandlers,
|
||||
disableMoveIds: options?.disableMoveIds,
|
||||
enableTrigger: sourceId === 'Input',
|
||||
// If the index is -1, it means that the target module is not in the modules array, so we set it to the length of the array
|
||||
index: index >= 0 ? index : mods?.length ?? 0,
|
||||
...extra
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const inputNode: Node = {
|
||||
id: 'Input',
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'input2',
|
||||
data: {
|
||||
value: module.value,
|
||||
offset: offset,
|
||||
module: module,
|
||||
modules: subModules ?? modules,
|
||||
parentIds: [],
|
||||
eventHandlers: eventHandlers,
|
||||
moving: moving,
|
||||
modules: modules,
|
||||
hasPreprocessor: !!preprocessorModule,
|
||||
...extra
|
||||
}
|
||||
}
|
||||
|
||||
const resultNode: Node = {
|
||||
id: 'result',
|
||||
data: {
|
||||
eventHandlers: eventHandlers,
|
||||
modules: modules,
|
||||
success: success,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: type
|
||||
})
|
||||
|
||||
return module.id
|
||||
}
|
||||
|
||||
const parents: { [key: string]: string[] } = {}
|
||||
|
||||
function addEdge(
|
||||
sourceId: string,
|
||||
targetId: string,
|
||||
options?: {
|
||||
customId?: string
|
||||
type?: string
|
||||
subModules?: FlowModule[]
|
||||
disableMoveIds?: string[]
|
||||
type: 'result'
|
||||
}
|
||||
) {
|
||||
parents[targetId] = [...(parents[targetId] ?? []), sourceId]
|
||||
|
||||
// Find the index of the target module in the modules array
|
||||
const mods = options?.subModules ?? modules
|
||||
nodes.push(inputNode)
|
||||
nodes.push(resultNode)
|
||||
|
||||
// Index of the target module in the modules array
|
||||
let index = mods?.findIndex((m) => m.id === targetId) ?? -1
|
||||
function processModules(
|
||||
modules: FlowModule[],
|
||||
beforeNode: Node,
|
||||
nextNode: Node,
|
||||
currentOffset = 0,
|
||||
disableMoveIds: string[] = [],
|
||||
parentIndex?: string
|
||||
) {
|
||||
let previousId: string | undefined = undefined
|
||||
|
||||
edges.push({
|
||||
id: options?.customId || `edge:${sourceId}->${targetId}`,
|
||||
source: sourceId,
|
||||
target: targetId,
|
||||
type: options?.type ?? 'edge',
|
||||
data: {
|
||||
insertable: extra.insertable,
|
||||
modules: options?.subModules ?? modules,
|
||||
sourceId,
|
||||
targetId,
|
||||
moving,
|
||||
eventHandlers,
|
||||
disableMoveIds: options?.disableMoveIds,
|
||||
enableTrigger: sourceId === 'Input',
|
||||
// If the index is -1, it means that the target module is not in the modules array, so we set it to the length of the array
|
||||
index: index >= 0 ? index : mods?.length ?? 0,
|
||||
...extra
|
||||
}
|
||||
})
|
||||
}
|
||||
if (modules.length === 0) {
|
||||
addEdge(beforeNode.id, nextNode.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
} else {
|
||||
modules.forEach((module, index) => {
|
||||
const localDisableMoveIds = [...disableMoveIds, module.id]
|
||||
|
||||
const inputNode: Node = {
|
||||
id: 'Input',
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'input2',
|
||||
data: {
|
||||
eventHandlers: eventHandlers,
|
||||
modules: modules,
|
||||
hasPreprocessor: !!preprocessorModule,
|
||||
...extra
|
||||
}
|
||||
}
|
||||
|
||||
const resultNode: Node = {
|
||||
id: 'result',
|
||||
data: {
|
||||
eventHandlers: eventHandlers,
|
||||
modules: modules,
|
||||
success: success,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'result'
|
||||
}
|
||||
|
||||
nodes.push(inputNode)
|
||||
nodes.push(resultNode)
|
||||
|
||||
function processModules(
|
||||
modules: FlowModule[],
|
||||
beforeNode: Node,
|
||||
nextNode: Node,
|
||||
currentOffset = 0,
|
||||
disableMoveIds: string[] = [],
|
||||
parentIndex?: string
|
||||
) {
|
||||
let previousId: string | undefined = undefined
|
||||
|
||||
if (modules.length === 0) {
|
||||
addEdge(beforeNode.id, nextNode.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
} else {
|
||||
modules.forEach((module, index) => {
|
||||
const localDisableMoveIds = [...disableMoveIds, module.id]
|
||||
|
||||
// Add the edge between the previous node and the current one
|
||||
if (index > 0 && previousId) {
|
||||
addEdge(previousId, module.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
|
||||
if (module.value.type === 'branchall') {
|
||||
// Start
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
// "Collect result of each branch" node
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
id: module.id,
|
||||
module: module,
|
||||
modules: modules,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchAllEnd'
|
||||
// Add the edge between the previous node and the current one
|
||||
if (index > 0 && previousId) {
|
||||
addEdge(previousId, module.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
|
||||
nodes.push(endNode)
|
||||
if (module.value.type === 'branchall') {
|
||||
// Start
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
if (module.value.branches.length === 0) {
|
||||
// Add a "No branches" node
|
||||
const startNode = {
|
||||
id: `${module.id}-branch-0`,
|
||||
// "Collect result of each branch" node
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
id: module.id,
|
||||
branchIndex: -1,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'noBranch'
|
||||
type: 'branchAllEnd'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
addEdge(startNode.id, endNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
} else {
|
||||
module.value.branches.forEach((branch, branchIndex) => {
|
||||
// Start node by branch
|
||||
nodes.push(endNode)
|
||||
|
||||
if (module.value.branches.length === 0) {
|
||||
// Add a "No branches" node
|
||||
const startNode = {
|
||||
id: `${module.id}-branch-${branchIndex}`,
|
||||
id: `${module.id}-branch-0`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: defaultIfEmptyString(branch.summary, `Branch ${branchIndex + 1}`),
|
||||
id: module.id,
|
||||
branchIndex: branchIndex,
|
||||
branchIndex: -1,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchAllStart'
|
||||
type: 'noBranch'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
@@ -220,6 +235,204 @@ export default function graphBuilder(
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
addEdge(startNode.id, endNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
} else {
|
||||
module.value.branches.forEach((branch, branchIndex) => {
|
||||
// Start node by branch
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-branch-${branchIndex}`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: defaultIfEmptyString(branch.summary, `Branch ${branchIndex + 1}`),
|
||||
id: module.id,
|
||||
branchIndex: branchIndex,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchAllStart'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
|
||||
processModules(
|
||||
branch.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset,
|
||||
localDisableMoveIds,
|
||||
parentIndex ? `${parentIndex}-${index}-${branchIndex}` : `${index}-${branchIndex}`
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'forloopflow') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-start`,
|
||||
data: {
|
||||
offset: currentOffset + 25,
|
||||
id: module.id,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'forLoopStart'
|
||||
}
|
||||
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
id: module.id,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'forLoopEnd'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
nodes.push(endNode)
|
||||
|
||||
const selectedIterIndex = extra.flowModuleStates?.[module.id]?.selectedForloopIndex
|
||||
|
||||
processModules(
|
||||
module.value.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset + 25,
|
||||
localDisableMoveIds,
|
||||
parentIndex
|
||||
? `${parentIndex}-${index}-${selectedIterIndex ?? '?'}`
|
||||
: `${index}-${selectedIterIndex ?? '?'}`
|
||||
)
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'whileloopflow') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-start`,
|
||||
data: {
|
||||
offset: currentOffset + 25,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'whileLoopStart'
|
||||
}
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: { offset: currentOffset, module: module, modules: modules, ...extra },
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'whileLoopEnd'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
nodes.push(endNode)
|
||||
|
||||
const selectedIterIndex = extra.flowModuleStates?.[module.id]?.selectedForloopIndex
|
||||
|
||||
processModules(
|
||||
module.value.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset + 25,
|
||||
localDisableMoveIds,
|
||||
parentIndex
|
||||
? `${parentIndex}-${index}-${selectedIterIndex ?? '?'}`
|
||||
: `${index}-${selectedIterIndex ?? '?'}`
|
||||
)
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'branchone') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: { offset: currentOffset, eventHandlers: eventHandlers },
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchOneEnd'
|
||||
}
|
||||
nodes.push(endNode)
|
||||
|
||||
// Add default branch
|
||||
const defaultBranch = {
|
||||
id: `${module.id}-default`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: 'Default',
|
||||
id: module.id,
|
||||
branchIndex: -1,
|
||||
modules: module.value.default,
|
||||
eventHandlers: eventHandlers,
|
||||
branchOne: true,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'noBranch'
|
||||
}
|
||||
|
||||
nodes.push(defaultBranch)
|
||||
|
||||
addEdge(module.id, defaultBranch.id, { type: 'empty' })
|
||||
|
||||
processModules(
|
||||
module.value.default,
|
||||
defaultBranch,
|
||||
endNode,
|
||||
currentOffset,
|
||||
localDisableMoveIds,
|
||||
parentIndex ? `${parentIndex}-${index}` : index.toString()
|
||||
)
|
||||
|
||||
module.value.branches.forEach((branch, branchIndex) => {
|
||||
// Start node by branch
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-branch-${branchIndex}`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: defaultIfEmptyString(branch.summary, 'Branch ' + (branchIndex + 1)),
|
||||
preLabel: branch.summary ? '' : branch.expr,
|
||||
id: module.id,
|
||||
branchIndex: branchIndex,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchOneStart'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
|
||||
addEdge(module.id, startNode.id, { type: 'empty' })
|
||||
|
||||
processModules(
|
||||
branch.modules,
|
||||
@@ -227,277 +440,113 @@ export default function graphBuilder(
|
||||
endNode,
|
||||
currentOffset,
|
||||
localDisableMoveIds,
|
||||
parentIndex ? `${parentIndex}-${index}-${branchIndex}` : `${index}-${branchIndex}`
|
||||
parentIndex ? `${parentIndex}-${index}` : index.toString()
|
||||
)
|
||||
})
|
||||
|
||||
previousId = endNode.id
|
||||
} else {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
previousId = module.id
|
||||
}
|
||||
|
||||
if (index === 0) {
|
||||
addEdge(beforeNode.id, module.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'forloopflow') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-start`,
|
||||
data: {
|
||||
offset: currentOffset + 25,
|
||||
id: module.id,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'forLoopStart'
|
||||
if (index === modules.length - 1 && previousId) {
|
||||
addEdge(previousId, nextNode.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
if (failureModule) {
|
||||
const id = parentIndex ? `failure-${parentIndex}` : 'failure'
|
||||
const failureState = extra.flowModuleStates?.[id] as GraphModuleState | undefined
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
id: module.id,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'forLoopEnd'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
nodes.push(endNode)
|
||||
|
||||
const selectedIterIndex = extra.flowModuleStates?.[module.id]?.selectedForloopIndex
|
||||
|
||||
processModules(
|
||||
module.value.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset + 25,
|
||||
localDisableMoveIds,
|
||||
parentIndex
|
||||
? `${parentIndex}-${index}-${selectedIterIndex ?? '?'}`
|
||||
: `${index}-${selectedIterIndex ?? '?'}`
|
||||
)
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'whileloopflow') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-start`,
|
||||
data: {
|
||||
offset: currentOffset + 25,
|
||||
module: module,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'whileLoopStart'
|
||||
}
|
||||
addEdge(module.id, startNode.id, {
|
||||
type: 'empty'
|
||||
})
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: { offset: currentOffset, module: module, modules: modules, ...extra },
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'whileLoopEnd'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
nodes.push(endNode)
|
||||
|
||||
const selectedIterIndex = extra.flowModuleStates?.[module.id]?.selectedForloopIndex
|
||||
|
||||
processModules(
|
||||
module.value.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset + 25,
|
||||
localDisableMoveIds,
|
||||
parentIndex
|
||||
? `${parentIndex}-${index}-${selectedIterIndex ?? '?'}`
|
||||
: `${index}-${selectedIterIndex ?? '?'}`
|
||||
)
|
||||
|
||||
previousId = endNode.id
|
||||
} else if (module.value.type === 'branchone') {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
const endNode = {
|
||||
id: `${module.id}-end`,
|
||||
data: { offset: currentOffset, eventHandlers: eventHandlers },
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchOneEnd'
|
||||
}
|
||||
nodes.push(endNode)
|
||||
|
||||
// Add default branch
|
||||
const defaultBranch = {
|
||||
id: `${module.id}-default`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: 'Default',
|
||||
id: module.id,
|
||||
branchIndex: -1,
|
||||
modules: module.value.default,
|
||||
eventHandlers: eventHandlers,
|
||||
branchOne: true,
|
||||
...extra
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'noBranch'
|
||||
}
|
||||
|
||||
nodes.push(defaultBranch)
|
||||
|
||||
addEdge(module.id, defaultBranch.id, { type: 'empty' })
|
||||
|
||||
processModules(
|
||||
module.value.default,
|
||||
defaultBranch,
|
||||
endNode,
|
||||
currentOffset,
|
||||
localDisableMoveIds,
|
||||
parentIndex ? `${parentIndex}-${index}` : index.toString()
|
||||
)
|
||||
|
||||
module.value.branches.forEach((branch, branchIndex) => {
|
||||
// Start node by branch
|
||||
|
||||
const startNode = {
|
||||
id: `${module.id}-branch-${branchIndex}`,
|
||||
data: {
|
||||
offset: currentOffset,
|
||||
label: defaultIfEmptyString(branch.summary, 'Branch ' + (branchIndex + 1)),
|
||||
preLabel: branch.summary ? '' : branch.expr,
|
||||
id: module.id,
|
||||
branchIndex: branchIndex,
|
||||
modules: modules,
|
||||
eventHandlers: eventHandlers,
|
||||
...extra
|
||||
if (failureState && failureState.parent_module) {
|
||||
addNode(
|
||||
{
|
||||
...failureModule,
|
||||
id: id
|
||||
},
|
||||
position: { x: -1, y: -1 },
|
||||
type: 'branchOneStart'
|
||||
}
|
||||
|
||||
nodes.push(startNode)
|
||||
|
||||
addEdge(module.id, startNode.id, { type: 'empty' })
|
||||
|
||||
processModules(
|
||||
branch.modules,
|
||||
startNode,
|
||||
endNode,
|
||||
currentOffset,
|
||||
localDisableMoveIds,
|
||||
parentIndex ? `${parentIndex}-${index}` : index.toString()
|
||||
0,
|
||||
'module'
|
||||
)
|
||||
})
|
||||
|
||||
previousId = endNode.id
|
||||
} else {
|
||||
addNode(module, currentOffset, 'module', modules)
|
||||
|
||||
previousId = module.id
|
||||
}
|
||||
|
||||
if (index === 0) {
|
||||
addEdge(beforeNode.id, module.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
|
||||
if (index === modules.length - 1 && previousId) {
|
||||
addEdge(previousId, nextNode.id, {
|
||||
subModules: modules,
|
||||
disableMoveIds
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (failureModule) {
|
||||
const id = parentIndex ? `failure-${parentIndex}` : 'failure'
|
||||
const failureState = extra.flowModuleStates?.[id] as GraphModuleState | undefined
|
||||
|
||||
if (failureState && failureState.parent_module) {
|
||||
addNode(
|
||||
{
|
||||
...failureModule,
|
||||
id: id
|
||||
},
|
||||
0,
|
||||
'module'
|
||||
)
|
||||
addEdge(failureState.parent_module, id, { type: 'empty' })
|
||||
addEdge(failureState.parent_module, id, { type: 'empty' })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processModules(modules, inputNode, resultNode)
|
||||
processModules(modules, inputNode, resultNode)
|
||||
|
||||
if (preprocessorModule) {
|
||||
addNode(preprocessorModule, 0, 'module')
|
||||
const id = JSON.parse(JSON.stringify(preprocessorModule.id))
|
||||
addEdge(id, 'Input', { type: 'empty' })
|
||||
}
|
||||
|
||||
if (failureModule && !extra.flowModuleStates) {
|
||||
addNode(failureModule, 0, 'module')
|
||||
}
|
||||
|
||||
Object.keys(parents).forEach((key) => {
|
||||
const node = nodes.find((n) => n.id === key)
|
||||
|
||||
if (node) {
|
||||
node.data.parentIds = parents[key]
|
||||
if (preprocessorModule) {
|
||||
addNode(preprocessorModule, 0, 'module')
|
||||
const id = JSON.parse(JSON.stringify(preprocessorModule.id))
|
||||
addEdge(id, 'Input', { type: 'empty' })
|
||||
}
|
||||
})
|
||||
|
||||
if (useDataflow && selectedId) {
|
||||
let deps = getDependeeAndDependentComponents(selectedId, modules ?? [], failureModule)
|
||||
if (failureModule && !extra.flowModuleStates) {
|
||||
addNode(failureModule, 0, 'module')
|
||||
}
|
||||
|
||||
if (deps) {
|
||||
Object.entries(deps.dependees).forEach((x, i) => {
|
||||
const inputs = x[1]
|
||||
Object.keys(parents).forEach((key) => {
|
||||
const node = nodes.find((n) => n.id === key)
|
||||
|
||||
inputs?.forEach((input, index) => {
|
||||
if (node) {
|
||||
node.data.parentIds = parents[key]
|
||||
}
|
||||
})
|
||||
|
||||
if (useDataflow && selectedId) {
|
||||
let deps = getDependeeAndDependentComponents(selectedId, modules ?? [], failureModule)
|
||||
|
||||
if (deps) {
|
||||
Object.entries(deps.dependees).forEach((x, i) => {
|
||||
const inputs = x[1]
|
||||
|
||||
inputs?.forEach((input, index) => {
|
||||
let pid = x[0]
|
||||
|
||||
if (input?.startsWith('flow_input.iter')) {
|
||||
const parent = dfsByModule(selectedId!, modules ?? [])?.pop()
|
||||
|
||||
if (parent?.id) {
|
||||
pid = parent.id
|
||||
}
|
||||
}
|
||||
|
||||
addEdge(pid, selectedId!, {
|
||||
customId: `dep-${pid}-${selectedId}-${input}-${index}`,
|
||||
type: 'dataflowedge'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Object.entries(deps.dependents).forEach((x, i) => {
|
||||
let pid = x[0]
|
||||
|
||||
if (input?.startsWith('flow_input.iter')) {
|
||||
const parent = dfsByModule(selectedId!, modules ?? [])?.pop()
|
||||
|
||||
if (parent?.id) {
|
||||
pid = parent.id
|
||||
}
|
||||
}
|
||||
|
||||
addEdge(pid, selectedId!, {
|
||||
customId: `dep-${pid}-${selectedId}-${input}-${index}`,
|
||||
addEdge(selectedId!, pid, {
|
||||
customId: `dep-${selectedId}-${pid}-${i}`,
|
||||
type: 'dataflowedge'
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Object.entries(deps.dependents).forEach((x, i) => {
|
||||
let pid = x[0]
|
||||
|
||||
addEdge(selectedId!, pid, {
|
||||
customId: `dep-${selectedId}-${pid}-${i}`,
|
||||
type: 'dataflowedge'
|
||||
})
|
||||
})
|
||||
return { nodes, edges }
|
||||
} catch (e) {
|
||||
return {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
error: e
|
||||
}
|
||||
}
|
||||
|
||||
return { nodes, edges }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user