From 69fdff742386daded33058116e24850d97aa2f6a Mon Sep 17 00:00:00 2001 From: feedmeapples Date: Wed, 22 Feb 2023 19:09:47 -0500 Subject: [PATCH 1/5] Make workflow show output json good for replay --- workflow/workflow_commands.go | 71 +++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/workflow/workflow_commands.go b/workflow/workflow_commands.go index 78b40515..13c5bee0 100644 --- a/workflow/workflow_commands.go +++ b/workflow/workflow_commands.go @@ -20,6 +20,7 @@ import ( "github.com/temporalio/cli/common/stringify" "github.com/temporalio/cli/dataconverter" "github.com/temporalio/tctl-kit/pkg/color" + "github.com/temporalio/tctl-kit/pkg/iterator" "github.com/temporalio/tctl-kit/pkg/output" "github.com/temporalio/tctl-kit/pkg/pager" "github.com/urfave/cli/v2" @@ -238,20 +239,18 @@ func UnmarshalMemoFromCLI(c *cli.Context) (map[string]interface{}, error) { return memo, nil } -type historyIterator struct { - iter interface { - HasNext() bool - Next() (*historypb.HistoryEvent, error) - } +// historyTableIter adapts history iterator for Table output view +type historyTableIter struct { + iter iterator.Iterator[*historypb.HistoryEvent] maxFieldLength int lastEvent *historypb.HistoryEvent } -func (h *historyIterator) HasNext() bool { +func (h *historyTableIter) HasNext() bool { return h.iter.HasNext() } -func (h *historyIterator) Next() (interface{}, error) { +func (h *historyTableIter) Next() (interface{}, error) { event, err := h.iter.Next() if err != nil { return nil, err @@ -259,7 +258,13 @@ func (h *historyIterator) Next() (interface{}, error) { reflect.ValueOf(h.lastEvent).Elem().Set(reflect.ValueOf(event).Elem()) - return eventRow{ + // adapted structure for Table output view + return struct { + ID string + Time string + Type string + Details string + }{ ID: convert.Int64ToString(event.GetEventId()), Time: common.FormatTime(timestamp.TimeValue(event.GetEventTime()), false), Type: common.ColorEvent(event), @@ -292,18 +297,23 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { fmt.Println(color.Magenta(c, "Progress:")) } - var lastEvent historypb.HistoryEvent // used for print result of this run + var lastEvent historypb.HistoryEvent - po := &output.PrintOptions{ - Fields: []string{"ID", "Time", "Type"}, - FieldsLong: []string{"Details"}, - Pager: pager.Less, - } errChan := make(chan error) go func() { - hIter := sdkClient.GetWorkflowHistory(tcCtx, wid, rid, watch, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) - iter := &historyIterator{iter: hIter, maxFieldLength: maxFieldLength, lastEvent: &lastEvent} - err = output.PrintIterator(c, iter, po) + iter := sdkClient.GetWorkflowHistory(tcCtx, wid, rid, watch, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) + if isJSON { + printReplayableHistory(iter) + } else { + hIter := &historyTableIter{iter: iter, maxFieldLength: maxFieldLength, lastEvent: &lastEvent} + po := &output.PrintOptions{ + Fields: []string{"ID", "Time", "Type"}, + FieldsLong: []string{"Details"}, + Pager: pager.Less, + } + err = output.PrintIterator(c, hIter, po) + } + if err != nil { errChan <- err return @@ -343,6 +353,25 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { } } +func printReplayableHistory(iter iterator.Iterator[*historypb.HistoryEvent]) error { + var events []*historypb.HistoryEvent + for iter.HasNext() { + event, err := iter.Next() + if err != nil { + return err + + } + events = append(events, event) + } + + history := &historypb.History{} + history.Events = events + + common.PrettyPrintJSONObject(history) + + return nil +} + func TerminateWorkflow(c *cli.Context) error { if c.String(common.FlagQuery) != "" { return batch.BatchTerminate(c) @@ -800,7 +829,6 @@ func printRunStatus(c *cli.Context, event *historypb.HistoryEvent) { func ShowHistory(c *cli.Context) error { wid := c.String(common.FlagWorkflowID) rid := c.String(common.FlagRunID) - follow := c.Bool(output.FlagFollow) return printWorkflowProgress(c, wid, rid, follow) @@ -1418,13 +1446,6 @@ func TraceWorkflow(c *cli.Context) error { return nil } -type eventRow struct { - ID string - Time string - Type string - Details string -} - // this only works for ANSI terminal, which means remove existing lines won't work if users redirect to file // ref: https://en.wikipedia.org/wiki/ANSI_escape_code func removePrevious2LinesFromTerminal() { From bc1b7dbbb2601713319f34fecd83e7c30540250b Mon Sep 17 00:00:00 2001 From: feedmeapples Date: Wed, 22 Mar 2023 00:39:44 -0400 Subject: [PATCH 2/5] Use urfave context for printing pretty json --- common/util.go | 13 ++++++++----- schedule/schedule_commands.go | 11 +++++------ workflow/workflow_commands.go | 12 ++++++------ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/common/util.go b/common/util.go index dfca5ef5..d4c91e5b 100644 --- a/common/util.go +++ b/common/util.go @@ -169,7 +169,7 @@ func GetCurrentUserFromEnv() string { return "unknown" } -func PrettyPrintJSONObject(o interface{}) { +func PrettyPrintJSONObject(c *cli.Context, o interface{}) { var b []byte var err error if pb, ok := o.(proto.Message); ok { @@ -179,12 +179,15 @@ func PrettyPrintJSONObject(o interface{}) { b, err = json.MarshalIndent(o, "", " ") } + w := c.App.Writer + if err != nil { - fmt.Printf("Error when try to print pretty: %v", err) - fmt.Println(o) + fmt.Fprintf(w, "Error when try to print pretty: %v", err) + fmt.Fprintln(w, o) } - _, _ = os.Stdout.Write(b) - fmt.Println() + + _, _ = w.Write(b) + fmt.Fprintln(w) } func RequiredFlag(c *cli.Context, optionName string) (string, error) { diff --git a/schedule/schedule_commands.go b/schedule/schedule_commands.go index e65a2350..4409c6fd 100644 --- a/schedule/schedule_commands.go +++ b/schedule/schedule_commands.go @@ -16,7 +16,6 @@ import ( "github.com/temporalio/tctl-kit/pkg/output" "github.com/temporalio/tctl-kit/pkg/pager" "github.com/urfave/cli/v2" - apicommon "go.temporal.io/api/common/v1" commonpb "go.temporal.io/api/common/v1" enumspb "go.temporal.io/api/enums/v1" schedpb "go.temporal.io/api/schedule/v1" @@ -132,7 +131,7 @@ func buildScheduleAction(c *cli.Context) (*schedpb.ScheduleAction, error) { newWorkflow := &workflowpb.NewWorkflowExecutionInfo{ WorkflowId: wid, - WorkflowType: &apicommon.WorkflowType{Name: workflowType}, + WorkflowType: &commonpb.WorkflowType{Name: workflowType}, TaskQueue: &taskqueue.TaskQueue{Name: taskQueue}, Input: inputs, WorkflowExecutionTimeout: timestamp.DurationPtr(time.Second * time.Duration(et)), @@ -202,7 +201,7 @@ func buildSchedule(c *cli.Context) (*schedpb.Schedule, error) { return sched, nil } -func getMemoAndSearchAttributesForSchedule(c *cli.Context) (*apicommon.Memo, *apicommon.SearchAttributes, error) { +func getMemoAndSearchAttributesForSchedule(c *cli.Context) (*commonpb.Memo, *commonpb.SearchAttributes, error) { if memoMap, err := workflow.UnmarshalMemoFromCLI(c); err != nil { return nil, nil, err } else if memo, err := encodeMemo(memoMap); err != nil { @@ -416,7 +415,7 @@ func DescribeSchedule(c *cli.Context) error { } if c.Bool(common.FlagPrintRaw) { - common.PrettyPrintJSONObject(resp) + common.PrettyPrintJSONObject(c, resp) return nil } @@ -446,7 +445,7 @@ func DescribeSchedule(c *cli.Context) error { // more convenient copies of values from Info NextRunTime *time.Time LastRunTime *time.Time - LastRunExecution *apicommon.WorkflowExecution + LastRunExecution *commonpb.WorkflowExecution LastRunActualTime *time.Time Memo map[string]string // json only @@ -577,7 +576,7 @@ func ListSchedules(c *cli.Context) error { Info struct { NextRunTime *time.Time LastRunTime *time.Time - LastRunExecution *apicommon.WorkflowExecution + LastRunExecution *commonpb.WorkflowExecution LastRunActualTime *time.Time } } diff --git a/workflow/workflow_commands.go b/workflow/workflow_commands.go index 13c5bee0..6282fc25 100644 --- a/workflow/workflow_commands.go +++ b/workflow/workflow_commands.go @@ -303,7 +303,7 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { go func() { iter := sdkClient.GetWorkflowHistory(tcCtx, wid, rid, watch, enumspb.HISTORY_EVENT_FILTER_TYPE_ALL_EVENT) if isJSON { - printReplayableHistory(iter) + printReplayableHistory(c, iter) } else { hIter := &historyTableIter{iter: iter, maxFieldLength: maxFieldLength, lastEvent: &lastEvent} po := &output.PrintOptions{ @@ -353,7 +353,7 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { } } -func printReplayableHistory(iter iterator.Iterator[*historypb.HistoryEvent]) error { +func printReplayableHistory(c *cli.Context, iter iterator.Iterator[*historypb.HistoryEvent]) error { var events []*historypb.HistoryEvent for iter.HasNext() { event, err := iter.Next() @@ -367,7 +367,7 @@ func printReplayableHistory(iter iterator.Iterator[*historypb.HistoryEvent]) err history := &historypb.History{} history.Events = events - common.PrettyPrintJSONObject(history) + common.PrettyPrintJSONObject(c, history) return nil } @@ -690,9 +690,9 @@ func DescribeWorkflow(c *cli.Context) error { } if printRaw { - common.PrettyPrintJSONObject(resp) + common.PrettyPrintJSONObject(c, resp) } else { - common.PrettyPrintJSONObject(convertDescribeWorkflowExecutionResponse(c, resp)) + common.PrettyPrintJSONObject(c, convertDescribeWorkflowExecutionResponse(c, resp)) } return nil @@ -890,7 +890,7 @@ func ResetWorkflow(c *cli.Context) error { if err != nil { return fmt.Errorf("reset failed: %w", err) } - common.PrettyPrintJSONObject(resp) + common.PrettyPrintJSONObject(c, resp) return nil } From bed83072c28c32e01c4120bf08320da44430d09e Mon Sep 17 00:00:00 2001 From: feedmeapples Date: Wed, 22 Mar 2023 00:47:27 -0400 Subject: [PATCH 3/5] Address comment --- workflow/workflow_commands.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/workflow/workflow_commands.go b/workflow/workflow_commands.go index 6282fc25..a5f757a4 100644 --- a/workflow/workflow_commands.go +++ b/workflow/workflow_commands.go @@ -243,7 +243,7 @@ func UnmarshalMemoFromCLI(c *cli.Context) (map[string]interface{}, error) { type historyTableIter struct { iter iterator.Iterator[*historypb.HistoryEvent] maxFieldLength int - lastEvent *historypb.HistoryEvent + wfResult *historypb.HistoryEvent } func (h *historyTableIter) HasNext() bool { @@ -256,7 +256,7 @@ func (h *historyTableIter) Next() (interface{}, error) { return nil, err } - reflect.ValueOf(h.lastEvent).Elem().Set(reflect.ValueOf(event).Elem()) + reflect.ValueOf(h.wfResult).Elem().Set(reflect.ValueOf(event).Elem()) // adapted structure for Table output view return struct { @@ -297,7 +297,7 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { fmt.Println(color.Magenta(c, "Progress:")) } - var lastEvent historypb.HistoryEvent + var wfResult historypb.HistoryEvent errChan := make(chan error) go func() { @@ -305,7 +305,7 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { if isJSON { printReplayableHistory(c, iter) } else { - hIter := &historyTableIter{iter: iter, maxFieldLength: maxFieldLength, lastEvent: &lastEvent} + hIter := &historyTableIter{iter: iter, maxFieldLength: maxFieldLength, wfResult: &wfResult} po := &output.PrintOptions{ Fields: []string{"ID", "Time", "Type"}, FieldsLong: []string{"Details"}, @@ -344,7 +344,7 @@ func printWorkflowProgress(c *cli.Context, wid, rid string, watch bool) error { if watch { fmt.Printf(" Run Time: %d seconds\n", timeElapsed) } - printRunStatus(c, &lastEvent) + printRunStatus(c, &wfResult) } return nil case err = <-errChan: From 92652d184f063abbb273900d4bfdafb764d022b2 Mon Sep 17 00:00:00 2001 From: feedmeapples Date: Tue, 28 Mar 2023 14:13:14 -0400 Subject: [PATCH 4/5] . --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 8b476f8c..eda083e5 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pborman/uuid v1.2.1 github.com/stretchr/testify v1.8.2 - github.com/temporalio/tctl-kit v0.0.0-20230104170414-10932650d727 + github.com/temporalio/tctl-kit v0.0.0-20230328153839-577f95d16fa0 github.com/temporalio/ui-server/v2 v2.13.0 github.com/urfave/cli/v2 v2.23.6 go.temporal.io/api v1.18.1 diff --git a/go.sum b/go.sum index 0cd3bcb2..35f2139d 100644 --- a/go.sum +++ b/go.sum @@ -812,8 +812,8 @@ github.com/temporalio/ringpop-go v0.0.0-20220818230611-30bf23b490b2 h1:QIwUh2HCt github.com/temporalio/ringpop-go v0.0.0-20220818230611-30bf23b490b2/go.mod h1:ZEYrWwPO7607ZEaPzK7nWRv55cIrTtH4TeBBu3V532U= github.com/temporalio/tchannel-go v1.22.1-0.20220818200552-1be8d8cffa5b h1:Fs3LdlF7xbnOWHymbFmvIEuxIEt1dNRCfaDkoajSaZk= github.com/temporalio/tchannel-go v1.22.1-0.20220818200552-1be8d8cffa5b/go.mod h1:c+V9Z/ZgkzAdyGvHrvC5AsXgN+M9Qwey04cBdKYzV7U= -github.com/temporalio/tctl-kit v0.0.0-20230104170414-10932650d727 h1:Yrisr5sO+sPzc2ATX4LS8K7iM1L1ww71RIbZk8N240Q= -github.com/temporalio/tctl-kit v0.0.0-20230104170414-10932650d727/go.mod h1:hk/LJCKZNNmtVSWRKepbdUJme+k/4fb/hPkekXk40sk= +github.com/temporalio/tctl-kit v0.0.0-20230328153839-577f95d16fa0 h1:E1iAre7/4VvSJri8uOnItKVsMKnP+WEQourm+zVO0cc= +github.com/temporalio/tctl-kit v0.0.0-20230328153839-577f95d16fa0/go.mod h1:hk/LJCKZNNmtVSWRKepbdUJme+k/4fb/hPkekXk40sk= github.com/temporalio/ui-server/v2 v2.13.0 h1:aKurAPeskgkLYG+1GLm2Cb/1qkoL5wg0gjc776FhRw0= github.com/temporalio/ui-server/v2 v2.13.0/go.mod h1:8yI8soutsbGEKyOblUZeuo1CPgl4U43+yVYnUrIiNto= github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= From fc71dfad9c465f04fbb0fb9f46358115c896e921 Mon Sep 17 00:00:00 2001 From: Ruslan <11838981+feedmeapples@users.noreply.github.com> Date: Tue, 28 Mar 2023 21:55:41 -0400 Subject: [PATCH 5/5] Update workflow/workflow_commands.go Co-authored-by: Alex Shtin --- workflow/workflow_commands.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/workflow/workflow_commands.go b/workflow/workflow_commands.go index a5f757a4..6344843a 100644 --- a/workflow/workflow_commands.go +++ b/workflow/workflow_commands.go @@ -364,8 +364,7 @@ func printReplayableHistory(c *cli.Context, iter iterator.Iterator[*historypb.Hi events = append(events, event) } - history := &historypb.History{} - history.Events = events + history := &historypb.History{Events: events} common.PrettyPrintJSONObject(c, history)