temporalio 0.4.0-aarch64-linux-musl

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.
Files changed (183) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +27 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +123 -0
  7. data/lib/temporalio/activity/definition.rb +192 -0
  8. data/lib/temporalio/activity/info.rb +67 -0
  9. data/lib/temporalio/activity.rb +12 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/batch/v1/message.rb +36 -0
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  15. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  16. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  17. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  18. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  19. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  20. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  22. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  23. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  24. data/lib/temporalio/api/command/v1/message.rb +46 -0
  25. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  26. data/lib/temporalio/api/common/v1/message.rb +48 -0
  27. data/lib/temporalio/api/deployment/v1/message.rb +38 -0
  28. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  29. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  30. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  31. data/lib/temporalio/api/enums/v1/deployment.rb +23 -0
  32. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  33. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  34. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/nexus.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  37. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  38. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  39. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  40. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  41. data/lib/temporalio/api/enums/v1/workflow.rb +31 -0
  42. data/lib/temporalio/api/errordetails/v1/message.rb +44 -0
  43. data/lib/temporalio/api/export/v1/message.rb +24 -0
  44. data/lib/temporalio/api/failure/v1/message.rb +37 -0
  45. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  46. data/lib/temporalio/api/history/v1/message.rb +92 -0
  47. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  48. data/lib/temporalio/api/nexus/v1/message.rb +41 -0
  49. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  50. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  51. data/lib/temporalio/api/operatorservice.rb +3 -0
  52. data/lib/temporalio/api/payload_visitor.rb +1581 -0
  53. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  54. data/lib/temporalio/api/query/v1/message.rb +28 -0
  55. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  56. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  57. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  58. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  59. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  60. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  61. data/lib/temporalio/api/taskqueue/v1/message.rb +48 -0
  62. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  63. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  64. data/lib/temporalio/api/update/v1/message.rb +33 -0
  65. data/lib/temporalio/api/version/v1/message.rb +26 -0
  66. data/lib/temporalio/api/workflow/v1/message.rb +51 -0
  67. data/lib/temporalio/api/workflowservice/v1/request_response.rb +233 -0
  68. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  69. data/lib/temporalio/api/workflowservice.rb +3 -0
  70. data/lib/temporalio/api.rb +15 -0
  71. data/lib/temporalio/cancellation.rb +170 -0
  72. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  73. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  74. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  75. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  76. data/lib/temporalio/client/connection/service.rb +42 -0
  77. data/lib/temporalio/client/connection/test_service.rb +111 -0
  78. data/lib/temporalio/client/connection/workflow_service.rb +1251 -0
  79. data/lib/temporalio/client/connection.rb +316 -0
  80. data/lib/temporalio/client/interceptor.rb +455 -0
  81. data/lib/temporalio/client/schedule.rb +991 -0
  82. data/lib/temporalio/client/schedule_handle.rb +126 -0
  83. data/lib/temporalio/client/with_start_workflow_operation.rb +115 -0
  84. data/lib/temporalio/client/workflow_execution.rb +119 -0
  85. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  86. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  87. data/lib/temporalio/client/workflow_handle.rb +389 -0
  88. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  89. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  90. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  91. data/lib/temporalio/client.rb +607 -0
  92. data/lib/temporalio/common_enums.rb +41 -0
  93. data/lib/temporalio/contrib/open_telemetry.rb +470 -0
  94. data/lib/temporalio/converters/data_converter.rb +99 -0
  95. data/lib/temporalio/converters/failure_converter.rb +202 -0
  96. data/lib/temporalio/converters/payload_codec.rb +26 -0
  97. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  98. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  99. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  100. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  101. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  102. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  103. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  104. data/lib/temporalio/converters/payload_converter.rb +71 -0
  105. data/lib/temporalio/converters/raw_value.rb +20 -0
  106. data/lib/temporalio/converters.rb +9 -0
  107. data/lib/temporalio/error/failure.rb +219 -0
  108. data/lib/temporalio/error.rb +156 -0
  109. data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
  110. data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -0
  111. data/lib/temporalio/internal/bridge/3.4/temporalio_bridge.so +0 -0
  112. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  113. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  114. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  115. data/lib/temporalio/internal/bridge/api/common/common.rb +27 -0
  116. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  117. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  118. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  119. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  120. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  121. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +31 -0
  122. data/lib/temporalio/internal/bridge/api.rb +3 -0
  123. data/lib/temporalio/internal/bridge/client.rb +95 -0
  124. data/lib/temporalio/internal/bridge/runtime.rb +56 -0
  125. data/lib/temporalio/internal/bridge/testing.rb +69 -0
  126. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  127. data/lib/temporalio/internal/bridge.rb +36 -0
  128. data/lib/temporalio/internal/client/implementation.rb +922 -0
  129. data/lib/temporalio/internal/metric.rb +122 -0
  130. data/lib/temporalio/internal/proto_utils.rb +165 -0
  131. data/lib/temporalio/internal/worker/activity_worker.rb +385 -0
  132. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/context.rb +383 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/details.rb +46 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  139. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  140. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  141. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  142. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +400 -0
  143. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  144. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  145. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +183 -0
  146. data/lib/temporalio/internal/worker/workflow_instance.rb +774 -0
  147. data/lib/temporalio/internal/worker/workflow_worker.rb +239 -0
  148. data/lib/temporalio/internal.rb +7 -0
  149. data/lib/temporalio/metric.rb +109 -0
  150. data/lib/temporalio/retry_policy.rb +74 -0
  151. data/lib/temporalio/runtime/metric_buffer.rb +94 -0
  152. data/lib/temporalio/runtime.rb +352 -0
  153. data/lib/temporalio/scoped_logger.rb +96 -0
  154. data/lib/temporalio/search_attributes.rb +356 -0
  155. data/lib/temporalio/testing/activity_environment.rb +160 -0
  156. data/lib/temporalio/testing/workflow_environment.rb +406 -0
  157. data/lib/temporalio/testing.rb +10 -0
  158. data/lib/temporalio/version.rb +5 -0
  159. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  160. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  161. data/lib/temporalio/worker/activity_executor.rb +55 -0
  162. data/lib/temporalio/worker/interceptor.rb +365 -0
  163. data/lib/temporalio/worker/thread_pool.rb +237 -0
  164. data/lib/temporalio/worker/tuner.rb +189 -0
  165. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +235 -0
  166. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  167. data/lib/temporalio/worker/workflow_replayer.rb +350 -0
  168. data/lib/temporalio/worker.rb +603 -0
  169. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  170. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  171. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  172. data/lib/temporalio/workflow/definition.rb +598 -0
  173. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  174. data/lib/temporalio/workflow/future.rb +151 -0
  175. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  176. data/lib/temporalio/workflow/info.rb +104 -0
  177. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  178. data/lib/temporalio/workflow/update_info.rb +20 -0
  179. data/lib/temporalio/workflow.rb +575 -0
  180. data/lib/temporalio/workflow_history.rb +47 -0
  181. data/lib/temporalio.rb +11 -0
  182. data/temporalio.gemspec +29 -0
  183. metadata +258 -0
