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  }