diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29707789..74af3bd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,7 +88,7 @@ jobs: TEMPORAL_CLOUD_MTLS_TEST_CLIENT_KEY: ${{ secrets.TEMPORAL_CLIENT_KEY }} # For API key tests - TEMPORAL_CLOUD_API_KEY_TEST_TARGET_HOST: us-west-2.aws.api.temporal.io:7233 + TEMPORAL_CLOUD_API_KEY_TEST_TARGET_HOST: us-east-1.aws.api.temporal.io:7233 TEMPORAL_CLOUD_API_KEY_TEST_NAMESPACE: ${{ vars.TEMPORAL_CLIENT_NAMESPACE }} TEMPORAL_CLOUD_API_KEY_TEST_API_KEY: ${{ secrets.TEMPORAL_CLIENT_CLOUD_API_KEY }} diff --git a/temporalio/lib/temporalio/converters/payload_converter/composite.rb b/temporalio/lib/temporalio/converters/payload_converter/composite.rb index 321af21a..35fcf087 100644 --- a/temporalio/lib/temporalio/converters/payload_converter/composite.rb +++ b/temporalio/lib/temporalio/converters/payload_converter/composite.rb @@ -49,12 +49,14 @@ def to_payload(value, hint: nil) # Convert payload to Ruby value based on its +encoding+ metadata on the payload. # - # @param payload [Api::Common::V1::Payload] Payload to convert. + # @param payload [Api::Common::V1::Payload, nil] Payload to convert. # @param hint [Object, nil] Hint, if any, to assist conversion. - # @return [Object] Converted Ruby value. + # @return [Object, nil] Converted Ruby value. # @raise [EncodingNotSet] If encoding not set on the metadata. # @raise [ConverterNotFound] If no converter found for the encoding. def from_payload(payload, hint: nil) + return nil unless payload + encoding = payload.metadata['encoding'] raise EncodingNotSet, 'Missing payload encoding' unless encoding diff --git a/temporalio/sig/temporalio/converters/data_converter.rbs b/temporalio/sig/temporalio/converters/data_converter.rbs index 7056142c..dd123287 100644 --- a/temporalio/sig/temporalio/converters/data_converter.rbs +++ b/temporalio/sig/temporalio/converters/data_converter.rbs @@ -13,14 +13,20 @@ module Temporalio ?payload_codec: PayloadCodec? ) -> void - def to_payload: (Object? value, ?hint: Object?) -> untyped - def to_payloads: (Array[Object?] values, ?hints: Array[Object]?) -> untyped + def to_payload: (Object? value, ?hint: Object?) -> Temporalio::Api::Common::V1::Payload + def to_payloads: ( + Array[Object?] values, + ?hints: Array[Object]? + ) -> Temporalio::Api::Common::V1::Payloads - def from_payload: (untyped payload, ?hint: Object?) -> Object? - def from_payloads: (untyped payloads, ?hints: Array[Object]?) -> Array[Object?] + def from_payload: (Temporalio::Api::Common::V1::Payload payload, ?hint: Object?) -> Object? + def from_payloads: ( + Temporalio::Api::Common::V1::Payloads payloads, + ?hints: Array[Object]? + ) -> Array[Object?] def to_failure: (Exception error) -> untyped def from_failure: (untyped failure) -> Exception end end -end \ No newline at end of file +end diff --git a/temporalio/sig/temporalio/converters/payload_converter.rbs b/temporalio/sig/temporalio/converters/payload_converter.rbs index 5ac4c055..de2114cb 100644 --- a/temporalio/sig/temporalio/converters/payload_converter.rbs +++ b/temporalio/sig/temporalio/converters/payload_converter.rbs @@ -8,11 +8,17 @@ module Temporalio ?json_generate_options: Hash[Symbol, untyped] ) -> Composite - def to_payload: (Object? value, ?hint: Object?) -> untyped - def to_payloads: (Array[Object?] values, ?hints: Array[Object]?) -> untyped + def to_payload: (Object? value, ?hint: Object?) -> Temporalio::Api::Common::V1::Payload + def to_payloads: ( + Array[Object?] values, + ?hints: Array[Object]? + ) -> Temporalio::Api::Common::V1::Payloads - def from_payload: (untyped payload, ?hint: Object?) -> Object? - def from_payloads: (untyped payloads, ?hints: Array[Object]?) -> Array[Object?] + def from_payload: (Temporalio::Api::Common::V1::Payload? payload, ?hint: Object?) -> Object? + def from_payloads: ( + Temporalio::Api::Common::V1::Payloads payloads, + ?hints: Array[Object]? + ) -> Array[Object?] end end -end \ No newline at end of file +end diff --git a/temporalio/sig/temporalio/converters/payload_converter/encoding.rbs b/temporalio/sig/temporalio/converters/payload_converter/encoding.rbs index 2bbee826..2b3de533 100644 --- a/temporalio/sig/temporalio/converters/payload_converter/encoding.rbs +++ b/temporalio/sig/temporalio/converters/payload_converter/encoding.rbs @@ -4,10 +4,10 @@ module Temporalio class Encoding def encoding: -> String - def to_payload: (Object? value, ?hint: Object?) -> untyped? + def to_payload: (Object? value, ?hint: Object?) -> Temporalio::Api::Common::V1::Payload? - def from_payload: (untyped payload, ?hint: Object?) -> Object? + def from_payload: (Temporalio::Api::Common::V1::Payload payload, ?hint: Object?) -> Object? end end end -end \ No newline at end of file +end diff --git a/temporalio/sig/temporalio/internal/worker/workflow_instance/child_workflow_handle.rbs b/temporalio/sig/temporalio/internal/worker/workflow_instance/child_workflow_handle.rbs index b55a8ed0..cf1d144d 100644 --- a/temporalio/sig/temporalio/internal/worker/workflow_instance/child_workflow_handle.rbs +++ b/temporalio/sig/temporalio/internal/worker/workflow_instance/child_workflow_handle.rbs @@ -12,9 +12,11 @@ module Temporalio result_hint: Object? ) -> void - def _resolve: (untyped resolution) -> void + def _resolve: ( + Temporalio::Internal::Bridge::Api::ChildWorkflow::ChildWorkflowResult resolution + ) -> void end end end end -end \ No newline at end of file +end diff --git a/temporalio/sig/temporalio/internal/worker/workflow_instance/outbound_implementation.rbs b/temporalio/sig/temporalio/internal/worker/workflow_instance/outbound_implementation.rbs index e3d55140..47b01144 100644 --- a/temporalio/sig/temporalio/internal/worker/workflow_instance/outbound_implementation.rbs +++ b/temporalio/sig/temporalio/internal/worker/workflow_instance/outbound_implementation.rbs @@ -32,4 +32,4 @@ module Temporalio end end end -end \ No newline at end of file +end diff --git a/temporalio/test/converters/payload_converter_test.rb b/temporalio/test/converters/payload_converter_test.rb index 65d12ed2..1c550222 100644 --- a/temporalio/test/converters/payload_converter_test.rb +++ b/temporalio/test/converters/payload_converter_test.rb @@ -78,6 +78,10 @@ def test_default_converter assert_equal 'temporal.api.common.v1.WorkflowExecution', payload.metadata['messageType'] end + def test_missing_payload_decodes_to_nil + assert_nil Temporalio::Converters::PayloadConverter.default.from_payload(nil) + end + def test_binary_proto # Make a new converter with all default converters except json proto so # that binary proto takes precedent diff --git a/temporalio/test/golangworker/main.go b/temporalio/test/golangworker/main.go index 53233ecf..6f1671ba 100644 --- a/temporalio/test/golangworker/main.go +++ b/temporalio/test/golangworker/main.go @@ -11,6 +11,7 @@ import ( "time" "github.com/nexus-rpc/sdk-go/nexus" + "go.temporal.io/sdk/activity" "go.temporal.io/sdk/client" sdklog "go.temporal.io/sdk/log" "go.temporal.io/sdk/temporal" @@ -72,6 +73,14 @@ func NexusHandlerWorkflow(ctx workflow.Context, input NexusHandlerInput) (NexusH } } +func NoResultWorkflow(ctx workflow.Context) error { + return nil +} + +func NoResultActivity(context.Context) error { + return nil +} + func main() { if len(os.Args) != 4 { log.Fatalf("expected endpoint, namespace, and task queue arg, found %v args", len(os.Args)-1) @@ -97,6 +106,8 @@ func run(endpoint, namespace, taskQueue string) error { w := worker.New(cl, taskQueue, worker.Options{}) w.RegisterWorkflowWithOptions(KitchenSinkWorkflow, workflow.RegisterOptions{Name: "kitchen_sink"}) w.RegisterWorkflowWithOptions(NexusHandlerWorkflow, workflow.RegisterOptions{Name: "nexus_handler"}) + w.RegisterWorkflowWithOptions(NoResultWorkflow, workflow.RegisterOptions{Name: "no_result"}) + w.RegisterActivityWithOptions(NoResultActivity, activity.RegisterOptions{Name: "no_result_activity"}) // Register Nexus service nexusService := nexus.NewService("test-service") diff --git a/temporalio/test/sig/workflow_utils.rbs b/temporalio/test/sig/workflow_utils.rbs index 38a0009b..f604be79 100644 --- a/temporalio/test/sig/workflow_utils.rbs +++ b/temporalio/test/sig/workflow_utils.rbs @@ -54,4 +54,10 @@ module WorkflowUtils handle: Temporalio::Client::WorkflowHandle, ?message_contains: String? ) -> void + + def assert_eventually_complete: ( + handle: Temporalio::Client::WorkflowHandle, + ?timeout: Float, + ?interval: Float + ) -> untyped end diff --git a/temporalio/test/worker_workflow_activity_test.rb b/temporalio/test/worker_workflow_activity_test.rb index ff75436c..0e5e202f 100644 --- a/temporalio/test/worker_workflow_activity_test.rb +++ b/temporalio/test/worker_workflow_activity_test.rb @@ -57,6 +57,24 @@ def test_simple execute_workflow(SimpleWorkflow, :local_string_name, activities: [SimpleActivity]) end + class GoNoResultActivityWorkflow < Temporalio::Workflow::Definition + def execute(activity_task_queue) + Temporalio::Workflow.execute_activity( + 'no_result_activity', + task_queue: activity_task_queue, + start_to_close_timeout: 10 + ) + end + end + + def test_activity_without_result_from_go_sdk + env.with_kitchen_sink_worker do |activity_task_queue| + execute_workflow(GoNoResultActivityWorkflow, activity_task_queue) do |handle| + assert_nil assert_eventually_complete(handle:) + end + end + end + class FailureActivity < Temporalio::Activity::Definition def execute raise Temporalio::Error::ApplicationError.new('Intentional error', 'detail1', 'detail2', non_retryable: true) diff --git a/temporalio/test/worker_workflow_child_test.rb b/temporalio/test/worker_workflow_child_test.rb index bbeabe6c..ad6cc3a3 100644 --- a/temporalio/test/worker_workflow_child_test.rb +++ b/temporalio/test/worker_workflow_child_test.rb @@ -277,6 +277,20 @@ def test_search_attributes assert_equal({ ATTR_KEY_TEXT.name => 'changed-text', ATTR_KEY_KEYWORD.name => 'some-keyword' }, results) end + class GoNoResultParentWorkflow < Temporalio::Workflow::Definition + def execute(child_task_queue) + Temporalio::Workflow.execute_child_workflow('no_result', task_queue: child_task_queue) + end + end + + def test_child_workflow_without_result_from_go_sdk + env.with_kitchen_sink_worker do |child_task_queue| + execute_workflow(GoNoResultParentWorkflow, child_task_queue) do |handle| + assert_nil assert_eventually_complete(handle:) + end + end + end + class ManyChildrenActivity < Temporalio::Activity::Definition def execute(name) "Hello #{name}" diff --git a/temporalio/test/workflow_utils.rb b/temporalio/test/workflow_utils.rb index 539c013d..93a53e21 100644 --- a/temporalio/test/workflow_utils.rb +++ b/temporalio/test/workflow_utils.rb @@ -79,4 +79,25 @@ def assert_eventually_task_fail(handle:, message_contains: nil) event end end + + def assert_eventually_complete(handle:, timeout: 10, interval: 0.2) + start_time = Time.now + loop do + events = handle.fetch_history_events + + task_fail_event = events.find(&:workflow_task_failed_event_attributes) + if task_fail_event + flunk( + 'Workflow task failed instead of completing: ' \ + "#{task_fail_event.workflow_task_failed_event_attributes.failure.message}" + ) + end + + return handle.result if events.any?(&:workflow_execution_completed_event_attributes) + + flunk('Timed out waiting for workflow completion') if Time.now - start_time > timeout + + sleep(interval) + end + end end