github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/dashboard/dashboardserver/payload.go (about) 1 package dashboardserver 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "github.com/spf13/viper" 7 typeHelpers "github.com/turbot/go-kit/types" 8 "github.com/turbot/steampipe/pkg/constants" 9 "github.com/turbot/steampipe/pkg/dashboard/dashboardevents" 10 "github.com/turbot/steampipe/pkg/dashboard/dashboardexecute" 11 "github.com/turbot/steampipe/pkg/steampipeconfig" 12 "github.com/turbot/steampipe/pkg/steampipeconfig/modconfig" 13 "github.com/turbot/steampipe/pkg/version" 14 ) 15 16 func buildDashboardMetadataPayload(workspaceResources *modconfig.ResourceMaps, cloudMetadata *steampipeconfig.CloudMetadata) ([]byte, error) { 17 installedMods := make(map[string]ModDashboardMetadata) 18 for _, mod := range workspaceResources.Mods { 19 // Ignore current mod 20 if mod.FullName == workspaceResources.Mod.FullName { 21 continue 22 } 23 installedMods[mod.FullName] = ModDashboardMetadata{ 24 Title: typeHelpers.SafeString(mod.Title), 25 FullName: mod.FullName, 26 ShortName: mod.ShortName, 27 } 28 } 29 30 payload := DashboardMetadataPayload{ 31 Action: "dashboard_metadata", 32 Metadata: DashboardMetadata{ 33 CLI: DashboardCLIMetadata{ 34 Version: version.VersionString, 35 }, 36 InstalledMods: installedMods, 37 Telemetry: viper.GetString(constants.ArgTelemetry), 38 }, 39 } 40 41 if mod := workspaceResources.Mod; mod != nil { 42 payload.Metadata.Mod = &ModDashboardMetadata{ 43 Title: typeHelpers.SafeString(mod.Title), 44 FullName: mod.FullName, 45 ShortName: mod.ShortName, 46 } 47 } 48 // if telemetry is enabled, send cloud metadata 49 if payload.Metadata.Telemetry != constants.TelemetryNone { 50 payload.Metadata.Cloud = cloudMetadata 51 } 52 53 return json.Marshal(payload) 54 } 55 56 func addBenchmarkChildren(benchmark *modconfig.Benchmark, recordTrunk bool, trunk []string, trunks map[string][][]string) []ModAvailableBenchmark { 57 var children []ModAvailableBenchmark 58 for _, child := range benchmark.GetChildren() { 59 switch t := child.(type) { 60 case *modconfig.Benchmark: 61 childTrunk := make([]string, len(trunk)+1) 62 copy(childTrunk, trunk) 63 childTrunk[len(childTrunk)-1] = t.FullName 64 if recordTrunk { 65 trunks[t.FullName] = append(trunks[t.FullName], childTrunk) 66 } 67 availableBenchmark := ModAvailableBenchmark{ 68 Title: t.GetTitle(), 69 FullName: t.FullName, 70 ShortName: t.ShortName, 71 Tags: t.Tags, 72 Children: addBenchmarkChildren(t, recordTrunk, childTrunk, trunks), 73 } 74 children = append(children, availableBenchmark) 75 } 76 } 77 return children 78 } 79 80 func buildAvailableDashboardsPayload(workspaceResources *modconfig.ResourceMaps) ([]byte, error) { 81 82 payload := AvailableDashboardsPayload{ 83 Action: "available_dashboards", 84 Dashboards: make(map[string]ModAvailableDashboard), 85 Benchmarks: make(map[string]ModAvailableBenchmark), 86 Snapshots: workspaceResources.Snapshots, 87 } 88 89 // if workspace resources has a mod, populate dashboards and benchmarks 90 if workspaceResources.Mod != nil { 91 // build a map of the dashboards provided by each mod 92 93 // iterate over the dashboards for the top level mod - this will include the dashboards from dependency mods 94 for _, dashboard := range workspaceResources.Mod.ResourceMaps.Dashboards { 95 mod := dashboard.Mod 96 // add this dashboard 97 payload.Dashboards[dashboard.FullName] = ModAvailableDashboard{ 98 Title: typeHelpers.SafeString(dashboard.Title), 99 FullName: dashboard.FullName, 100 ShortName: dashboard.ShortName, 101 Tags: dashboard.Tags, 102 ModFullName: mod.FullName, 103 } 104 } 105 106 benchmarkTrunks := make(map[string][][]string) 107 for _, benchmark := range workspaceResources.Mod.ResourceMaps.Benchmarks { 108 if benchmark.IsAnonymous() { 109 continue 110 } 111 112 // Find any benchmarks who have a parent that is a mod - we consider these top-level 113 isTopLevel := false 114 for _, parent := range benchmark.GetParents() { 115 switch parent.(type) { 116 case *modconfig.Mod: 117 isTopLevel = true 118 } 119 } 120 121 mod := benchmark.Mod 122 trunk := []string{benchmark.FullName} 123 124 if isTopLevel { 125 benchmarkTrunks[benchmark.FullName] = [][]string{trunk} 126 } 127 128 availableBenchmark := ModAvailableBenchmark{ 129 Title: benchmark.GetTitle(), 130 FullName: benchmark.FullName, 131 ShortName: benchmark.ShortName, 132 Tags: benchmark.Tags, 133 IsTopLevel: isTopLevel, 134 Children: addBenchmarkChildren(benchmark, isTopLevel, trunk, benchmarkTrunks), 135 ModFullName: mod.FullName, 136 } 137 138 payload.Benchmarks[benchmark.FullName] = availableBenchmark 139 } 140 for benchmarkName, trunks := range benchmarkTrunks { 141 if foundBenchmark, ok := payload.Benchmarks[benchmarkName]; ok { 142 foundBenchmark.Trunks = trunks 143 payload.Benchmarks[benchmarkName] = foundBenchmark 144 } 145 } 146 } 147 148 return json.Marshal(payload) 149 } 150 151 func buildWorkspaceErrorPayload(e *dashboardevents.WorkspaceError) ([]byte, error) { 152 payload := ErrorPayload{ 153 Action: "workspace_error", 154 Error: e.Error.Error(), 155 } 156 return json.Marshal(payload) 157 } 158 159 func buildControlCompletePayload(event *dashboardevents.ControlComplete) ([]byte, error) { 160 payload := ControlEventPayload{ 161 Action: "control_complete", 162 Control: event.Control, 163 Name: event.Name, 164 Progress: event.Progress, 165 ExecutionId: event.ExecutionId, 166 Timestamp: event.Timestamp, 167 } 168 return json.Marshal(payload) 169 } 170 171 func buildControlErrorPayload(event *dashboardevents.ControlError) ([]byte, error) { 172 payload := ControlEventPayload{ 173 Action: "control_error", 174 Control: event.Control, 175 Name: event.Name, 176 Progress: event.Progress, 177 ExecutionId: event.ExecutionId, 178 Timestamp: event.Timestamp, 179 } 180 return json.Marshal(payload) 181 } 182 183 func buildLeafNodeUpdatedPayload(event *dashboardevents.LeafNodeUpdated) ([]byte, error) { 184 payload := LeafNodeUpdatedPayload{ 185 SchemaVersion: fmt.Sprintf("%d", LeafNodeUpdatedSchemaVersion), 186 Action: "leaf_node_updated", 187 DashboardNode: event.LeafNode, 188 ExecutionId: event.ExecutionId, 189 Timestamp: event.Timestamp, 190 } 191 return json.Marshal(payload) 192 } 193 194 func buildExecutionStartedPayload(event *dashboardevents.ExecutionStarted) ([]byte, error) { 195 payload := ExecutionStartedPayload{ 196 SchemaVersion: fmt.Sprintf("%d", ExecutionStartedSchemaVersion), 197 Action: "execution_started", 198 ExecutionId: event.ExecutionId, 199 Panels: event.Panels, 200 Layout: event.Root.AsTreeNode(), 201 Inputs: event.Inputs, 202 Variables: event.Variables, 203 StartTime: event.StartTime, 204 } 205 return json.Marshal(payload) 206 } 207 208 func buildExecutionErrorPayload(event *dashboardevents.ExecutionError) ([]byte, error) { 209 payload := ExecutionErrorPayload{ 210 Action: "execution_error", 211 Error: event.Error.Error(), 212 Timestamp: event.Timestamp, 213 } 214 return json.Marshal(payload) 215 } 216 217 func buildExecutionCompletePayload(event *dashboardevents.ExecutionComplete) ([]byte, error) { 218 snap := dashboardexecute.ExecutionCompleteToSnapshot(event) 219 payload := &ExecutionCompletePayload{ 220 Action: "execution_complete", 221 SchemaVersion: fmt.Sprintf("%d", ExecutionCompletePayloadSchemaVersion), 222 ExecutionId: event.ExecutionId, 223 Snapshot: snap, 224 } 225 return json.Marshal(payload) 226 } 227 228 func buildDisplaySnapshotPayload(snap map[string]any) ([]byte, error) { 229 payload := &DisplaySnapshotPayload{ 230 Action: "execution_complete", 231 SchemaVersion: fmt.Sprintf("%d", ExecutionCompletePayloadSchemaVersion), 232 Snapshot: snap, 233 } 234 return json.Marshal(payload) 235 } 236 237 func buildInputValuesClearedPayload(event *dashboardevents.InputValuesCleared) ([]byte, error) { 238 payload := InputValuesClearedPayload{ 239 Action: "input_values_cleared", 240 ClearedInputs: event.ClearedInputs, 241 ExecutionId: event.ExecutionId, 242 } 243 return json.Marshal(payload) 244 }