github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/ui/dashboard/src/utils/state.ts (about) 1 import dayjs from "dayjs"; 2 import sortBy from "lodash/sortBy"; 3 import { 4 AvailableDashboard, 5 AvailableDashboardsDictionary, 6 DashboardDefinition, 7 DashboardRunState, 8 DashboardsCollection, 9 DependencyPanelProperties, 10 PanelDefinition, 11 PanelLog, 12 PanelsLog, 13 PanelsMap, 14 } from "../types"; 15 import { 16 EdgeProperties, 17 KeyValueStringPairs, 18 NodeProperties, 19 } from "../components/dashboards/common/types"; 20 21 const buildDashboards = ( 22 dashboards: AvailableDashboardsDictionary, 23 benchmarks: AvailableDashboardsDictionary, 24 snapshots: KeyValueStringPairs 25 ): DashboardsCollection => { 26 const dashboardsMap = {}; 27 const builtDashboards: AvailableDashboard[] = []; 28 29 for (const [, dashboard] of Object.entries(dashboards)) { 30 const builtDashboard: AvailableDashboard = { 31 title: dashboard.title, 32 full_name: dashboard.full_name, 33 short_name: dashboard.short_name, 34 type: "dashboard", 35 tags: dashboard.tags, 36 mod_full_name: dashboard.mod_full_name, 37 is_top_level: true, 38 }; 39 dashboardsMap[builtDashboard.full_name] = builtDashboard; 40 builtDashboards.push(builtDashboard); 41 } 42 43 for (const [, benchmark] of Object.entries(benchmarks)) { 44 const builtBenchmark: AvailableDashboard = { 45 title: benchmark.title, 46 full_name: benchmark.full_name, 47 short_name: benchmark.short_name, 48 type: "benchmark", 49 tags: benchmark.tags, 50 mod_full_name: benchmark.mod_full_name, 51 is_top_level: benchmark.is_top_level, 52 trunks: benchmark.trunks, 53 children: benchmark.children, 54 }; 55 dashboardsMap[builtBenchmark.full_name] = builtBenchmark; 56 builtDashboards.push(builtBenchmark); 57 } 58 59 for (const snapshot of Object.keys(snapshots || {})) { 60 const builtSnapshot: AvailableDashboard = { 61 title: snapshot, 62 full_name: snapshot, 63 short_name: snapshot, 64 type: "snapshot", 65 tags: {}, 66 is_top_level: true, 67 }; 68 dashboardsMap[builtSnapshot.full_name] = builtSnapshot; 69 builtDashboards.push(builtSnapshot); 70 } 71 72 return { 73 dashboards: sortBy(builtDashboards, [ 74 (dashboard) => 75 dashboard.title 76 ? dashboard.title.toLowerCase() 77 : dashboard.full_name.toLowerCase(), 78 ]), 79 dashboardsMap, 80 }; 81 }; 82 83 const panelLogTitle = (panel: PanelDefinition) => { 84 switch (panel.panel_type) { 85 case "with": 86 const dependencyPanelProperties = (panel.properties || 87 {}) as DependencyPanelProperties; 88 return panel.title 89 ? panel.title 90 : dependencyPanelProperties && dependencyPanelProperties.name 91 ? dependencyPanelProperties.name 92 : panel.name; 93 case "edge": 94 if (panel.title) { 95 return panel.title; 96 } 97 const edgeProperties = (panel.properties || {}) as EdgeProperties; 98 if (edgeProperties.category) { 99 if (edgeProperties.category.title) { 100 return edgeProperties.category.title as string; 101 } 102 return edgeProperties.category.name as string; 103 } 104 return panel.name; 105 case "node": 106 if (panel.title) { 107 return panel.title; 108 } 109 const nodeProperties = (panel.properties || {}) as NodeProperties; 110 if (nodeProperties.category) { 111 if (nodeProperties.category.title) { 112 return nodeProperties.category.title as string; 113 } 114 return nodeProperties.category.name as string; 115 } 116 return panel.name; 117 default: 118 return panel.title || panel.name; 119 } 120 }; 121 122 const buildPanelLog = ( 123 panel: PanelDefinition, 124 timestamp: string, 125 executionTime?: number 126 ): PanelLog => { 127 return { 128 error: panel.status === "error" ? panel.error : null, 129 executionTime, 130 status: panel.status as DashboardRunState, 131 timestamp, 132 title: panelLogTitle(panel), 133 }; 134 }; 135 136 const buildPanelsLog = (panels: PanelsMap, timestamp: string) => { 137 const panelsLog: PanelsLog = {}; 138 for (const [name, panel] of Object.entries(panels || {})) { 139 panelsLog[name] = [buildPanelLog(panel, timestamp)]; 140 } 141 return panelsLog; 142 }; 143 144 const calculateExecutionTime = ( 145 timestamp: string, 146 panel: PanelDefinition, 147 panelLogs: PanelLog[] 148 ): number | undefined => { 149 let overallTime: number | undefined = undefined; 150 if (panel.status === "complete") { 151 const runningLog = panelLogs.find((l) => l.status === "running"); 152 if (runningLog) { 153 overallTime = dayjs(timestamp).diff(runningLog.timestamp); 154 } 155 } 156 return overallTime; 157 }; 158 159 const addUpdatedPanelLogs = ( 160 panelsLog: PanelsLog, 161 panel: PanelDefinition, 162 timestamp: string 163 ) => { 164 const newPanelsLog = { ...panelsLog }; 165 const newPanelLog = [...(newPanelsLog[panel.name] || [])]; 166 if (newPanelLog.find((l) => l.status === panel.status)) { 167 return newPanelsLog; 168 } else { 169 const overallTime = calculateExecutionTime(timestamp, panel, newPanelLog); 170 newPanelLog.push(buildPanelLog(panel, timestamp, overallTime)); 171 } 172 newPanelsLog[panel.name] = newPanelLog; 173 return newPanelsLog; 174 }; 175 176 const updatePanelsLogFromCompletedPanels = ( 177 panelsLog: PanelsLog, 178 panels: PanelsMap, 179 timestamp: string 180 ) => { 181 const newPanelsLog = { ...panelsLog }; 182 for (const [panelName, panel] of Object.entries(panels || {})) { 183 const newPanelLog = [...(newPanelsLog[panelName] || [])]; 184 // If we have an existing panel log for the same status, don't log it 185 if (newPanelLog.find((l) => l.status === panel.status)) { 186 continue; 187 } 188 const overallTime = calculateExecutionTime(timestamp, panel, newPanelLog); 189 newPanelLog.push(buildPanelLog(panel, timestamp, overallTime)); 190 newPanelsLog[panelName] = newPanelLog; 191 } 192 193 return newPanelsLog; 194 }; 195 196 const buildSelectedDashboardInputsFromSearchParams = (searchParams) => { 197 const selectedDashboardInputs = {}; 198 // @ts-ignore 199 for (const entry of searchParams.entries()) { 200 if (!entry[0].startsWith("input")) { 201 continue; 202 } 203 selectedDashboardInputs[entry[0]] = entry[1]; 204 } 205 return selectedDashboardInputs; 206 }; 207 208 const updateSelectedDashboard = ( 209 selectedDashboard: AvailableDashboard | null, 210 newDashboards: AvailableDashboard[] 211 ) => { 212 if (!selectedDashboard) { 213 return null; 214 } 215 const matchingDashboard = newDashboards.find( 216 (dashboard) => dashboard.full_name === selectedDashboard.full_name 217 ); 218 if (matchingDashboard) { 219 return matchingDashboard; 220 } else { 221 return null; 222 } 223 }; 224 225 const wrapDefinitionInArtificialDashboard = ( 226 definition: PanelDefinition, 227 layout: any 228 ): DashboardDefinition => { 229 const { title: defTitle, ...definitionWithoutTitle } = definition; 230 const { title: layoutTitle, ...layoutWithoutTitle } = layout; 231 return { 232 artificial: true, 233 name: definition.name, 234 title: definition.title, 235 panel_type: "dashboard", 236 children: [ 237 { 238 ...definitionWithoutTitle, 239 ...layoutWithoutTitle, 240 }, 241 ], 242 dashboard: definition.dashboard, 243 }; 244 }; 245 246 export { 247 addUpdatedPanelLogs, 248 buildDashboards, 249 buildPanelsLog, 250 buildSelectedDashboardInputsFromSearchParams, 251 panelLogTitle, 252 updatePanelsLogFromCompletedPanels, 253 updateSelectedDashboard, 254 wrapDefinitionInArtificialDashboard, 255 };