temporalio 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9ab77e9e3a462220ab99a1eb7814e4e7e6c7f9485487344cda2e12b8f983147
4
- data.tar.gz: d3d2f0a9026ec2033de2a367735d9ddffec50ced84c5bd99f049f481ea534b89
3
+ metadata.gz: 02f37fc5f0253de8502b8f11163f2df6038932aa103d1a6b4cde9e7b744f8db1
4
+ data.tar.gz: 9fa127403d3ffc13493c155493122eb7bd52eb83aae31f2ba8c32c47a05e5661
5
5
  SHA512:
6
- metadata.gz: 436ef0cdcbf32fe0c88c719a409dc1707e141df0e8bccb5f024c181f5c41ab7f03a2ccf83e445cf9c1b98e208ef618ad8dcf2715ce767d46b99e903e766eb9c5
7
- data.tar.gz: 780131e186208f905023064c38d6298d94f4218909e34ff381261167ea38e7d13754a157a33ff0db2b8de79551827d3dd449dabcd0db66bbfdfc16bcb5c8cd38
6
+ metadata.gz: 5c06d25a83a29b5a7f1a5d1aa3ae5e83bd7c35155b5d5700d561dd87026b51e020eb0b8db17354e740bba49aee5b36e88e17c40e03ecedfb0aba8435fb4dce33
7
+ data.tar.gz: 3b6a5ab5a22fc430f27cf664107a7b7b26f17fd59661c6768efb839f1988dda8999eac2ba40d2957894a89fd0a0ca0c482cc53274819df70128584ac145e3c87
data/Cargo.lock CHANGED
@@ -83,7 +83,7 @@ version = "1.1.5"
83
83
  source = "registry+https://github.com/rust-lang/crates.io-index"
84
84
  checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
85
85
  dependencies = [
86
- "windows-sys 0.61.2",
86
+ "windows-sys 0.60.2",
87
87
  ]
88
88
 
89
89
  [[package]]
@@ -94,7 +94,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
94
94
  dependencies = [
95
95
  "anstyle",
96
96
  "once_cell_polyfill",
97
- "windows-sys 0.61.2",
97
+ "windows-sys 0.60.2",
98
98
  ]
99
99
 
100
100
  [[package]]
@@ -711,7 +711,7 @@ dependencies = [
711
711
  "libc",
712
712
  "option-ext",
713
713
  "redox_users",
714
- "windows-sys 0.61.2",
714
+ "windows-sys 0.60.2",
715
715
  ]
716
716
 
717
717
  [[package]]
@@ -811,7 +811,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
811
811
  checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
812
812
  dependencies = [
813
813
  "libc",
814
- "windows-sys 0.61.2",
814
+ "windows-sys 0.60.2",
815
815
  ]
816
816
 
817
817
  [[package]]
@@ -1755,7 +1755,7 @@ version = "0.50.3"
1755
1755
  source = "registry+https://github.com/rust-lang/crates.io-index"
1756
1756
  checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
1757
1757
  dependencies = [
1758
- "windows-sys 0.61.2",
1758
+ "windows-sys 0.60.2",
1759
1759
  ]
1760
1760
 
1761
1761
  [[package]]
@@ -2721,7 +2721,7 @@ dependencies = [
2721
2721
  "errno",
2722
2722
  "libc",
2723
2723
  "linux-raw-sys",
2724
- "windows-sys 0.61.2",
2724
+ "windows-sys 0.60.2",
2725
2725
  ]
2726
2726
 
2727
2727
  [[package]]
@@ -2780,7 +2780,7 @@ dependencies = [
2780
2780
  "security-framework",
2781
2781
  "security-framework-sys",
2782
2782
  "webpki-root-certs",
2783
- "windows-sys 0.61.2",
2783
+ "windows-sys 0.60.2",
2784
2784
  ]
2785
2785
 
2786
2786
  [[package]]
@@ -3007,7 +3007,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
3007
3007
  checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
