diff --git a/service/frontend/handler.go b/service/frontend/handler.go index d89363f08d3..bc037b91ade 100644 --- a/service/frontend/handler.go +++ b/service/frontend/handler.go @@ -1897,7 +1897,7 @@ func (wh *WorkflowHandler) validateExecution(w *gen.WorkflowExecution, scope int if w.WorkflowId == nil || w.GetWorkflowId() == "" { return wh.error(errWorkflowIDNotSet, scope) } - if w.RunId != nil && uuid.Parse(*w.RunId) == nil { + if w.GetRunId() != "" && uuid.Parse(w.GetRunId()) == nil { return wh.error(errInvalidRunID, scope) } return nil diff --git a/tools/cli/README.md b/tools/cli/README.md index 4081a6b0fe4..37943ed70a2 100644 --- a/tools/cli/README.md +++ b/tools/cli/README.md @@ -84,12 +84,24 @@ User need to run `show` to view workflow history/progress. # a shortcut of this is (without -w -r flag) ./cadence workflow showid 3ea6b242-b23c-4279-bb13-f215661b4717 866ae14c-88cf-4f1e-980f-571e031d71b0 -# if run_id is not provided, it will show the latest run history for that workflow_id +# if run_id is not provided, it will show the latest run history of that workflow_id ./cadence workflow show -w 3ea6b242-b23c-4279-bb13-f215661b4717 # a shortcut of this is ./cadence workflow showid 3ea6b242-b23c-4279-bb13-f215661b4717 ``` +- Show workflow execution info +``` +./cadence workflow descibe -w 3ea6b242-b23c-4279-bb13-f215661b4717 -r 866ae14c-88cf-4f1e-980f-571e031d71b0 +# a shortcut of this is (without -w -r flag) +./cadence workflow descibeid 3ea6b242-b23c-4279-bb13-f215661b4717 866ae14c-88cf-4f1e-980f-571e031d71b0 + +# if run_id is not provided, it will show the latest workflow execution of that workflow_id +./cadence workflow descibe -w 3ea6b242-b23c-4279-bb13-f215661b4717 +# a shortcut of this is +./cadence workflow descibeid 3ea6b242-b23c-4279-bb13-f215661b4717 +``` + - List closed or open workflow executions ``` ./cadence workflow list diff --git a/tools/cli/commands.go b/tools/cli/commands.go index 12cdae34db0..57f0b357d96 100644 --- a/tools/cli/commands.go +++ b/tools/cli/commands.go @@ -324,7 +324,7 @@ func showHistoryHelper(c *cli.Context, wid, rid string) { table.Append([]string{strconv.FormatInt(e.GetEventId(), 10), strconv.FormatInt(e.GetTimestamp(), 10), ColorEvent(e), HistoryEventToString(e)}) } else if printDateTime { table.Append([]string{strconv.FormatInt(e.GetEventId(), 10), convertTime(e.GetTimestamp(), false), ColorEvent(e), HistoryEventToString(e)}) - } else { + } else { // default not show time table.Append([]string{strconv.FormatInt(e.GetEventId(), 10), ColorEvent(e), HistoryEventToString(e)}) } } @@ -662,6 +662,115 @@ func ListAllWorkflow(c *cli.Context) { table.Render() } +// DescribeWorkflow show information about the specified workflow execution +func DescribeWorkflow(c *cli.Context) { + wid := getRequiredOption(c, FlagWorkflowID) + rid := c.String(FlagRunID) + + describeWorkflowHelper(c, wid, rid) +} + +// DescribeWorkflowWithID show information about the specified workflow execution +func DescribeWorkflowWithID(c *cli.Context) { + if !c.Args().Present() { + ExitIfError(errors.New("workflow_id is required")) + } + wid := c.Args().First() + rid := "" + if c.NArg() >= 2 { + rid = c.Args().Get(1) + } + + describeWorkflowHelper(c, wid, rid) +} + +func describeWorkflowHelper(c *cli.Context, wid, rid string) { + wfClient := getWorkflowClient(c) + + printRawTime := c.Bool(FlagPrintRawTime) // default show datetime instead of raw time + + ctx, cancel := newContext() + defer cancel() + + resp, err := wfClient.DescribeWorkflowExecution(ctx, wid, rid) + if err != nil { + ErrorAndExit("Describe workflow execution failed", err) + } + var o interface{} + if printRawTime { + o = resp + } else { + o = convertDescribeWorkflowExecutionResponse(resp) + } + prettyPrintJSONObject(o) +} + +func prettyPrintJSONObject(o interface{}) { + b, err := json.MarshalIndent(o, "", " ") + if err != nil { + fmt.Printf("Error when try to print pretty: %v\n", err) + fmt.Println(o) + } + os.Stdout.Write(b) + fmt.Println() +} + +// describeWorkflowExecutionResponse is used to print datetime instead of print raw time +type describeWorkflowExecutionResponse struct { + ExecutionConfiguration *s.WorkflowExecutionConfiguration + WorkflowExecutionInfo workflowExecutionInfo + PendingActivities []*pendingActivityInfo +} + +// workflowExecutionInfo has same fields as shared.WorkflowExecutionInfo, but has datetime instead of raw time +type workflowExecutionInfo struct { + Execution *s.WorkflowExecution + Type *s.WorkflowType + StartTime *string // change from *int64 + CloseTime *string // change from *int64 + CloseStatus *s.WorkflowExecutionCloseStatus + HistoryLength *int64 +} + +// pendingActivityInfo has same fields as shared.PendingActivityInfo, but different field type for better display +type pendingActivityInfo struct { + ActivityID *string + ActivityType *s.ActivityType + State *s.PendingActivityState + HeartbeatDetails *string // change from byte[] + LastHeartbeatTimestamp *string // change from *int64 +} + +func convertDescribeWorkflowExecutionResponse(resp *s.DescribeWorkflowExecutionResponse) *describeWorkflowExecutionResponse { + info := resp.WorkflowExecutionInfo + executionInfo := workflowExecutionInfo{ + Execution: info.Execution, + Type: info.Type, + StartTime: common.StringPtr(convertTime(info.GetStartTime(), false)), + CloseTime: common.StringPtr(convertTime(info.GetCloseTime(), false)), + CloseStatus: info.CloseStatus, + HistoryLength: info.HistoryLength, + } + var pendingActs []*pendingActivityInfo + var tmpAct *pendingActivityInfo + for _, pa := range resp.PendingActivities { + tmpAct = &pendingActivityInfo{ + ActivityID: pa.ActivityID, + ActivityType: pa.ActivityType, + State: pa.State, + HeartbeatDetails: common.StringPtr(string(pa.HeartbeatDetails)), + LastHeartbeatTimestamp: common.StringPtr(convertTime(pa.GetLastHeartbeatTimestamp(), false)), + } + pendingActs = append(pendingActs, tmpAct) + } + + return &describeWorkflowExecutionResponse{ + ExecutionConfiguration: resp.ExecutionConfiguration, + WorkflowExecutionInfo: executionInfo, + PendingActivities: pendingActs, + } +} + func createTableForListWorkflow(listAll bool) *tablewriter.Table { table := tablewriter.NewWriter(os.Stdout) table.SetBorder(false) diff --git a/tools/cli/workflow.go b/tools/cli/workflow.go index 11d354d41c4..65f1c668448 100644 --- a/tools/cli/workflow.go +++ b/tools/cli/workflow.go @@ -369,5 +369,42 @@ func newWorkflowCommands() []cli.Command { QueryWorkflowUsingStackTrace(c) }, }, + { + Name: "describe", + Aliases: []string{"desc"}, + Usage: "show information of workflow execution", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: FlagWorkflowIDWithAlias, + Usage: "WorkflowID", + }, + cli.StringFlag{ + Name: FlagRunIDWithAlias, + Usage: "RunID", + }, + cli.BoolFlag{ + Name: FlagPrintRawTimeWithAlias, + Usage: "Print raw time stamp", + }, + }, + Action: func(c *cli.Context) { + DescribeWorkflow(c) + }, + }, + { + Name: "describeid", + Aliases: []string{"descid"}, + Usage: "show information of workflow execution with given workflow_id and optional run_id (a shortcut of `describe -w -r `)", + Description: "cadence workflow describeid . workflow_id is required; run_id is optional", + Flags: []cli.Flag{ + cli.BoolFlag{ + Name: FlagPrintRawTimeWithAlias, + Usage: "Print raw time stamp", + }, + }, + Action: func(c *cli.Context) { + DescribeWorkflowWithID(c) + }, + }, } }