Add temporal workflow signal-with-start command#758
Conversation
|
|
7cd38b6 to
b1da783
Compare
Description and sample usage:
Send an asynchronous notification (Signal) to a Workflow Execution.
If the Workflow Execution is not running or is not found, it starts the
workflow then sends the signal.
```
temporal workflow signal-with-start \
--signal-name YourSignal \
--signal-input '{"some-key": "some-value"}' \
--workflow-id YourWorkflowId \
--type YourWorkflowType \
--task-queue YourTaskQueue \
--input '{"some-key": "some-value"}'
```
5216d71 to
5c0a899
Compare
|
Note: the command doesn't configure Let me know if we usually opt for a different approach here. |
temporalcli/commands.workflow.go
Outdated
| if wfStartOpts.Memo != nil { | ||
| fields, err := encodeMapToPayloads(wfStartOpts.Memo) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| memo = &common.Memo{Fields: fields} | ||
| } |
There was a problem hiding this comment.
I think this kind of logic needs to match temporal workflow start (which uses stringKeysJSONValues for memo). In fact, looking at this in general, is there a reason not to use the Go SDK's high-level SignalWithStartWorkflow call instead of the raw RPC? We use the high-level ExecuteWorkflow call for other workflow start. Then you can just reuse the buildStartOptions helper that exists. Also I would consider moving this into commands.workflow_exec.go with its other start workflow friends.
There was a problem hiding this comment.
In fact, looking at this in general, is there a reason not to use the Go SDK's high-level SignalWithStartWorkflow call instead of the raw RPC?
There's a comment above the workflow service call below - SignalWithStartWorkflow only accepts a single arg for signal. The existing signal command on CLI uses the workflow service call because of this. I think it makes sense to provide the flexibility for multiple signal args and stay consistent with the existing CLI signal command
There was a problem hiding this comment.
I see, that is unfortunate that Go SDK is that way but I agree this needs to support multiple signal arguments. I fear that people that implement a new start option may not know to come update this. But I cannot think of a better way short of having Go SDK support a signal-with-start that takes multiple signal parameters. @Quinn-With-Two-Ns - thoughts here?
I do think it still may make more sense to move that encodeMapToPayloads utility that used to be for schedules only into commands.go for more generic use, and maybe move this command impl to commands.workflow_exec.go next to the other commands that work with the workflow start options.
There was a problem hiding this comment.
signal-with-start moved to commands.workflow_exec.go and encodeMapToPayloads to commands.go
temporal workflow signal-with-start command
cretz
left a comment
There was a problem hiding this comment.
Only minor suggestions regarding moving code a bit and providing output.
I do wonder if we should do update-with-start at the same time in case we learn something about the approach we take there that can affect signal-with-start. But maybe not since the result types/approaches are so different.
temporalcli/commands.workflow.go
Outdated
| if wfStartOpts.Memo != nil { | ||
| fields, err := encodeMapToPayloads(wfStartOpts.Memo) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| memo = &common.Memo{Fields: fields} | ||
| } |
There was a problem hiding this comment.
I see, that is unfortunate that Go SDK is that way but I agree this needs to support multiple signal arguments. I fear that people that implement a new start option may not know to come update this. But I cannot think of a better way short of having Go SDK support a signal-with-start that takes multiple signal parameters. @Quinn-With-Two-Ns - thoughts here?
I do think it still may make more sense to move that encodeMapToPayloads utility that used to be for schedules only into commands.go for more generic use, and maybe move this command impl to commands.workflow_exec.go next to the other commands that work with the workflow start options.
temporalcli/commands.workflow.go
Outdated
| WorkflowIdConflictPolicy: wfStartOpts.WorkflowIDConflictPolicy, | ||
| }, | ||
| ) | ||
| return err |
There was a problem hiding this comment.
The startWorkflow helper when called from workflow start prints a structured output like:
cctx.Printer.Println(color.MagentaString("Running execution:"))
err := cctx.Printer.PrintStructured(struct {
WorkflowId string `json:"workflowId"`
RunId string `json:"runId"`
Type string `json:"type"`
Namespace string `json:"namespace"`
TaskQueue string `json:"taskQueue"`
}{
WorkflowId: run.GetID(),
RunId: run.GetRunID(),
Type: sharedWorkflowOpts.Type,
Namespace: c.Namespace,
TaskQueue: sharedWorkflowOpts.TaskQueue,
}, printer.StructuredOptions{})I think we should match that output here too (not much code reuse though, so just need to copy the logic probably)
There was a problem hiding this comment.
Added (along with a check in test for expected output)
cretz
left a comment
There was a problem hiding this comment.
This LGTM. Only reason I hesitate to mark approved is because I'd like to see at least a high-level discussed/approved design of update with start in CLI just to make sure we're not missing anything.
| err = cctx.Printer.PrintStructured(struct { | ||
| WorkflowId string `json:"workflowId"` | ||
| RunId string `json:"runId"` | ||
| Type string `json:"type"` | ||
| Namespace string `json:"namespace"` | ||
| TaskQueue string `json:"taskQueue"` | ||
| }{ | ||
| WorkflowId: c.WorkflowId, | ||
| RunId: resp.RunId, | ||
| Type: c.Type, | ||
| Namespace: c.Parent.Namespace, | ||
| TaskQueue: c.TaskQueue, | ||
| }, printer.StructuredOptions{}) | ||
| if err != nil { | ||
| return fmt.Errorf("failed printing: %w", err) | ||
| } | ||
| return nil |
There was a problem hiding this comment.
| err = cctx.Printer.PrintStructured(struct { | |
| WorkflowId string `json:"workflowId"` | |
| RunId string `json:"runId"` | |
| Type string `json:"type"` | |
| Namespace string `json:"namespace"` | |
| TaskQueue string `json:"taskQueue"` | |
| }{ | |
| WorkflowId: c.WorkflowId, | |
| RunId: resp.RunId, | |
| Type: c.Type, | |
| Namespace: c.Parent.Namespace, | |
| TaskQueue: c.TaskQueue, | |
| }, printer.StructuredOptions{}) | |
| if err != nil { | |
| return fmt.Errorf("failed printing: %w", err) | |
| } | |
| return nil | |
| return cctx.Printer.PrintStructured(struct { | |
| WorkflowId string `json:"workflowId"` | |
| RunId string `json:"runId"` | |
| Type string `json:"type"` | |
| Namespace string `json:"namespace"` | |
| TaskQueue string `json:"taskQueue"` | |
| }{ | |
| WorkflowId: c.WorkflowId, | |
| RunId: resp.RunId, | |
| Type: c.Type, | |
| Namespace: c.Parent.Namespace, | |
| TaskQueue: c.TaskQueue, | |
| }, printer.StructuredOptions{}) |
Pedantic, but don't really need to wrap printing failures I don't think (even though we did in some other places)
Ok, I can put up a separate |
cretz
left a comment
There was a problem hiding this comment.
After internal discussion, this is like what we'll do with update-with-start, LGTM
Added
temporal workflow signal-with-startcommand.Description and sample usage:
Closes [Feature Request] SignalWithStartWorkflow support #332
How was this tested:
Added unit tests
Any docs updates needed?
Yes