diff --git a/temporalio/lib/temporalio/internal/worker/workflow_instance.rb b/temporalio/lib/temporalio/internal/worker/workflow_instance.rb index 92e8f81d..1165930b 100644 --- a/temporalio/lib/temporalio/internal/worker/workflow_instance.rb +++ b/temporalio/lib/temporalio/internal/worker/workflow_instance.rb @@ -133,6 +133,7 @@ def initialize(details) last_result: if @init_job.last_completion_result @payload_converter.from_payloads(@init_job.last_completion_result).first end, + has_last_result?: !@init_job.last_completion_result.nil?, namespace: details.namespace, parent: if @init_job.parent_workflow_info Workflow::Info::ParentInfo.new( diff --git a/temporalio/lib/temporalio/workflow/info.rb b/temporalio/lib/temporalio/workflow/info.rb index 6cc6d4fe..566be9a5 100644 --- a/temporalio/lib/temporalio/workflow/info.rb +++ b/temporalio/lib/temporalio/workflow/info.rb @@ -11,6 +11,7 @@ module Workflow :headers, :last_failure, :last_result, + :has_last_result?, :namespace, :parent, :priority, @@ -44,6 +45,9 @@ module Workflow # @return [Exception, nil] Failure if this workflow run is a continuation of a failure. # @!attribute last_result # @return [Object, nil] Successful result if this workflow is a continuation of a success. + # @!attribute has_last_result? + # @return [Boolean] Successful result if this workflow is a continuation of a success. + # @!attribute namespace # @return [String] Namespace for the workflow. # @!attribute parent diff --git a/temporalio/sig/temporalio/workflow/info.rbs b/temporalio/sig/temporalio/workflow/info.rbs index 596a92ab..e8fc4b72 100644 --- a/temporalio/sig/temporalio/workflow/info.rbs +++ b/temporalio/sig/temporalio/workflow/info.rbs @@ -9,6 +9,7 @@ module Temporalio attr_reader headers: Hash[String, untyped] attr_reader last_failure: Exception? attr_reader last_result: Object? + attr_reader has_last_result?: bool attr_reader namespace: String attr_reader parent: ParentInfo? attr_reader priority: Temporalio::Priority @@ -31,6 +32,7 @@ module Temporalio headers: Hash[String, untyped], last_failure: Exception?, last_result: Object?, + has_last_result?: bool, namespace: String, parent: ParentInfo?, priority: Temporalio::Priority?, diff --git a/temporalio/test/client_test.rb b/temporalio/test/client_test.rb index 70d2835b..282ad468 100644 --- a/temporalio/test/client_test.rb +++ b/temporalio/test/client_test.rb @@ -267,4 +267,90 @@ def test_binary_metadata ensure env.client.connection.rpc_metadata = orig_metadata end + + class ScheduleResult + def initialize(client, handle) + @handle = handle + @client = client + end + + def result + desc = @handle.describe + # oldest first + workflow_id = desc.info.recent_actions.last&.action&.workflow_id + return nil if workflow_id.nil? + + workflow_handle = @client.workflow_handle(workflow_id) + workflow_handle.result + end + end + + class LastResultWorkflow < Temporalio::Workflow::Definition + def execute + last_result = Temporalio::Workflow.info.last_result + return "The last result was #{last_result}" unless last_result.nil? + + 'First result' + end + end + + def test_last_completion_result + id = "wf-#{SecureRandom.uuid}" + task_queue = "tq-#{SecureRandom.uuid}" + handle = env.client.create_schedule( + 'last-result-workflow', + Temporalio::Client::Schedule.new( + action: Temporalio::Client::Schedule::Action::StartWorkflow.new( + LastResultWorkflow, + id:, task_queue: + ), + spec: Temporalio::Client::Schedule::Spec.new + ) + ) + + schedule = ScheduleResult.new(env.client, handle) + + Temporalio::Worker.new(client: env.client, task_queue:, workflows: [LastResultWorkflow]).run do + handle.trigger + assert_equal 'First result', schedule.result + + handle.trigger + assert_equal 'The last result was First result', schedule.result + end + + handle.delete + end + + class HasLastResultWorkflow < Temporalio::Workflow::Definition + def execute # rubocop:disable Naming/PredicateMethod + Temporalio::Workflow.info.has_last_result? + end + end + + def test_has_last_completion_result + id = "wf-#{SecureRandom.uuid}" + task_queue = "tq-#{SecureRandom.uuid}" + handle = env.client.create_schedule( + 'has-last-result-workflow', + Temporalio::Client::Schedule.new( + action: Temporalio::Client::Schedule::Action::StartWorkflow.new( + HasLastResultWorkflow, + id:, task_queue: + ), + spec: Temporalio::Client::Schedule::Spec.new + ) + ) + + schedule = ScheduleResult.new(env.client, handle) + + Temporalio::Worker.new(client: env.client, task_queue:, workflows: [HasLastResultWorkflow]).run do + handle.trigger + assert_equal false, schedule.result + + handle.trigger + assert_equal true, schedule.result + end + + handle.delete + end end diff --git a/temporalio/test/worker_workflow_test.rb b/temporalio/test/worker_workflow_test.rb index e33e57f9..679a0342 100644 --- a/temporalio/test/worker_workflow_test.rb +++ b/temporalio/test/worker_workflow_test.rb @@ -206,6 +206,7 @@ def test_info assert_nil info.fetch('execution_timeout') assert_nil info.fetch('last_failure') assert_nil info.fetch('last_result') + assert_equal false, info.fetch('has_last_result?') assert_equal env.client.namespace, info['namespace'] assert_nil info.fetch('parent') assert_nil info.fetch('retry_policy') @@ -2653,6 +2654,35 @@ def test_non_durable_timer .map { |a| a.start_to_fire_timeout.to_f }) end end + + class LastFailureWorkflow < Temporalio::Workflow::Definition + workflow_query_attr_reader :failure + + def execute + info = Temporalio::Workflow.info + + @failure = info.last_failure + + return 'Done' if info.attempt != 1 + + raise Temporalio::Error::ApplicationError.new('Intentional failure', category: Temporalio::Error::ApplicationError::Category::BENIGN) + end + end + + def test_last_failure + execute_workflow(LastFailureWorkflow, retry_policy: + Temporalio::RetryPolicy.new( + initial_interval: 0, + max_attempts: 2 + )) do |handle| + result = handle.result + + assert_equal 'Done', result + + previous_failure = handle.query(:failure) + assert_equal 'Intentional failure', previous_failure + end + end end # TODO(cretz): To test