|
| 1 | +import { |
| 2 | + DefaultTaskGroup, |
| 3 | + DEFAULT_EDGE_TYPE, |
| 4 | + DEFAULT_FINALLY_NODE_TYPE, |
| 5 | + DEFAULT_SPACER_NODE_TYPE, |
| 6 | + DEFAULT_TASK_NODE_TYPE, |
| 7 | + DEFAULT_WHEN_OFFSET, |
| 8 | + FinallyNode, |
| 9 | + getEdgesFromNodes, |
| 10 | + getSpacerNodes, |
| 11 | + Graph, |
| 12 | + GraphComponent, |
| 13 | + Layout, |
| 14 | + Model, |
| 15 | + ModelKind, |
| 16 | + Node, |
| 17 | + PipelineDagreLayout, |
| 18 | + PipelineNodeModel, |
| 19 | + RunStatus, |
| 20 | + SpacerNode, |
| 21 | + TaskEdge, |
| 22 | + TaskNode, |
| 23 | + TopologyView, |
| 24 | + useVisualizationController, |
| 25 | + Visualization, |
| 26 | + VisualizationProvider, |
| 27 | + VisualizationSurface, |
| 28 | + WhenDecorator |
| 29 | +} from '@patternfly/react-topology'; |
| 30 | +import * as React from 'react'; |
| 31 | + |
| 32 | +export interface Step { |
| 33 | + id: string; |
| 34 | + type?: string; |
| 35 | + label: string; |
| 36 | + runAfterTasks?: string[]; |
| 37 | + // eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 38 | + data?: any; |
| 39 | +} |
| 40 | + |
| 41 | +const steps: Step[] = [ |
| 42 | + { |
| 43 | + id: 'ebpf', |
| 44 | + label: 'eBPF agents', |
| 45 | + data: { |
| 46 | + status: RunStatus.Succeeded |
| 47 | + } |
| 48 | + }, |
| 49 | + { |
| 50 | + id: 'kafka', |
| 51 | + label: 'Kafka', |
| 52 | + runAfterTasks: ['ebpf'] |
| 53 | + }, |
| 54 | + { |
| 55 | + id: 'flp', |
| 56 | + label: 'Flowlogs pipeline', |
| 57 | + runAfterTasks: ['kafka'], |
| 58 | + data: { |
| 59 | + status: RunStatus.Succeeded |
| 60 | + } |
| 61 | + }, |
| 62 | + { |
| 63 | + id: 'loki', |
| 64 | + label: 'Loki', |
| 65 | + runAfterTasks: ['flp'] |
| 66 | + }, |
| 67 | + { |
| 68 | + id: 'prom', |
| 69 | + label: 'Prometheus', |
| 70 | + runAfterTasks: ['flp'] |
| 71 | + }, |
| 72 | + { |
| 73 | + id: 'exporter-1', |
| 74 | + label: 'OpenTelemetry', |
| 75 | + runAfterTasks: ['flp'] |
| 76 | + }, |
| 77 | + { |
| 78 | + id: 'exporter-2', |
| 79 | + label: 'Kafka', |
| 80 | + runAfterTasks: ['flp'] |
| 81 | + }, |
| 82 | + { |
| 83 | + id: 'exporter-3', |
| 84 | + label: 'IPFIX', |
| 85 | + runAfterTasks: ['flp'] |
| 86 | + }, |
| 87 | + { |
| 88 | + id: 'plugin', |
| 89 | + label: 'Console plugin', |
| 90 | + runAfterTasks: ['loki', 'prom'], |
| 91 | + data: { |
| 92 | + status: RunStatus.Succeeded |
| 93 | + } |
| 94 | + } |
| 95 | +]; |
| 96 | + |
| 97 | +const getSteps = () => { |
| 98 | + return steps.map(s => ({ |
| 99 | + type: s.type || 'DEFAULT_TASK_NODE', |
| 100 | + width: 180, |
| 101 | + height: 32, |
| 102 | + style: { |
| 103 | + padding: [45, 15] |
| 104 | + }, |
| 105 | + ...s |
| 106 | + })) as PipelineNodeModel[]; |
| 107 | +}; |
| 108 | + |
| 109 | +export interface StepProps { |
| 110 | + element: Node; |
| 111 | +} |
| 112 | + |
| 113 | +export const StepNode: React.FunctionComponent<StepProps> = ({ element }) => { |
| 114 | + const data = element.getData(); |
| 115 | + |
| 116 | + const whenDecorator = data?.whenStatus ? ( |
| 117 | + <WhenDecorator element={element} status={data.whenStatus} leftOffset={DEFAULT_WHEN_OFFSET} /> |
| 118 | + ) : null; |
| 119 | + |
| 120 | + return ( |
| 121 | + <TaskNode element={element} status={data?.status}> |
| 122 | + {whenDecorator} |
| 123 | + </TaskNode> |
| 124 | + ); |
| 125 | +}; |
| 126 | + |
| 127 | +const pipelineComponentFactory = (kind: ModelKind, type: string) => { |
| 128 | + if (kind === ModelKind.graph) { |
| 129 | + return GraphComponent; |
| 130 | + } |
| 131 | + switch (type) { |
| 132 | + case DEFAULT_TASK_NODE_TYPE: |
| 133 | + return StepNode; |
| 134 | + case DEFAULT_FINALLY_NODE_TYPE: |
| 135 | + return FinallyNode; |
| 136 | + case 'task-group': |
| 137 | + return DefaultTaskGroup; |
| 138 | + case 'finally-group': |
| 139 | + return DefaultTaskGroup; |
| 140 | + case DEFAULT_SPACER_NODE_TYPE: |
| 141 | + return SpacerNode; |
| 142 | + case 'finally-spacer-edge': |
| 143 | + case DEFAULT_EDGE_TYPE: |
| 144 | + return TaskEdge; |
| 145 | + default: |
| 146 | + return undefined; |
| 147 | + } |
| 148 | +}; |
| 149 | + |
| 150 | +export const PipelineSteps: React.FC = () => { |
| 151 | + const controller = useVisualizationController(); |
| 152 | + React.useEffect(() => { |
| 153 | + controller.fromModel( |
| 154 | + { |
| 155 | + graph: { |
| 156 | + id: 'g1', |
| 157 | + type: 'graph' |
| 158 | + }, |
| 159 | + nodes: getSteps() |
| 160 | + }, |
| 161 | + false |
| 162 | + ); |
| 163 | + }, [controller]); |
| 164 | + |
| 165 | + return ( |
| 166 | + <TopologyView> |
| 167 | + <VisualizationSurface /> |
| 168 | + </TopologyView> |
| 169 | + ); |
| 170 | +}; |
| 171 | + |
| 172 | +PipelineSteps.displayName = 'PipelineSteps'; |
| 173 | + |
| 174 | +export const Pipeline: React.FC = () => { |
| 175 | + const controller = new Visualization(); |
| 176 | + controller.setFitToScreenOnLayout(true); |
| 177 | + controller.registerComponentFactory(pipelineComponentFactory); |
| 178 | + controller.registerLayoutFactory((type: string, graph: Graph): Layout | undefined => new PipelineDagreLayout(graph)); |
| 179 | + const spacerNodes = getSpacerNodes(getSteps()); |
| 180 | + const nodes = [...getSteps(), ...spacerNodes]; |
| 181 | + const edges = getEdgesFromNodes(getSteps()); |
| 182 | + |
| 183 | + const model: Model = { |
| 184 | + nodes, |
| 185 | + edges, |
| 186 | + graph: { |
| 187 | + id: 'g1', |
| 188 | + type: 'graph', |
| 189 | + layout: 'pipelineLayout' |
| 190 | + } |
| 191 | + }; |
| 192 | + |
| 193 | + controller.fromModel(model, false); |
| 194 | + |
| 195 | + return ( |
| 196 | + <VisualizationProvider controller={controller}> |
| 197 | + <VisualizationSurface /> |
| 198 | + </VisualizationProvider> |
| 199 | + ); |
| 200 | +}; |
| 201 | + |
| 202 | +export default Pipeline; |
0 commit comments