@@ -0,0 +1,356 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/api'
4
+
5
+ module Temporalio
6
+ # Collection of typed search attributes.
7
+ #
8
+ # This is represented as a mapping of {SearchAttributes::Key} to object values. This is not a hash though it does have
9
+ # a few hash-like methods and can be converted to a hash via {#to_h}. In some situations, such as in workflows, this
10
+ # class is immutable for outside use.
11
+ class SearchAttributes
12
+ # Key for a search attribute.
13
+ class Key
14
+ # @return [String] Name of the search attribute.
15
+ attr_reader :name
16
+
17
+ # @return [IndexedValueType] Type of the search attribute.
18
+ attr_reader :type
19
+
20
+ def initialize(name, type)
21
+ raise ArgumentError, 'Invalid type' unless Api::Enums::V1::IndexedValueType.lookup(type)
22
+
23
+ @name = name.to_s
24
+ @type = type
25
+ end
26
+
27
+ # @return [Boolean] Check equality.
28
+ def ==(other)
29
+ other.is_a?(Key) && other.name == @name && other.type == @type
30
+ end
31
+
32
+ alias eql? ==
33
+
34
+ # @return [Integer] Hash
35
+ def hash
36
+ [self.class, @name, @age].hash
37
+ end
38
+
39
+ # Validate that the given value matches the expected {#type}.
40
+ #
41
+ # @raise [TypeError] The value does not have the proper type.
42
+ def validate_value(value)
43
+ case type
44
+ when IndexedValueType::TEXT
45
+ raise TypeError, 'Value of TEXT key must be a String' unless value.is_a?(String)
46
+ when IndexedValueType::KEYWORD
47
+ raise TypeError, 'Value of KEYWORD key must be a String' unless value.is_a?(String)
48
+ when IndexedValueType::INTEGER
49
+ raise TypeError, 'Value of INTEGER key must be a Integer' unless value.is_a?(Integer)
50
+ when IndexedValueType::FLOAT
51
+ unless value.is_a?(Float) || value.is_a?(Integer)
52
+ raise TypeError, 'Value of FLOAT key must be a Float or Integer'
53
+ end
54
+ when IndexedValueType::BOOLEAN
55
+ unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
56
+ raise TypeError, 'Value of BOOLEAN key must be a Boolean'
57
+ end
58
+ when IndexedValueType::TIME
59
+ raise TypeError, 'Value of TIME key must be a Time' unless value.is_a?(Time)
60
+ when IndexedValueType::KEYWORD_LIST
61
+ unless value.is_a?(Array) && value.all? { |v| v.is_a?(String) }
62
+ raise TypeError, 'Value of KEYWORD_LIST key must be an Array of String'
63
+ end
64
+ else
65
+ # Should never happen, checked in constructor
66
+ raise 'Unrecognized key type'
67
+ end
68
+ end
69
+
70
+ # Create an updated that sets the given value for this key.
71
+ #
72
+ # @param value [Object] Value to update. Must be the proper type for the key.
73
+ # @return [Update] Created update.
74
+ def value_set(value)
75
+ raise ArgumentError, 'Value cannot be nil, use value_unset' if value.nil?
76
+
77
+ Update.new(self, value)
78
+ end
79
+
80
+ # Create an updated that unsets the key.
81
+ #
82
+ # @return [Update] Created update.
83
+ def value_unset
84
+ Update.new(self, nil)
85
+ end
86
+ end
87
+
88
+ # Search attribute update that can be separately applied.
89
+ class Update
90
+ # @return [Key] Key this update applies to.
91
+ attr_reader :key
92
+
93
+ # @return [Object, nil] Value to update or `nil` to remove the key.
94
+ attr_reader :value
95
+
96
+ # Create an update. Users may find it easier to use {Key#value_set} and {Key#value_unset} instead.
97
+ #
98
+ # @param key [Key] Key to update.
99
+ # @param value [Object, nil] Value to update to or nil to remove the value.
100
+ def initialize(key, value)
101
+ raise ArgumentError, 'Key must be a key' unless key.is_a?(Key)
102
+
103
+ key.validate_value(value) unless value.nil?
104
+ @key = key
105
+ @value = value
106
+ end
107
+
108
+ # @!visibility private
109
+ def _to_proto_pair
110
+ SearchAttributes._to_proto_pair(key, value)
111
+ end
112
+ end
113
+
114
+ # @!visibility private
115
+ def self._from_proto(proto, disable_mutations: false, never_nil: false)
116
+ return nil unless proto || never_nil
117
+
118
+ attrs = if proto
119
+ unless proto.is_a?(Api::Common::V1::SearchAttributes)
120
+ raise ArgumentError, 'Expected proto search attribute'
121
+ end
122
+
123
+ SearchAttributes.new(proto.indexed_fields.map do |key_name, payload| # rubocop:disable Style/MapToHash
124
+ key = Key.new(key_name, IndexedValueType::PROTO_VALUES[payload.metadata['type']])
125
+ value = _value_from_payload(payload)
126
+ [key, value]
127
+ end.to_h)
128
+ else
129
+ SearchAttributes.new
130
+ end
131
+ attrs._disable_mutations = disable_mutations
132
+ attrs
133
+ end
134
+
135
+ # @!visibility private
136
+ def self._value_from_payload(payload)
137
+ value = Converters::PayloadConverter.default.from_payload(payload)
138
+ # Time needs to be converted
139
+ value = Time.iso8601(value) if payload.metadata['type'] == 'DateTime' && value.is_a?(String)
140
+ value
141
+ end
142
+
143
+ # @!visibility private
144
+ def self._to_proto_pair(key, value)
145
+ # We use a default converter, but if type is a time, we need ISO format
146
+ value = value.iso8601 if key.type == IndexedValueType::TIME && value.is_a?(Time)
147
+
148
+ # Convert to payload
149
+ payload = Converters::PayloadConverter.default.to_payload(value)
150
+ payload.metadata['type'] = IndexedValueType::PROTO_NAMES[key.type]
151
+
152
+ [key.name, payload]
153
+ end
154
+
155
+ # Create a search attribute collection.
156
+ #
157
+ # @param existing [SearchAttributes, Hash<Key, Object>, nil] Existing collection. This can be another
158
+ # {SearchAttributes} instance or a {::Hash}.
159
+ def initialize(existing = nil)
160
+ if existing.nil?
161
+ @raw_hash = {}
162
+ elsif existing.is_a?(SearchAttributes)
163
+ @raw_hash = existing.to_h
164
+ elsif existing.is_a?(Hash)
165
+ @raw_hash = {}
166
+ existing.each { |key, value| self[key] = value }
167
+ else
168
+ raise ArgumentError, 'Existing must be nil, a SearchAttributes instance, or a valid Hash'
169
+ end
170
+ end
171
+
172
+ # Set a search attribute value for a key. This will replace any existing value for the {Key#name }regardless of
173
+ # {Key#type}.
174
+ #
175
+ # @param key [Key] A key to set. This must be a {Key} and the value must be proper for the {Key#type}.
176
+ # @param value [Object, nil] The value to set. If `nil`, the key is removed. The value must be proper for the `key`.
177
+ def []=(key, value)
178
+ _assert_mutations_enabled
179
+ # Key must be a Key
180
+ raise ArgumentError, 'Key must be a key' unless key.is_a?(Key)
181
+
182
+ key.validate_value(value) unless value.nil?
183
+
184
+ # Remove any key with the same name and set
185
+ delete(key)
186
+ # We only set the value if it's non-nil, otherwise it's a delete
187
+ @raw_hash[key] = value unless value.nil?
188
+ end
189
+
190
+ # Get a search attribute value for a key.
191
+ #
192
+ # @param key [Key, String, Symbol] The key to find. If this is a {Key}, it will use key equality (i.e. name and
193
+ # type) to search. If this is a {::String}, the type is not checked when finding the proper key.
194
+ # @return [Object, nil] Value if found or `nil` if not.
195
+ def [](key)
196
+ # Key must be a Key or a string
197
+ case key
198
+ when Key
199
+ @raw_hash[key]
200
+ when String, Symbol
201
+ @raw_hash.find { |hash_key, _| hash_key.name == key.to_s }&.last
202
+ else
203
+ raise ArgumentError, 'Key must be a key or string/symbol'
204
+ end
205
+ end
206
+
207
+ # Delete a search attribute key
208
+ #
209
+ # @param key [Key, String, Symbol] The key to delete. Regardless of whether this is a {Key} or a {::String}, the key
210
+ # with the matching name will be deleted. This means a {Key} with a matching name but different type may be
211
+ # deleted.
212
+ def delete(key)
213
+ _assert_mutations_enabled
214
+ # Key must be a Key or a string, but we delete all values for the
215
+ # name no matter what
216
+ name = case key
217
+ when Key
218
+ key.name
219
+ when String, Symbol
220
+ key.to_s
221
+ else
222
+ raise ArgumentError, 'Key must be a key or string/symbol'
223
+ end
224
+ @raw_hash.delete_if { |hash_key, _| hash_key.name == name }
225
+ end
226
+
227
+ # Like {::Hash#each}.
228
+ def each(&)
229
+ @raw_hash.each(&)
230
+ end
231
+
232
+ # @return [Hash<Key, Object>] Copy of the search attributes as a hash.
233
+ def to_h
234
+ @raw_hash.dup
235
+ end
236
+
237
+ # @return [SearchAttributes] Copy of the search attributes.
238
+ def dup
239
+ attrs = SearchAttributes.new(self)
240
+ attrs._disable_mutations = false
241
+ attrs
242
+ end
243
+
244
+ # @return [Boolean] Whether the set of attributes is empty.
245
+ def empty?
246
+ length.zero?
247
+ end
248
+
249
+ # @return [Integer] Number of attributes.
250
+ def length
251
+ @raw_hash.length
252
+ end
253
+
254
+ # Check equality.
255
+ #
256
+ # @param other [SearchAttributes] To compare.
257
+ # @return [Boolean] Whether equal.
258
+ def ==(other)
259
+ other.is_a?(SearchAttributes) && @raw_hash == other._raw_hash
260
+ end
261
+
262
+ alias size length
263
+
264
+ # Return a new search attributes collection with updates applied.
265
+ #
266
+ # @param updates [Update] Updates created via {Key#value_set} or {Key#value_unset}.
267
+ # @return [SearchAttributes] New collection.
268
+ def update(*updates)
269
+ _assert_mutations_enabled
270
+ attrs = dup
271
+ attrs.update!(*updates)
272
+ attrs
273
+ end
274
+
275
+ # Update this search attribute collection with given updates.
276
+ #
277
+ # @param updates [Update] Updates created via {Key#value_set} or {Key#value_unset}.
278
+ def update!(*updates)
279
+ _assert_mutations_enabled
280
+ updates.each do |update|
281
+ raise ArgumentError, 'Update must be an update' unless update.is_a?(Update)
282
+
283
+ if update.value.nil?
284
+ delete(update.key)
285
+ else
286
+ self[update.key] = update.value
287
+ end
288
+ end
289
+ end
290
+
291
+ # @!visibility private
292
+ def _raw_hash
293
+ @raw_hash
294
+ end
295
+
296
+ # @!visibility private
297
+ def _to_proto
298
+ Api::Common::V1::SearchAttributes.new(indexed_fields: _to_proto_hash)
299
+ end
300
+
301
+ # @!visibility private
302
+ def _to_proto_hash
303
+ @raw_hash.to_h { |key, value| SearchAttributes._to_proto_pair(key, value) }
304
+ end
305
+
306
+ # @!visibility private
307
+ def _assert_mutations_enabled
308
+ raise 'Search attribute mutations disabled' if @disable_mutations
309
+ end
310
+
311
+ # @!visibility private
312
+ def _disable_mutations=(value)
313
+ @disable_mutations = value
314
+ end
315
+
316
+ # Type for a search attribute key/value.
317
+ #
318
+ # @see https://docs.temporal.io/visibility#supported-types
319
+ module IndexedValueType
320
+ # Text type, values must be {::String}.
321
+ TEXT = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_TEXT
322
+
323
+ # Keyword type, values must be {::String}.
324
+ KEYWORD = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_KEYWORD
325
+
326
+ # Integer type, values must be {::Integer}.
327
+ INTEGER = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_INT
328
+
329
+ # Float type, values must be {::Float} or {::Integer}.
330
+ FLOAT = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_DOUBLE
331
+
332
+ # Boolean type, values must be {::TrueClass} or {::FalseClass}.
333
+ BOOLEAN = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_BOOL
334
+
335
+ # Time type, values must be {::Time}.
336
+ TIME = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_DATETIME
337
+
338
+ # Keyword list type, values must be {::Array<String>}.
339
+ KEYWORD_LIST = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_KEYWORD_LIST
340
+
341
+ # @!visibility private
342
+ PROTO_NAMES = {
343
+ TEXT => 'Text',
344
+ KEYWORD => 'Keyword',
345
+ INTEGER => 'Int',
346
+ FLOAT => 'Double',
347
+ BOOLEAN => 'Bool',
348
+ TIME => 'DateTime',
349
+ KEYWORD_LIST => 'KeywordList'
350
+ }.freeze
351
+
352
+ # @!visibility private
353
+ PROTO_VALUES = PROTO_NAMES.invert.freeze
354
+ end
355
+ end
356
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/activity'
4
+ require 'temporalio/cancellation'
5
+ require 'temporalio/converters/payload_converter'
6
+ require 'temporalio/worker/activity_executor'
7
+
8
+ module Temporalio
9
+ module Testing
10
+ # Test environment for testing activities.
11
+ #
12
+ # Users can create this environment and then use {run} to execute activities on it. Often, since mutable things like
13
+ # cancellation can be set, users create this for each activity that is run. There is no real performance penalty for
14
+ # creating an environment for every run.
15
+ class ActivityEnvironment
16
+ # @return [Activity::Info] The activity info used by default. This is frozen, but `with` can be used to make a new
17
+ # instance with changes to pass in to {initialize}.
18
+ def self.default_info
19
+ @default_info ||= Activity::Info.new(
20
+ activity_id: 'test',
21
+ activity_type: 'unknown',
22
+ attempt: 1,
23
+ current_attempt_scheduled_time: Time.at(0),
24
+ heartbeat_details: [],
25
+ heartbeat_timeout: nil,
26
+ local?: false,
27
+ schedule_to_close_timeout: 1.0,
28
+ scheduled_time: Time.at(0),
29
+ start_to_close_timeout: 1.0,
30
+ started_time: Time.at(0),
31
+ task_queue: 'test',
32
+ task_token: String.new('test', encoding: Encoding::ASCII_8BIT),
33
+ workflow_id: 'test',
34
+ workflow_namespace: 'default',
35
+ workflow_run_id: 'test-run',
36
+ workflow_type: 'test'
37
+ )
38
+ end
39
+
40
+ # Create a test environment for activities.
41
+ #
42
+ # @param info [Activity::Info] Value for {Activity::Context#info}. Users should not try to instantiate this
43
+ # themselves, but rather use `with` on {default_info}.
44
+ # @param on_heartbeat [Proc(Array), nil] Proc that is called with all heartbeat details when
45
+ # {Activity::Context#heartbeat} is called.
46
+ # @param cancellation [Cancellation] Value for {Activity::Context#cancellation}.
47
+ # @param worker_shutdown_cancellation [Cancellation] Value for {Activity::Context#worker_shutdown_cancellation}.
48
+ # @param payload_converter [Converters::PayloadConverter] Value for {Activity::Context#payload_converter}.
49
+ # @param logger [Logger] Value for {Activity::Context#logger}.
50
+ # @param activity_executors [Hash<Symbol, Worker::ActivityExecutor>] Executors that activities can run within.
51
+ # @param metric_meter [Metric::Meter, nil] Value for {Activity::Context#metric_meter}, or nil to raise when
52
+ # called.
53
+ # @param client [Client, nil] Value for {Activity::Context#client}, or nil to raise when called.
54
+ def initialize(
55
+ info: ActivityEnvironment.default_info,
56
+ on_heartbeat: nil,
57
+ cancellation: Cancellation.new,
58
+ worker_shutdown_cancellation: Cancellation.new,
59
+ payload_converter: Converters::PayloadConverter.default,
60
+ logger: Logger.new(nil),
61
+ activity_executors: Worker::ActivityExecutor.defaults,
62
+ metric_meter: nil,
63
+ client: nil
64
+ )
65
+ @info = info
66
+ @on_heartbeat = on_heartbeat
67
+ @cancellation = cancellation
68
+ @worker_shutdown_cancellation = worker_shutdown_cancellation
69
+ @payload_converter = payload_converter
70
+ @logger = logger
71
+ @activity_executors = activity_executors
72
+ @metric_meter = metric_meter
73
+ @client = client
74
+ end
75
+
76
+ # Run an activity and returns its result or raises its exception.
77
+ #
78
+ # @param activity [Activity::Definition, Class<Activity::Definition>, Activity::Definition::Info] Activity to run.
79
+ # @param args [Array<Object>] Arguments to the activity.
80
+ # @return Activity result.
81
+ def run(activity, *args)
82
+ defn = Activity::Definition::Info.from_activity(activity)
83
+ executor = @activity_executors[defn.executor]
84
+ raise ArgumentError, "Unknown executor: #{defn.executor}" if executor.nil?
85
+
86
+ queue = Queue.new
87
+ executor.execute_activity(defn) do
88
+ Activity::Context._current_executor = executor
89
+ executor.set_activity_context(defn, Context.new(
90
+ info: @info.dup,
91
+ instance:
92
+ defn.instance.is_a?(Proc) ? defn.instance.call : defn.instance,
93
+ on_heartbeat: @on_heartbeat,
94
+ cancellation: @cancellation,
95
+ worker_shutdown_cancellation: @worker_shutdown_cancellation,
96
+ payload_converter: @payload_converter,
97
+ logger: @logger,
98
+ metric_meter: @metric_meter,
99
+ client: @client
100
+ ))
101
+ queue.push([defn.proc.call(*args), nil])
102
+ rescue Exception => e # rubocop:disable Lint/RescueException -- Intentionally capturing all exceptions
103
+ queue.push([nil, e])
104
+ ensure
105
+ executor.set_activity_context(defn, nil)
106
+ Activity::Context._current_executor = nil
107
+ end
108
+
109
+ result, err = queue.pop
110
+ raise err unless err.nil?
111
+
112
+ result
113
+ end
114
+
115
+ # @!visibility private
116
+ class Context < Activity::Context
117
+ attr_reader :info, :instance, :cancellation, :worker_shutdown_cancellation, :payload_converter, :logger
118
+
119
+ def initialize( # rubocop:disable Lint/MissingSuper
120
+ info:,
121
+ instance:,
122
+ on_heartbeat:,
123
+ cancellation:,
124
+ worker_shutdown_cancellation:,
125
+ payload_converter:,
126
+ logger:,
127
+ metric_meter:,
128
+ client:
129
+ )
130
+ @info = info
131
+ @instance = instance
132
+ @on_heartbeat = on_heartbeat
133
+ @cancellation = cancellation
134
+ @worker_shutdown_cancellation = worker_shutdown_cancellation
135
+ @payload_converter = payload_converter
136
+ @logger = logger
137
+ @metric_meter = metric_meter
138
+ @client = client
139
+ end
140
+
141
+ # @!visibility private
142
+ def heartbeat(*details)
143
+ @on_heartbeat&.call(details)
144
+ end
145
+
146
+ # @!visibility private
147
+ def metric_meter
148
+ @metric_meter or raise 'No metric meter configured in this test environment'
149
+ end
150
+
151
+ # @!visibility private
152
+ def client
153
+ @client or raise 'No client configured in this test environment'
154
+ end
155
+ end
156
+
157
+ private_constant :Context
158
+ end
159
+ end
160
+ end