Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions internal/api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,12 @@ func (c *Client) ListExecutions(opts ListExecutionsOptions) (*ListResult[Executi
}

// GetExecution returns an execution by ID
func (c *Client) GetExecution(id string) (*Execution, error) {
respBody, err := c.request(http.MethodGet, "/executions/"+id, nil)
func (c *Client) GetExecution(id string, includeData bool) (*Execution, error) {
path := "/executions/" + id
if includeData {
path += "?includeData=true"
}
respBody, err := c.request(http.MethodGet, path, nil)
if err != nil {
return nil, err
}
Expand Down
103 changes: 101 additions & 2 deletions internal/cmd/execution/execution.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ func newListCmd() *cobra.Command {
}

func newViewCmd() *cobra.Command {
return &cobra.Command{
var showData bool

cmd := &cobra.Command{
Use: "view <execution-id>",
Short: "View execution details",
Args: cobra.ExactArgs(1),
Expand All @@ -191,7 +193,7 @@ func newViewCmd() *cobra.Command {
return err
}

exec, err := client.GetExecution(args[0])
exec, err := client.GetExecution(args[0], showData)
if err != nil {
return fmt.Errorf("failed to get execution: %w", err)
}
Expand Down Expand Up @@ -240,9 +242,106 @@ func newViewCmd() *cobra.Command {
fmt.Printf("\nError: %s\n", exec.Error)
}

if showData && exec.Data != nil {
printNodeData(exec.Data)
}

return nil
},
}

cmd.Flags().BoolVar(&showData, "data", false, "Include per-node execution data")

return cmd
}

func printNodeData(data map[string]interface{}) {
resultData, ok := data["resultData"].(map[string]interface{})
if !ok {
return
}
runData, ok := resultData["runData"].(map[string]interface{})
if !ok {
return
}

fmt.Printf("\nNode Execution Data:\n")
fmt.Printf("────────────────────\n")

for nodeName, nodeRuns := range runData {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The iteration order for maps in Go is not guaranteed. To ensure a consistent and deterministic output for the nodes, it's best to sort the node names alphabetically before iterating and printing the data.

You'll need to import the sort package for this.

	nodeNames := make([]string, 0, len(runData))
	for nodeName := range runData {
		nodeNames = append(nodeNames, nodeName)
	}
	sort.Strings(nodeNames)

	for _, nodeName := range nodeNames {
		nodeRuns := runData[nodeName]
		// ... rest of the loop
	}

runs, ok := nodeRuns.([]interface{})
if !ok || len(runs) == 0 {
continue
}

// Use the last run entry for this node
run, ok := runs[len(runs)-1].(map[string]interface{})
if !ok {
continue
}

// Determine status
nodeStatus := "success"
if _, hasError := run["error"]; hasError {
nodeStatus = "error"
}

// Get execution time
execTimeMs := ""
if et, ok := run["executionTime"].(float64); ok {
execTimeMs = fmt.Sprintf("%dms", int(et))
}

// Count input/output items
inputItems, outputItems := countItems(run)

fmt.Printf("\n %s\n", nodeName)
fmt.Printf(" Status: %s\n", nodeStatus)
if inputItems >= 0 || outputItems >= 0 {
parts := []string{}
if inputItems >= 0 {
parts = append(parts, fmt.Sprintf("%d input", inputItems))
}
if outputItems >= 0 {
parts = append(parts, fmt.Sprintf("%d output", outputItems))
}
fmt.Printf(" Items: %s\n", strings.Join(parts, ", "))
}
if execTimeMs != "" {
fmt.Printf(" Time: %s\n", execTimeMs)
}
}
}

func countItems(run map[string]interface{}) (input, output int) {
input = -1
output = -1

// Input items from inputData
if inputData, ok := run["inputData"].(map[string]interface{}); ok {
if main, ok := inputData["main"].([]interface{}); ok {
input = 0
for _, branch := range main {
if items, ok := branch.([]interface{}); ok {
input += len(items)
}
}
}
}

// Output items from data.main
if data, ok := run["data"].(map[string]interface{}); ok {
if main, ok := data["main"].([]interface{}); ok {
output = 0
for _, branch := range main {
if items, ok := branch.([]interface{}); ok {
output += len(items)
}
}
}
}

return
Comment on lines +317 to +344

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The countItems function has duplicated logic for counting input and output items. This can be refactored by extracting the common counting logic into a local helper function to improve readability and maintainability.

	input = -1
	output = -1

	countBranchItems := func(main []interface{}) int {
		count := 0
		for _, branch := range main {
			if items, ok := branch.([]interface{}); ok {
				count += len(items)
			}
		}
		return count
	}

	// Input items from inputData
	if inputData, ok := run["inputData"].(map[string]interface{}); ok {
		if main, ok := inputData["main"].([]interface{}); ok {
			input = countBranchItems(main)
		}
	}

	// Output items from data.main
	if data, ok := run["data"].(map[string]interface{}); ok {
		if main, ok := data["main"].([]interface{}); ok {
			output = countBranchItems(main)
		}
	}

	return

}

func newRetryCmd() *cobra.Command {
Expand Down