3008
3008
  dependencies = [
3009
3009
  "libc",
3010
- "windows-sys 0.61.2",
3010
+ "windows-sys 0.60.2",
3011
3011
  ]
3012
3012
 
3013
3013
  [[package]]
@@ -3100,7 +3100,7 @@ dependencies = [
3100
3100
  "getrandom 0.4.2",
3101
3101
  "once_cell",
3102
3102
  "rustix",
3103
- "windows-sys 0.61.2",
3103
+ "windows-sys 0.60.2",
3104
3104
  ]
3105
3105
 
3106
3106
  [[package]]
@@ -3534,9 +3534,9 @@ checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d"
3534
3534
 
3535
3535
  [[package]]
3536
3536
  name = "tonic"
3537
- version = "0.14.5"
3537
+ version = "0.14.6"
3538
3538
  source = "registry+https://github.com/rust-lang/crates.io-index"
3539
- checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec"
3539
+ checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
3540
3540
  dependencies = [
3541
3541
  "async-trait",
3542
3542
  "axum",
@@ -4033,7 +4033,7 @@ version = "0.1.11"
4033
4033
  source = "registry+https://github.com/rust-lang/crates.io-index"
4034
4034
  checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
4035
4035
  dependencies = [
4036
- "windows-sys 0.61.2",
4036
+ "windows-sys 0.60.2",
4037
4037
  ]
4038
4038
 
4039
4039
  [[package]]
data/README.md CHANGED
@@ -1092,6 +1092,63 @@ it will raise the error raised in the activity.
1092
1092
  The constructor of the environment has multiple keyword arguments that can be set to affect the activity context for the
1093
1093
  activity.
1094
1094
 
1095
+ #### Standalone Activities
1096
+
1097
+ Activities can be started directly from a client, outside the context of any workflow. Standalone activities reuse the
1098
+ existing `Activity::Definition` class — the same activity code runs whether invoked from a workflow or as a standalone
1099
+ activity. They are addressed by `activity_id` (with an optional `activity_run_id` for disambiguating re-runs).
1100
+
1101
+ Start a standalone activity:
1102
+
1103
+ ```ruby
1104
+ handle = client.start_activity(
1105
+ MyActivity,
1106
+ 'some-arg',
1107
+ id: 'my-activity-id',
1108
+ task_queue: 'my-task-queue',
1109
+ start_to_close_timeout: 60
1110
+ )
1111
+ result = handle.result # blocks until the activity completes
1112
+ ```
1113
+
1114
+ Or use the execute helper to start and wait:
1115
+
1116
+ ```ruby
1117
+ result = client.execute_activity(
1118
+ MyActivity, 'some-arg',
1119
+ id: 'my-activity-id', task_queue: 'my-task-queue', start_to_close_timeout: 60
1120
+ )
1121
+ ```
1122
+
1123
+ Get a handle to an existing standalone activity to describe, cancel, terminate, or fetch its result:
1124
+
1125
+ ```ruby
1126
+ handle = client.activity_handle('my-activity-id')
1127
+ description = handle.describe # ActivityExecution::Description
1128
+ result = handle.result # blocks until the activity reaches a terminal state
1129
+ handle.cancel('reason for cancel') # or
1130
+ handle.terminate('reason for terminate')
1131
+ ```
1132
+
1133
+ List and count standalone activities (visibility queries):
1134
+
1135
+ ```ruby
1136
+ client.list_activities('ActivityType="MyActivity"').each { |exec| puts exec.activity_id }
1137
+ count = client.count_activities('ActivityType="MyActivity"').count
1138
+ ```
1139
+
1140
+ Inside an activity body, `Temporalio::Activity::Context.current.info` exposes whether the activity is standalone
1141
+ or workflow-scheduled:
1142
+
1143
+ ```ruby
1144
+ info = Temporalio::Activity::Context.current.info
1145
+ if info.in_workflow?
1146
+ # info.workflow_id, info.workflow_run_id, info.workflow_type are set
1147
+ else
1148
+ # info.activity_run_id is set; workflow_* fields are nil
1149
+ end
1150
+ ```
1151
+
1095
1152
  ### Telemetry
1096
1153
 
1097
1154
  #### Metrics
@@ -155,6 +155,28 @@ module Temporalio
155
155
  # @return [Object, nil] Result hint
156
156
  attr_reader :result_hint
157
157
 
158
+ # Resolve an activity argument into a `[name, arg_hints, result_hint]` triple. Used by
159
+ # `Client#start_activity` to accept any of: a `Definition` subclass, an instance of one,
160
+ # an `Info`, a `Symbol` activity name, or a `String` activity name. Class/instance/Info
161
+ # inputs carry their definition's hints; Symbol/String inputs return `nil` hints.
162
+ #
163
+ # @param activity [Class<Definition>, Definition, Info, Symbol, String] Activity argument.
164
+ # @return [Array(String, Array[Object]?, Object?)] name, arg_hints, result_hint.
165
+ def self._type_and_hints_from_parameter(activity)
166
+ case activity
167
+ when String, Symbol
168
+ [activity.to_s, nil, nil]
169
+ when Class, Definition, Info
170
+ # Return or construct an Info -- needed because we want the checks in Info.initialize.
171
+ info = from_activity(activity)
172
+ raise ArgumentError, 'Cannot pass dynamic activity to start_activity' unless info.name
173
+
174
+ [info.name.to_s, info.arg_hints, info.result_hint]
175
+ else
176
+ raise ArgumentError, "#{activity} is not an activity class, instance, info, symbol, or string"
177
+ end
178
+ end
179
+
158
180
  # Obtain definition info representing the given activity, which can be a class, instance, or definition info.
159
181
  #
160
182
  # @param activity [Definition, Class<Definition>, Info] Activity to get info for.
@@ -7,11 +7,13 @@ module Temporalio
7
7
  module Activity
8
8
  Info = Data.define(
9
9
  :activity_id,
10
+ :activity_run_id,
10
11
  :activity_type,
11
12
  :attempt,
12
13
  :current_attempt_scheduled_time,
13
14
  :heartbeat_timeout,
14
15
  :local?,
16
+ :namespace,
15
17
  :priority,
16
18
  :retry_policy,
17
19
  :raw_heartbeat_details,
@@ -31,16 +33,21 @@ module Temporalio
31
33
  #
32
34
  # @!attribute activity_id
33
35
  # @return [String] ID for the activity.
36
+ # @!attribute activity_run_id
37
+ # @return [String, nil] Run ID for a standalone activity execution. nil for activities scheduled from a workflow.
38
+ # WARNING: Standalone Activities are experimental.
34
39
  # @!attribute activity_type
35
40
  # @return [String] Type name for the activity.
36
41
  # @!attribute attempt
37
- # @return [Integer] Attempt the activity is on.
42
+ # @return [Integer] Attempt the activity is on. Attempts start at 1 and increment on each retry.
38
43
  # @!attribute current_attempt_scheduled_time
39
44
  # @return [Time] When the current attempt was scheduled.
40
45
  # @!attribute heartbeat_timeout
41
46
  # @return [Float, nil] Heartbeat timeout set by the caller.
42
47
  # @!attribute local?
43
48
  # @return [Boolean] Whether the activity is a local activity or not.
49
+ # @!attribute namespace
50
+ # @return [String] Namespace this activity is on.
44
51
  # @!attribute priority
45
52
  # @return [Priority] The priority of this activity.
46
53
  # @!attribute retry_policy
@@ -64,17 +71,26 @@ module Temporalio
64
71
  # @return [String] Task token uniquely identifying this activity. Note, this is a `ASCII-8BIT` encoded string, not
65
72
  # a `UTF-8` encoded string nor a valid UTF-8 string.
66
73
  # @!attribute workflow_id
67
- # @return [String] Workflow ID that started this activity.
74
+ # @return [String, nil] Workflow ID that started this activity. nil for standalone activities.
68
75
  # @!attribute workflow_namespace
69
- # @return [String] Namespace this activity is on.
76
+ # @return [String, nil] Namespace of the workflow that scheduled this activity. Nil for standalone
77
+ # activities. Prefer {#namespace}, which is always set.
78
+ # @deprecated Use {#namespace} instead.
70
79
  # @!attribute workflow_run_id
71
- # @return [String] Workflow run ID that started this activity.
80
+ # @return [String, nil] Workflow run ID that started this activity. nil for standalone activities.
72
81
  # @!attribute workflow_type
73
- # @return [String] Workflow type name that started this activity.
82
+ # @return [String, nil] Workflow type name that started this activity. nil for standalone activities.
74
83
  #
75
84
  # @note WARNING: This class may have required parameters added to its constructor. Users should not instantiate this
76
85
  # class or it may break in incompatible ways.
77
86
  class Info
87
+ # @return [Boolean] True if this activity was scheduled by a workflow execution; false for standalone activities.
88
+ #
89
+ # WARNING: Standalone Activities are experimental.
90
+ def in_workflow?
91
+ !workflow_id.nil?
92
+ end
93
+
78
94
  # Convert raw heartbeat details into Ruby types.
79
95
  #
80
96
  # Note, this live-converts every invocation.
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+ require 'temporalio/client/activity_execution_status'
5
+ require 'temporalio/client/pending_activity_state'
6
+ require 'temporalio/internal/proto_utils'
7
+ require 'temporalio/priority'
8
+ require 'temporalio/retry_policy'
9
+ require 'temporalio/search_attributes'
10
+ require 'temporalio/worker_deployment_version'
11
+
12
+ module Temporalio
13
+ class Client
14
+ # Info for a standalone activity execution. Returned by list_activities; extended by {Description}
15
+ # for describe results.
16
+ #
17
+ # WARNING: Standalone Activities are experimental.
18
+ class ActivityExecution
19
+ # @return [Api::Activity::V1::ActivityExecutionListInfo, Api::Activity::V1::ActivityExecutionInfo]
20
+ # Underlying protobuf info.
21
+ attr_reader :raw_info
22
+
23
+ # @!visibility private
24
+ def initialize(raw_info)
25
+ @raw_info = raw_info
26
+ @search_attributes = Internal::ProtoUtils::LazySearchAttributes.new(raw_info.search_attributes)
27
+ end
28
+
29
+ # @return [String] ID for the activity.
30
+ def activity_id
31
+ @raw_info.activity_id
32
+ end
33
+
34
+ # @return [String] Run ID for this activity execution attempt.
35
+ def activity_run_id
36
+ Internal::ProtoUtils.string_or(@raw_info.run_id, nil)
37
+ end
38
+
39
+ # @return [String] Type name of the activity.
40
+ def activity_type
41
+ @raw_info.activity_type&.name
42
+ end
43
+
44
+ # @return [Time, nil] When the activity was scheduled.
45
+ def schedule_time
46
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.schedule_time)
47
+ end
48
+
49
+ # @return [Time, nil] When the activity reached a terminal state.
50
+ def close_time
51
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.close_time)
52
+ end
53
+
54
+ # @return [ActivityExecutionStatus] Overall status for the activity.
55
+ def status
56
+ Internal::ProtoUtils.enum_to_int(Api::Enums::V1::ActivityExecutionStatus, @raw_info.status)
57
+ end
58
+
59
+ # @return [SearchAttributes, nil] Search attributes attached to this activity if any.
60
+ def search_attributes
61
+ @search_attributes.get
62
+ end
63
+
64
+ # @return [String] Task queue for the activity.
65
+ def task_queue
66
+ @raw_info.task_queue
67
+ end
68
+
69
+ # @return [Float, nil] How long this activity has been running across all attempts, in seconds.
70
+ def execution_duration
71
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.execution_duration)
72
+ end
73
+
74
+ # Rich description of a standalone activity execution; returned by {ActivityHandle#describe}.
75
+ #
76
+ # WARNING: Standalone Activities are experimental.
77
+ class Description < ActivityExecution
78
+ # @return [Api::WorkflowService::V1::DescribeActivityExecutionResponse] Underlying protobuf response.
79
+ attr_reader :raw_description
80
+
81
+ # @!visibility private
82
+ def initialize(raw_description, data_converter)
83
+ super(raw_description.info)
84
+ @raw_description = raw_description
85
+ @data_converter = data_converter
86
+ end
87
+
88
+ # @return [PendingActivityState, nil] More detailed breakdown of the running state when
89
+ # the activity's status is RUNNING; nil otherwise.
90
+ def run_state
91
+ Internal::ProtoUtils.enum_to_int(Api::Enums::V1::PendingActivityState, @raw_info.run_state,
92
+ zero_means_nil: true)
93
+ end
94
+
95
+ # @return [Float, nil] Schedule-to-close timeout in seconds.
96
+ def schedule_to_close_timeout
97
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.schedule_to_close_timeout)
98
+ end
99
+
100
+ # @return [Float, nil] Schedule-to-start timeout in seconds.
101
+ def schedule_to_start_timeout
102
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.schedule_to_start_timeout)
103
+ end
104
+
105
+ # @return [Float, nil] Start-to-close timeout in seconds.
106
+ def start_to_close_timeout
107
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.start_to_close_timeout)
108
+ end
109
+
110
+ # @return [Float, nil] Heartbeat timeout in seconds.
111
+ def heartbeat_timeout
112
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.heartbeat_timeout)
113
+ end
114
+
115
+ # @return [Boolean] Whether the activity has recorded any heartbeat details.
116
+ def has_heartbeat_details? # rubocop:disable Naming/PredicatePrefix
117
+ !@raw_info.heartbeat_details&.payloads.nil? && !@raw_info.heartbeat_details.payloads.empty?
118
+ end
119
+
120
+ # Deserialized last-heartbeat details. Empty when no heartbeat has been recorded.
121
+ #
122
+ # @param hints [Array<Object>, nil] Hints, if any, to assist conversion.
123
+ # @return [Array<Object>] Converted details.
124
+ def heartbeat_details(hints: nil)
125
+ @data_converter.from_payloads(@raw_info.heartbeat_details, hints:)
126
+ end
127
+
128
+ # @return [RetryPolicy] Retry policy in effect for this activity.
129
+ def retry_policy
130
+ RetryPolicy._from_proto(@raw_info.retry_policy)
131
+ end
132
+
133
+ # @return [Time, nil] Time the last heartbeat was recorded.
134
+ def last_heartbeat_time
135
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.last_heartbeat_time)
136
+ end
137
+
138
+ # @return [Time, nil] Time the last attempt started.
139
+ def last_started_time
140
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.last_started_time)
141
+ end
142
+
143
+ # @return [Integer] Current attempt number. Attempts start at 1 and increment on each retry.
144
+ def attempt
145
+ @raw_info.attempt
146
+ end
147
+
148
+ # @return [Error::Failure, nil] Failure of the last failed attempt if any.
149
+ def last_failure
150
+ return nil unless @raw_info.last_failure
151
+
152
+ @data_converter.from_failure(@raw_info.last_failure)
153
+ end
154
+
155
+ # @return [Time, nil] Schedule time + schedule-to-close timeout.
156
+ def expiration_time
157
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.expiration_time)
158
+ end
159
+
160
+ # @return [String, nil] Identity of the worker that last picked up this activity.
161
+ def last_worker_identity
162
+ Internal::ProtoUtils.string_or(@raw_info.last_worker_identity, nil)
163
+ end
164
+
165
+ # @return [Float, nil] Time from last attempt failure to next retry, in seconds.
166
+ def current_retry_interval
167
+ Internal::ProtoUtils.duration_to_seconds(@raw_info.current_retry_interval)
168
+ end
169
+
170
+ # @return [Time, nil] Time when the last attempt completed.
171
+ def last_attempt_complete_time
172
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.last_attempt_complete_time)
173
+ end
174
+
175
+ # @return [Time, nil] Time when the next attempt will be scheduled.
176
+ def next_attempt_schedule_time
177
+ Internal::ProtoUtils.timestamp_to_time(@raw_info.next_attempt_schedule_time)
178
+ end
179
+
180
+ # @return [WorkerDeploymentVersion, nil] Worker deployment version this activity was last dispatched to.
181
+ def last_deployment_version
182
+ raw = @raw_info.last_deployment_version
183
+ return nil unless raw
184
+
185
+ WorkerDeploymentVersion.new(
186
+ deployment_name: raw.deployment_name,
187
+ build_id: raw.build_id
188
+ )
189
+ end
190
+
191
+ # @return [Priority] Priority of this activity.
192
+ def priority
193
+ Priority._from_proto(@raw_info.priority)
194
+ end
195
+
196
+ # @return [String, nil] Reason given when cancellation was requested.
197
+ def canceled_reason
198
+ Internal::ProtoUtils.string_or(@raw_info.canceled_reason, nil)
199
+ end
200
+
201
+ # @return [String, nil] Static user-metadata summary on the activity.
202
+ def static_summary
203
+ user_metadata.first
204
+ end
205
+
206
+ # @return [String, nil] Static user-metadata details on the activity. May be in markdown format.
207
+ def static_details
208
+ user_metadata.last
209
+ end
210
+
211
+ private
212
+
213
+ def user_metadata
214
+ @user_metadata ||= Internal::ProtoUtils.from_user_metadata(
215
+ @raw_info.user_metadata, @data_converter
216
+ )
217
+ end
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ class Client
5
+ # Result of a {Client#count_activities} call.
6
+ #
7
+ # WARNING: Standalone Activities are experimental.
8
+ class ActivityExecutionCount
9
+ # @return [Integer] Approximate number of activities matching the query. If the query had a group-by clause,
10
+ # this is the sum of all the counts in {groups}.
11
+ attr_reader :count
12
+
13
+ # @return [Array<AggregationGroup>] Groups if the query had a group-by clause, or empty if not.
14
+ attr_reader :groups
15
+
16
+ # @!visibility private
17
+ def initialize(count, groups)
18
+ @count = count
19
+ @groups = groups
20
+ end
21
+
22
+ # Aggregation group if the activity count query had a group-by clause.
23
+ #
24
+ # WARNING: Standalone Activities are experimental.
25
+ class AggregationGroup
26
+ # @return [Integer] Approximate number of activities matching the query for this group.
27
+ attr_reader :count
28
+
29
+ # @return [Array<Object>] Search attribute values for this group.
30
+ attr_reader :group_values
31
+
32
+ # @!visibility private
33
+ def initialize(count, group_values)
34
+ @count = count
35
+ @group_values = group_values
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ class Client
7
+ # Status of a standalone activity execution.
8
+ #
9
+ # WARNING: Standalone Activities are experimental.
10
+ module ActivityExecutionStatus
11
+ UNSPECIFIED = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_UNSPECIFIED
12
+ RUNNING = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_RUNNING
13
+ COMPLETED = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_COMPLETED
14
+ FAILED = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_FAILED
15
+ CANCELED = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_CANCELED
16
+ TERMINATED = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_TERMINATED
17
+ TIMED_OUT = Api::Enums::V1::ActivityExecutionStatus::ACTIVITY_EXECUTION_STATUS_TIMED_OUT
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+ require 'temporalio/client/activity_execution'
5
+ require 'temporalio/client/interceptor'
6
+ require 'temporalio/error'
7
+
8
+ module Temporalio
9
+ class Client
10
+ # Handle for interacting with a standalone activity. Usually created via {Client.activity_handle}
11
+ # or {Client#start_activity}.
12
+ #
13
+ # WARNING: Standalone Activities are experimental.
14
+ class ActivityHandle
15
+ # @return [String] ID for the activity.
16
+ attr_reader :id
17
+
18
+ # @return [String, nil] Run ID for this activity execution. When nil, this handle targets the latest run.
19
+ attr_reader :run_id
20
+
21
+ # @return [Object, nil] Result hint used when deserializing the activity's result. May be overridden per
22
+ # {#result} call.
23
+ attr_reader :result_hint
24
+
25
+ # @!visibility private
26
+ def initialize(client:, id:, run_id:, result_hint:)
27
+ @client = client
28
+ @id = id
29
+ @run_id = run_id
30
+ @result_hint = result_hint
31
+ end
32
+
33
+ # Wait for the activity's outcome (result or failure). Internally long-polls
34
+ # PollActivityExecution and reissues until the activity reaches a terminal state, so this can
35
+ # block indefinitely for long-running activities.
36
+ #
37
+ # @param result_hint [Object, nil] Override the result hint. If nil, uses {#result_hint}.
38
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
39
+ #
40
+ # @return [Object, nil] Deserialized activity result.
41
+ #
42
+ # @raise [Error::ActivityFailedError] With `cause` populated from the activity failure.
43
+ # @raise [Error::RPCError] RPC error from call.
44
+ def result(result_hint: nil, rpc_options: nil)
45
+ hint = result_hint || @result_hint
46
+ outcome = @client._impl.fetch_activity_outcome(
47
+ Interceptor::FetchActivityOutcomeInput.new(
48
+ activity_id: id,
49
+ activity_run_id: run_id,
50
+ rpc_options:
51
+ )
52
+ )
53
+ _process_outcome(outcome, hint)
54
+ end
55
+
56
+ # Describe the activity.
57
+ #
58
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
59
+ #
60
+ # @return [ActivityExecution::Description] Activity description.
61
+ # @raise [Error::RPCError] RPC error from call.
62
+ def describe(rpc_options: nil)
63
+ @client._impl.describe_activity(
64
+ Interceptor::DescribeActivityInput.new(
65
+ activity_id: id,
66
+ activity_run_id: run_id,
67
+ rpc_options:
68
+ )
69
+ )
70
+ end
71
+
72
+ # Request cancellation of the activity.
73
+ #
74
+ # @param reason [String, nil] Optional cancellation reason recorded on the server.
75
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
76
+ # @raise [Error::RPCError] RPC error from call.
77
+ def cancel(reason = nil, rpc_options: nil)
78
+ @client._impl.cancel_activity(
79
+ Interceptor::CancelActivityInput.new(
80
+ activity_id: id,
81
+ activity_run_id: run_id,
82
+ reason:,
83
+ rpc_options:
84
+ )
85
+ )
86
+ nil
87
+ end
88
+
89
+ # Terminate the activity (force-close).
90
+ #
91
+ # @param reason [String, nil] Optional termination reason recorded on the activity's failure outcome.
92
+ # @param rpc_options [RPCOptions, nil] Advanced RPC options.
93
+ # @raise [Error::RPCError] RPC error from call.
94
+ def terminate(reason = nil, rpc_options: nil)
95
+ @client._impl.terminate_activity(
96
+ Interceptor::TerminateActivityInput.new(
97
+ activity_id: id,
98
+ activity_run_id: run_id,
99
+ reason:,
100
+ rpc_options:
101
+ )
102
+ )
103
+ nil
104
+ end
105
+
106
+ private
107
+
108
+ def _process_outcome(outcome, hint)
109
+ raise Error, 'Activity completed but outcome is missing from server response' if outcome.nil?
110
+
111
+ case outcome.value
112
+ when :failure
113
+ cause = @client.data_converter.from_failure(outcome.failure)
114
+ raise Error::ActivityFailedError.new, cause: cause
115
+ when :result
116
+ @client.data_converter.from_payloads(outcome.result, hints: Array(hint)).first
117
+ else
118
+ raise Error, "Unknown activity outcome: #{outcome.value.inspect}"
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end