diff --git a/internal/workflow.go b/internal/workflow.go index 5b0758919..517b26d8a 100644 --- a/internal/workflow.go +++ b/internal/workflow.go @@ -28,6 +28,7 @@ import ( "context" "errors" "fmt" + "reflect" "strings" "time" @@ -2649,13 +2650,18 @@ func (wc *workflowEnvironmentInterceptor) prepareNexusOperationParams(ctx Contex var ok bool var operationName string if operationName, ok = input.Operation.(string); ok { - } else if regOp, ok := input.Operation.(interface{ Name() string }); ok { + } else if regOp, ok := input.Operation.(interface { + Name() string + InputType() reflect.Type + }); ok { operationName = regOp.Name() + inputType := reflect.TypeOf(input.Input) + if inputType != nil && !inputType.AssignableTo(regOp.InputType()) { + return executeNexusOperationParams{}, fmt.Errorf("cannot assign argument of type %s to type %s for operation %s", inputType.Name(), regOp.InputType().Name(), operationName) + } } else { return executeNexusOperationParams{}, fmt.Errorf("invalid 'operation' parameter, must be an OperationReference or a string") } - // TODO(bergundy): Validate operation types against input once there's a good way to extract the generic types from - // OperationReference in the Nexus Go SDK. payload, err := dc.ToPayload(input.Input) if err != nil { diff --git a/test/nexus_test.go b/test/nexus_test.go index 1944bc2eb..102c654ed 100644 --- a/test/nexus_test.go +++ b/test/nexus_test.go @@ -557,6 +557,25 @@ func TestSyncOperationFromWorkflow(t *testing.T) { }) } +func TestInvalidOperationInput(t *testing.T) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + tc := newTestContext(t, ctx) + + wf := func(ctx workflow.Context) error { + c := workflow.NewNexusClient(tc.endpoint, "test") + fut := c.ExecuteOperation(ctx, workflowOp, 3456, workflow.NexusOperationOptions{}) + return fut.Get(ctx, nil) + } + w := worker.New(tc.client, tc.taskQueue, worker.Options{}) + w.RegisterWorkflow(wf) + w.Start() + t.Cleanup(w.Stop) + run, err := tc.client.ExecuteWorkflow(ctx, client.StartWorkflowOptions{TaskQueue: tc.taskQueue}, wf) + require.NoError(t, err) + require.ErrorContains(t, run.Get(ctx, nil), `cannot assign argument of type int to type string for operation workflow-op`) +} + func TestSignalOperationFromWorkflow(t *testing.T) { receiverID := "nexus-signal-receiver-" + uuid.NewString()