amit-temporalio 0.3.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.
- checksums.yaml +7 -0
- data/.yardopts +2 -0
- data/Gemfile +23 -0
- data/Rakefile +101 -0
- data/lib/temporalio/activity/complete_async_error.rb +11 -0
- data/lib/temporalio/activity/context.rb +116 -0
- data/lib/temporalio/activity/definition.rb +189 -0
- data/lib/temporalio/activity/info.rb +64 -0
- data/lib/temporalio/activity.rb +12 -0
- data/lib/temporalio/api/activity/v1/message.rb +25 -0
- data/lib/temporalio/api/batch/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
- data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
- data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
- data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
- data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
- data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
- data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
- data/lib/temporalio/api/command/v1/message.rb +46 -0
- data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
- data/lib/temporalio/api/common/v1/message.rb +47 -0
- data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
- data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/common.rb +26 -0
- data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
- data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
- data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
- data/lib/temporalio/api/enums/v1/query.rb +22 -0
- data/lib/temporalio/api/enums/v1/reset.rb +23 -0
- data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
- data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
- data/lib/temporalio/api/enums/v1/update.rb +22 -0
- data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
- data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
- data/lib/temporalio/api/export/v1/message.rb +24 -0
- data/lib/temporalio/api/failure/v1/message.rb +35 -0
- data/lib/temporalio/api/filter/v1/message.rb +27 -0
- data/lib/temporalio/api/history/v1/message.rb +90 -0
- data/lib/temporalio/api/namespace/v1/message.rb +31 -0
- data/lib/temporalio/api/nexus/v1/message.rb +40 -0
- data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
- data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
- data/lib/temporalio/api/operatorservice.rb +3 -0
- data/lib/temporalio/api/payload_visitor.rb +1513 -0
- data/lib/temporalio/api/protocol/v1/message.rb +23 -0
- data/lib/temporalio/api/query/v1/message.rb +27 -0
- data/lib/temporalio/api/replication/v1/message.rb +26 -0
- data/lib/temporalio/api/schedule/v1/message.rb +43 -0
- data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
- data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
- data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
- data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
- data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
- data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
- data/lib/temporalio/api/testservice/v1/service.rb +23 -0
- data/lib/temporalio/api/update/v1/message.rb +33 -0
- data/lib/temporalio/api/version/v1/message.rb +26 -0
- data/lib/temporalio/api/workflow/v1/message.rb +43 -0
- data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
- data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflowservice.rb +3 -0
- data/lib/temporalio/api.rb +14 -0
- data/lib/temporalio/cancellation.rb +170 -0
- data/lib/temporalio/client/activity_id_reference.rb +32 -0
- data/lib/temporalio/client/async_activity_handle.rb +85 -0
- data/lib/temporalio/client/connection/cloud_service.rb +726 -0
- data/lib/temporalio/client/connection/operator_service.rb +201 -0
- data/lib/temporalio/client/connection/service.rb +42 -0
- data/lib/temporalio/client/connection/test_service.rb +111 -0
- data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
- data/lib/temporalio/client/connection.rb +316 -0
- data/lib/temporalio/client/interceptor.rb +416 -0
- data/lib/temporalio/client/schedule.rb +967 -0
- data/lib/temporalio/client/schedule_handle.rb +126 -0
- data/lib/temporalio/client/workflow_execution.rb +100 -0
- data/lib/temporalio/client/workflow_execution_count.rb +36 -0
- data/lib/temporalio/client/workflow_execution_status.rb +18 -0
- data/lib/temporalio/client/workflow_handle.rb +389 -0
- data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
- data/lib/temporalio/client/workflow_update_handle.rb +65 -0
- data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
- data/lib/temporalio/client.rb +484 -0
- data/lib/temporalio/common_enums.rb +41 -0
- data/lib/temporalio/converters/data_converter.rb +99 -0
- data/lib/temporalio/converters/failure_converter.rb +202 -0
- data/lib/temporalio/converters/payload_codec.rb +26 -0
- data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
- data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
- data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
- data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
- data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
- data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
- data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
- data/lib/temporalio/converters/payload_converter.rb +71 -0
- data/lib/temporalio/converters/raw_value.rb +20 -0
- data/lib/temporalio/converters.rb +9 -0
- data/lib/temporalio/error/failure.rb +219 -0
- data/lib/temporalio/error.rb +155 -0
- data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
- data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
- data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
- data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
- data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
- data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
- data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
- data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
- data/lib/temporalio/internal/bridge/api.rb +3 -0
- data/lib/temporalio/internal/bridge/client.rb +95 -0
- data/lib/temporalio/internal/bridge/runtime.rb +53 -0
- data/lib/temporalio/internal/bridge/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/testing.rb +66 -0
- data/lib/temporalio/internal/bridge/worker.rb +85 -0
- data/lib/temporalio/internal/bridge.rb +36 -0
- data/lib/temporalio/internal/client/implementation.rb +700 -0
- data/lib/temporalio/internal/metric.rb +122 -0
- data/lib/temporalio/internal/proto_utils.rb +133 -0
- data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
- data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
- data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
- data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -0
- data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
- data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
- data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
- data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
- data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
- data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
- data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
- data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
- data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
- data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
- data/lib/temporalio/internal/worker/workflow_worker.rb +236 -0
- data/lib/temporalio/internal.rb +7 -0
- data/lib/temporalio/metric.rb +109 -0
- data/lib/temporalio/retry_policy.rb +74 -0
- data/lib/temporalio/runtime.rb +314 -0
- data/lib/temporalio/scoped_logger.rb +96 -0
- data/lib/temporalio/search_attributes.rb +343 -0
- data/lib/temporalio/testing/activity_environment.rb +136 -0
- data/lib/temporalio/testing/workflow_environment.rb +383 -0
- data/lib/temporalio/testing.rb +10 -0
- data/lib/temporalio/version.rb +5 -0
- data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
- data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
- data/lib/temporalio/worker/activity_executor.rb +55 -0
- data/lib/temporalio/worker/interceptor.rb +362 -0
- data/lib/temporalio/worker/thread_pool.rb +237 -0
- data/lib/temporalio/worker/tuner.rb +189 -0
- data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
- data/lib/temporalio/worker/workflow_executor.rb +26 -0
- data/lib/temporalio/worker/workflow_replayer.rb +343 -0
- data/lib/temporalio/worker.rb +569 -0
- data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
- data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
- data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
- data/lib/temporalio/workflow/definition.rb +566 -0
- data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
- data/lib/temporalio/workflow/future.rb +151 -0
- data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
- data/lib/temporalio/workflow/info.rb +82 -0
- data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
- data/lib/temporalio/workflow/update_info.rb +20 -0
- data/lib/temporalio/workflow.rb +529 -0
- data/lib/temporalio/workflow_history.rb +47 -0
- data/lib/temporalio.rb +11 -0
- data/temporalio.gemspec +28 -0
- metadata +236 -0
@@ -0,0 +1,566 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/workflow'
|
4
|
+
require 'temporalio/workflow/handler_unfinished_policy'
|
5
|
+
|
6
|
+
module Temporalio
|
7
|
+
module Workflow
|
8
|
+
# Base class for all workflows.
|
9
|
+
#
|
10
|
+
# Workflows are instances of this class and must implement {execute}. Inside the workflow code, class methods on
|
11
|
+
# {Workflow} can be used.
|
12
|
+
#
|
13
|
+
# By default, the workflow is named as its unqualified class name. This can be customized with {workflow_name}.
|
14
|
+
class Definition
|
15
|
+
class << self
|
16
|
+
protected
|
17
|
+
|
18
|
+
# Customize the workflow name. By default the workflow is named the unqualified class name of the class provided
|
19
|
+
# to the worker.
|
20
|
+
#
|
21
|
+
# @param workflow_name [String, Symbol] Name to use.
|
22
|
+
def workflow_name(workflow_name)
|
23
|
+
if !workflow_name.is_a?(Symbol) && !workflow_name.is_a?(String)
|
24
|
+
raise ArgumentError,
|
25
|
+
'Workflow name must be a symbol or string'
|
26
|
+
end
|
27
|
+
|
28
|
+
@workflow_name = workflow_name.to_s
|
29
|
+
end
|
30
|
+
|
31
|
+
# Set a workflow as dynamic. Dynamic workflows do not have names and handle any workflow that is not otherwise
|
32
|
+
# registered. A worker can only have one dynamic workflow. It is often useful to use {workflow_raw_args} with
|
33
|
+
# this.
|
34
|
+
#
|
35
|
+
# @param value [Boolean] Whether the workflow is dynamic.
|
36
|
+
def workflow_dynamic(value = true) # rubocop:disable Style/OptionalBooleanParameter
|
37
|
+
@workflow_dynamic = value
|
38
|
+
end
|
39
|
+
|
40
|
+
# Have workflow arguments delivered to `execute` (and `initialize` if {workflow_init} in use) as
|
41
|
+
# {Converters::RawValue}s. These are wrappers for the raw payloads that have not been converted to types (but
|
42
|
+
# they have been decoded by the codec if present). They can be converted with {Workflow.payload_converter}.
|
43
|
+
#
|
44
|
+
# @param value [Boolean] Whether the workflow accepts raw arguments.
|
45
|
+
def workflow_raw_args(value = true) # rubocop:disable Style/OptionalBooleanParameter
|
46
|
+
@workflow_raw_args = value
|
47
|
+
end
|
48
|
+
|
49
|
+
# Configure workflow failure exception types. This sets the types of exceptions that, if a
|
50
|
+
# workflow-thrown exception extends, will cause the workflow/update to fail instead of suspending the workflow
|
51
|
+
# via task failure. These are applied in addition to the worker option. If {::Exception} is set, it effectively
|
52
|
+
# will fail a workflow/update in all user exception cases.
|
53
|
+
#
|
54
|
+
# @param types [Array<Class<Exception>>] Exception types to turn into workflow failures.
|
55
|
+
def workflow_failure_exception_type(*types)
|
56
|
+
types.each do |t|
|
57
|
+
raise ArgumentError, 'All types must classes inheriting Exception' unless t.is_a?(Class) && t < Exception
|
58
|
+
end
|
59
|
+
@workflow_failure_exception_types ||= []
|
60
|
+
@workflow_failure_exception_types.concat(types)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Expose an attribute as a method and as a query. A `workflow_query_attr_reader :foo` is the equivalent of:
|
64
|
+
# ```
|
65
|
+
# workflow_query
|
66
|
+
# def foo
|
67
|
+
# @foo
|
68
|
+
# end
|
69
|
+
# ```
|
70
|
+
# This means it is a superset of `attr_reader`` and will not work if also using `attr_reader` or
|
71
|
+
# `attr_accessor`. If a writer is needed alongside this, use `attr_writer`.
|
72
|
+
#
|
73
|
+
# @param attr_names [Array<Symbol>] Attributes to expose.
|
74
|
+
def workflow_query_attr_reader(*attr_names)
|
75
|
+
@workflow_queries ||= {}
|
76
|
+
attr_names.each do |attr_name|
|
77
|
+
raise 'Expected attr to be a symbol' unless attr_name.is_a?(Symbol)
|
78
|
+
|
79
|
+
if method_defined?(attr_name, false)
|
80
|
+
raise 'Method already defined for this attr name. ' \
|
81
|
+
'Note that a workflow_query_attr_reader includes attr_reader behavior. ' \
|
82
|
+
'If you also want a writer for this attribute, use a separate attr_writer.'
|
83
|
+
end
|
84
|
+
|
85
|
+
# Just run this as if done manually
|
86
|
+
workflow_query
|
87
|
+
define_method(attr_name) { instance_variable_get("@#{attr_name}") }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Mark an `initialize` as needing the workflow start arguments. Otherwise, `initialize` must accept no required
|
92
|
+
# arguments. This must be placed above the `initialize` method or it will fail.
|
93
|
+
#
|
94
|
+
# @param value [Boolean] Whether the start parameters will be passed to `initialize`.
|
95
|
+
def workflow_init(value = true) # rubocop:disable Style/OptionalBooleanParameter
|
96
|
+
self.pending_handler_details = { type: :init, value: }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Mark the next method as a workflow signal with a default name as the name of the method. Signals cannot return
|
100
|
+
# values.
|
101
|
+
#
|
102
|
+
# @param name [String, Symbol, nil] Override the default name.
|
103
|
+
# @param dynamic [Boolean] If true, make the signal dynamic. This means it receives all other signals without
|
104
|
+
# handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
|
105
|
+
# it is useful to have the second parameter be `*args` and `raw_args` be true.
|
106
|
+
# @param raw_args [Boolean] If true, does not convert arguments, but instead provides each argument as
|
107
|
+
# {Converters::RawValue} which is a raw payload wrapper, convertible with {Workflow.payload_converter}.
|
108
|
+
# @param unfinished_policy [HandlerUnfinishedPolicy] How to treat unfinished handlers if they are still running
|
109
|
+
# when the workflow ends. The default warns, but this can be disabled.
|
110
|
+
def workflow_signal(
|
111
|
+
name: nil,
|
112
|
+
dynamic: false,
|
113
|
+
raw_args: false,
|
114
|
+
unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
|
115
|
+
)
|
116
|
+
raise 'Cannot provide name if dynamic is true' if name && dynamic
|
117
|
+
|
118
|
+
self.pending_handler_details = { type: :signal, name:, dynamic:, raw_args:, unfinished_policy: }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Mark the next method as a workflow query with a default name as the name of the method. Queries can not have
|
122
|
+
# any side effects, meaning they should never mutate state or try to wait on anything.
|
123
|
+
#
|
124
|
+
# @param name [String, Symbol, nil] Override the default name.
|
125
|
+
# @param dynamic [Boolean] If true, make the query dynamic. This means it receives all other queries without
|
126
|
+
# handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
|
127
|
+
# it is useful to have the second parameter be `*args` and `raw_args` be true.
|
128
|
+
# @param raw_args [Boolean] If true, does not convert arguments, but instead provides each argument as
|
129
|
+
# {Converters::RawValue} which is a raw payload wrapper, convertible with {Workflow.payload_converter}.
|
130
|
+
def workflow_query(
|
131
|
+
name: nil,
|
132
|
+
dynamic: false,
|
133
|
+
raw_args: false
|
134
|
+
)
|
135
|
+
raise 'Cannot provide name if dynamic is true' if name && dynamic
|
136
|
+
|
137
|
+
self.pending_handler_details = { type: :query, name:, dynamic:, raw_args: }
|
138
|
+
end
|
139
|
+
|
140
|
+
# Mark the next method as a workflow update with a default name as the name of the method. Updates can return
|
141
|
+
# values. Separate validation methods can be provided via {workflow_update_validator}.
|
142
|
+
#
|
143
|
+
# @param name [String, Symbol, nil] Override the default name.
|
144
|
+
# @param dynamic [Boolean] If true, make the update dynamic. This means it receives all other updates without
|
145
|
+
# handlers. This cannot have a name override since it is nameless. The first parameter will be the name. Often
|
146
|
+
# it is useful to have the second parameter be `*args` and `raw_args` be true.
|
147
|
+
# @param raw_args [Boolean] If true, does not convert arguments, but instead provides each argument as
|
148
|
+
# {Converters::RawValue} which is a raw payload wrapper, convertible with {Workflow.payload_converter}.
|
149
|
+
# @param unfinished_policy [HandlerUnfinishedPolicy] How to treat unfinished handlers if they are still running
|
150
|
+
# when the workflow ends. The default warns, but this can be disabled.
|
151
|
+
def workflow_update(
|
152
|
+
name: nil,
|
153
|
+
dynamic: false,
|
154
|
+
raw_args: false,
|
155
|
+
unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
|
156
|
+
)
|
157
|
+
raise 'Cannot provide name if dynamic is true' if name && dynamic
|
158
|
+
|
159
|
+
self.pending_handler_details = { type: :update, name:, dynamic:, raw_args:, unfinished_policy: }
|
160
|
+
end
|
161
|
+
|
162
|
+
# Mark the next method as a workflow update validator to the given update method. The validator is expected to
|
163
|
+
# have the exact same parameter signature. It will run before an update and if it raises an exception, the
|
164
|
+
# update will be rejected, possibly before even reaching history. Validators cannot have any side effects or do
|
165
|
+
# any waiting, and they do not return values.
|
166
|
+
#
|
167
|
+
# @param update_method [Symbol] Name of the update method.
|
168
|
+
def workflow_update_validator(update_method)
|
169
|
+
self.pending_handler_details = { type: :update_validator, update_method: }
|
170
|
+
end
|
171
|
+
|
172
|
+
private
|
173
|
+
|
174
|
+
attr_reader :pending_handler_details
|
175
|
+
|
176
|
+
def pending_handler_details=(value)
|
177
|
+
if value.nil?
|
178
|
+
@pending_handler_details = value
|
179
|
+
return
|
180
|
+
elsif @pending_handler_details
|
181
|
+
raise "Previous #{@pending_handler_details[:type]} handler was not put on method before this handler"
|
182
|
+
end
|
183
|
+
|
184
|
+
@pending_handler_details = value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# @!visibility private
|
189
|
+
def self.method_added(method_name)
|
190
|
+
super
|
191
|
+
|
192
|
+
# Nothing to do if there are no pending handler details
|
193
|
+
handler = pending_handler_details
|
194
|
+
return unless handler
|
195
|
+
|
196
|
+
# Reset details
|
197
|
+
self.pending_handler_details = nil
|
198
|
+
|
199
|
+
# Initialize class variables if not done already
|
200
|
+
@workflow_signals ||= {}
|
201
|
+
@workflow_queries ||= {}
|
202
|
+
@workflow_updates ||= {}
|
203
|
+
@workflow_update_validators ||= {}
|
204
|
+
@defined_methods ||= []
|
205
|
+
|
206
|
+
defn, hash, other_hashes =
|
207
|
+
case handler[:type]
|
208
|
+
when :init
|
209
|
+
raise "workflow_init was applied to #{method_name} instead of initialize" if method_name != :initialize
|
210
|
+
|
211
|
+
@workflow_init = handler[:value]
|
212
|
+
return
|
213
|
+
when :update_validator
|
214
|
+
other = @workflow_update_validators[handler[:update_method]]
|
215
|
+
if other && (other[:method_name] != method_name || other[:update_method] != handler[:update_method])
|
216
|
+
raise "Workflow update validator on #{method_name} for #{handler[:update_method]} defined separately " \
|
217
|
+
"on #{other[:method_name]} for #{other[:update_method]}"
|
218
|
+
end
|
219
|
+
|
220
|
+
# Just store this, we'll apply validators to updates at definition
|
221
|
+
# building time
|
222
|
+
@workflow_update_validators[handler[:update_method]] = { method_name:, **handler }
|
223
|
+
return
|
224
|
+
when :signal
|
225
|
+
[Signal.new(
|
226
|
+
name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
|
227
|
+
to_invoke: method_name,
|
228
|
+
raw_args: handler[:raw_args],
|
229
|
+
unfinished_policy: handler[:unfinished_policy]
|
230
|
+
), @workflow_signals, [@workflow_queries, @workflow_updates]]
|
231
|
+
when :query
|
232
|
+
[Query.new(
|
233
|
+
name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
|
234
|
+
to_invoke: method_name,
|
235
|
+
raw_args: handler[:raw_args]
|
236
|
+
), @workflow_queries, [@workflow_signals, @workflow_updates]]
|
237
|
+
when :update
|
238
|
+
[Update.new(
|
239
|
+
name: handler[:dynamic] ? nil : (handler[:name] || method_name).to_s,
|
240
|
+
to_invoke: method_name,
|
241
|
+
raw_args: handler[:raw_args],
|
242
|
+
unfinished_policy: handler[:unfinished_policy]
|
243
|
+
), @workflow_updates, [@workflow_signals, @workflow_queries]]
|
244
|
+
else
|
245
|
+
raise "Unrecognized handler type #{handler[:type]}"
|
246
|
+
end
|
247
|
+
|
248
|
+
# We only allow dupes with the same method name (override/redefine)
|
249
|
+
# TODO(cretz): Should we also check that everything else is the same?
|
250
|
+
other = hash[defn.name]
|
251
|
+
if other && other.to_invoke != method_name
|
252
|
+
raise "Workflow #{handler[:type].name} #{defn.name || '<dynamic>'} defined on " \
|
253
|
+
"different methods #{other.to_invoke} and #{method_name}"
|
254
|
+
elsif defn.name && other_hashes.any? { |h| h.include?(defn.name) }
|
255
|
+
raise "Workflow signal #{defn.name} already defined as a different handler type"
|
256
|
+
end
|
257
|
+
hash[defn.name] = defn
|
258
|
+
|
259
|
+
# Define class method for referencing the definition only if non-dynamic
|
260
|
+
return unless defn.name
|
261
|
+
|
262
|
+
define_singleton_method(method_name) { defn }
|
263
|
+
@defined_methods.push(method_name)
|
264
|
+
end
|
265
|
+
|
266
|
+
# @!visibility private
|
267
|
+
def self.singleton_method_added(method_name)
|
268
|
+
super
|
269
|
+
# We need to ensure class methods are not added after we have defined a method
|
270
|
+
return unless @defined_methods&.include?(method_name)
|
271
|
+
|
272
|
+
raise 'Attempting to override Temporal-defined class definition method'
|
273
|
+
end
|
274
|
+
|
275
|
+
# @!visibility private
|
276
|
+
def self._workflow_definition
|
277
|
+
@workflow_definition ||= _build_workflow_definition
|
278
|
+
end
|
279
|
+
|
280
|
+
# @!visibility private
|
281
|
+
def self._workflow_type_from_workflow_parameter(workflow)
|
282
|
+
case workflow
|
283
|
+
when Class
|
284
|
+
unless workflow < Definition
|
285
|
+
raise ArgumentError, "Class '#{workflow}' does not extend Temporalio::Workflow::Definition"
|
286
|
+
end
|
287
|
+
|
288
|
+
info = Info.from_class(workflow)
|
289
|
+
info.name || raise(ArgumentError, 'Cannot pass dynamic workflow to start')
|
290
|
+
when Info
|
291
|
+
workflow.name || raise(ArgumentError, 'Cannot pass dynamic workflow to start')
|
292
|
+
when String, Symbol
|
293
|
+
workflow.to_s
|
294
|
+
else
|
295
|
+
raise ArgumentError, 'Workflow is not a workflow class or string/symbol'
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# @!visibility private
|
300
|
+
def self._build_workflow_definition
|
301
|
+
# Make sure there isn't dangling pending handler details
|
302
|
+
if pending_handler_details
|
303
|
+
raise "Leftover #{pending_handler_details&.[](:type)} handler not applied to a method"
|
304
|
+
end
|
305
|
+
|
306
|
+
# Apply all update validators before merging with super
|
307
|
+
updates = @workflow_updates&.dup || {}
|
308
|
+
@workflow_update_validators&.each_value do |validator|
|
309
|
+
update = updates.values.find { |u| u.to_invoke == validator[:update_method] }
|
310
|
+
unless update
|
311
|
+
raise "Unable to find update #{validator[:update_method]} pointed to by " \
|
312
|
+
"validator on #{validator[:method_name]}"
|
313
|
+
end
|
314
|
+
if instance_method(validator[:method_name])&.parameters !=
|
315
|
+
instance_method(validator[:update_method])&.parameters
|
316
|
+
raise "Validator on #{validator[:method_name]} does not have " \
|
317
|
+
"exact parameter signature of #{validator[:update_method]}"
|
318
|
+
end
|
319
|
+
|
320
|
+
updates[update.name] = update._with_validator_to_invoke(validator[:method_name])
|
321
|
+
end
|
322
|
+
|
323
|
+
# If there is a superclass, apply some values and check others
|
324
|
+
override_name = @workflow_name
|
325
|
+
dynamic = @workflow_dynamic
|
326
|
+
init = @workflow_init
|
327
|
+
raw_args = @workflow_raw_args
|
328
|
+
signals = @workflow_signals || {}
|
329
|
+
queries = @workflow_queries || {}
|
330
|
+
if superclass && superclass != Temporalio::Workflow::Definition
|
331
|
+
# @type var super_info: Temporalio::Workflow::Definition::Info
|
332
|
+
super_info = superclass._workflow_definition # steep:ignore
|
333
|
+
|
334
|
+
# Override values if not set here
|
335
|
+
override_name = super_info.override_name if override_name.nil?
|
336
|
+
dynamic = super_info.dynamic if dynamic.nil?
|
337
|
+
init = super_info.init if init.nil?
|
338
|
+
raw_args = super_info.raw_args if raw_args.nil?
|
339
|
+
|
340
|
+
# Make sure handlers on the same method at least have the same name
|
341
|
+
# TODO(cretz): Need to validate any other handler override details?
|
342
|
+
# Probably not because we only care that caller-needed values remain
|
343
|
+
# unchanged (method and name), implementer-needed values can be
|
344
|
+
# overridden/changed.
|
345
|
+
self_handlers = signals.values + queries.values + updates.values
|
346
|
+
super_handlers = super_info.signals.values + super_info.queries.values + super_info.updates.values
|
347
|
+
super_handlers.each do |super_handler|
|
348
|
+
self_handler = self_handlers.find { |h| h.to_invoke == super_handler.to_invoke }
|
349
|
+
next unless self_handler
|
350
|
+
|
351
|
+
if super_handler.class != self_handler.class
|
352
|
+
raise "Superclass handler on #{self_handler.to_invoke} is a #{super_handler.class} " \
|
353
|
+
"but current class expects #{self_handler.class}"
|
354
|
+
end
|
355
|
+
if super_handler.name != self_handler.name
|
356
|
+
raise "Superclass handler on #{self_handler.to_invoke} has name #{super_handler.name} " \
|
357
|
+
"but current class expects #{self_handler.name}"
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Merge handlers. We will merge such that handlers defined here
|
362
|
+
# override ones from superclass by _name_ (not method to invoke).
|
363
|
+
signals = super_info.signals.merge(signals)
|
364
|
+
queries = super_info.queries.merge(queries)
|
365
|
+
updates = super_info.updates.merge(updates)
|
366
|
+
end
|
367
|
+
|
368
|
+
# If init is true, validate initialize and execute signatures are identical
|
369
|
+
if init && instance_method(:initialize)&.parameters&.size != instance_method(:execute)&.parameters&.size
|
370
|
+
raise 'workflow_init present, so parameter count of initialize and execute must be the same'
|
371
|
+
end
|
372
|
+
|
373
|
+
raise 'Workflow cannot be given a name and be dynamic' if dynamic && override_name
|
374
|
+
|
375
|
+
Info.new(
|
376
|
+
workflow_class: self,
|
377
|
+
override_name:,
|
378
|
+
dynamic: dynamic || false,
|
379
|
+
init: init || false,
|
380
|
+
raw_args: raw_args || false,
|
381
|
+
failure_exception_types: @workflow_failure_exception_types || [],
|
382
|
+
signals:,
|
383
|
+
queries:,
|
384
|
+
updates:
|
385
|
+
)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Execute the workflow. This is the primary workflow method. The workflow is completed when this method completes.
|
389
|
+
# This must be implemented by all workflows.
|
390
|
+
def execute(*args)
|
391
|
+
raise NotImplementedError, 'Workflow did not implement "execute"'
|
392
|
+
end
|
393
|
+
|
394
|
+
# Information about the workflow definition. This is usually not used directly.
|
395
|
+
class Info
|
396
|
+
attr_reader :workflow_class, :override_name, :dynamic, :init, :raw_args,
|
397
|
+
:failure_exception_types, :signals, :queries, :updates
|
398
|
+
|
399
|
+
# Derive the workflow definition info from the class.
|
400
|
+
#
|
401
|
+
# @param workflow_class [Class<Definition>] Workflow class.
|
402
|
+
# @return [Info] Built info.
|
403
|
+
def self.from_class(workflow_class)
|
404
|
+
unless workflow_class.is_a?(Class) && workflow_class < Definition
|
405
|
+
raise "Workflow '#{workflow_class}' must be a class and must extend Temporalio::Workflow::Definition"
|
406
|
+
end
|
407
|
+
|
408
|
+
workflow_class._workflow_definition
|
409
|
+
end
|
410
|
+
|
411
|
+
# Create a definition info. This should usually not be used directly, but instead a class that extends
|
412
|
+
# {Workflow::Definition} should be used.
|
413
|
+
def initialize(
|
414
|
+
workflow_class:,
|
415
|
+
override_name: nil,
|
416
|
+
dynamic: false,
|
417
|
+
init: false,
|
418
|
+
raw_args: false,
|
419
|
+
failure_exception_types: [],
|
420
|
+
signals: {},
|
421
|
+
queries: {},
|
422
|
+
updates: {}
|
423
|
+
)
|
424
|
+
@workflow_class = workflow_class
|
425
|
+
@override_name = override_name
|
426
|
+
@dynamic = dynamic
|
427
|
+
@init = init
|
428
|
+
@raw_args = raw_args
|
429
|
+
@failure_exception_types = failure_exception_types.dup.freeze
|
430
|
+
@signals = signals.dup.freeze
|
431
|
+
@queries = queries.dup.freeze
|
432
|
+
@updates = updates.dup.freeze
|
433
|
+
end
|
434
|
+
|
435
|
+
# @return [String] Workflow name.
|
436
|
+
def name
|
437
|
+
dynamic ? nil : (override_name || workflow_class.name.to_s.split('::').last)
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
# A signal definition. This is usually built as a result of a {Definition.workflow_signal} method, but can be
|
442
|
+
# manually created to set at runtime on {Workflow.signal_handlers}.
|
443
|
+
class Signal
|
444
|
+
attr_reader :name, :to_invoke, :raw_args, :unfinished_policy
|
445
|
+
|
446
|
+
# @!visibility private
|
447
|
+
def self._name_from_parameter(signal)
|
448
|
+
case signal
|
449
|
+
when Workflow::Definition::Signal
|
450
|
+
signal.name || raise(ArgumentError, 'Cannot call dynamic signal directly')
|
451
|
+
when String, Symbol
|
452
|
+
signal.to_s
|
453
|
+
else
|
454
|
+
raise ArgumentError, 'Signal is not a definition or string/symbol'
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
# Create a signal definition manually. See {Definition.workflow_signal} for more details on some of the
|
459
|
+
# parameters.
|
460
|
+
#
|
461
|
+
# @param name [String, nil] Name or nil if dynamic.
|
462
|
+
# @param to_invoke [Symbol, Proc] Method name or proc to invoke.
|
463
|
+
# @param raw_args [Boolean] Whether the parameters should be raw values.
|
464
|
+
# @param unfinished_policy [HandlerUnfinishedPolicy] How the workflow reacts when this handler is still running
|
465
|
+
# on workflow completion.
|
466
|
+
def initialize(
|
467
|
+
name:,
|
468
|
+
to_invoke:,
|
469
|
+
raw_args: false,
|
470
|
+
unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON
|
471
|
+
)
|
472
|
+
@name = name
|
473
|
+
@to_invoke = to_invoke
|
474
|
+
@raw_args = raw_args
|
475
|
+
@unfinished_policy = unfinished_policy
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
# A query definition. This is usually built as a result of a {Definition.workflow_query} method, but can be
|
480
|
+
# manually created to set at runtime on {Workflow.query_handlers}.
|
481
|
+
class Query
|
482
|
+
attr_reader :name, :to_invoke, :raw_args
|
483
|
+
|
484
|
+
# @!visibility private
|
485
|
+
def self._name_from_parameter(query)
|
486
|
+
case query
|
487
|
+
when Workflow::Definition::Query
|
488
|
+
query.name || raise(ArgumentError, 'Cannot call dynamic query directly')
|
489
|
+
when String, Symbol
|
490
|
+
query.to_s
|
491
|
+
else
|
492
|
+
raise ArgumentError, 'Query is not a definition or string/symbol'
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Create a query definition manually. See {Definition.workflow_query} for more details on some of the
|
497
|
+
# parameters.
|
498
|
+
#
|
499
|
+
# @param name [String, nil] Name or nil if dynamic.
|
500
|
+
# @param to_invoke [Symbol, Proc] Method name or proc to invoke.
|
501
|
+
# @param raw_args [Boolean] Whether the parameters should be raw values.
|
502
|
+
def initialize(
|
503
|
+
name:,
|
504
|
+
to_invoke:,
|
505
|
+
raw_args: false
|
506
|
+
)
|
507
|
+
@name = name
|
508
|
+
@to_invoke = to_invoke
|
509
|
+
@raw_args = raw_args
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
# An update definition. This is usually built as a result of a {Definition.workflow_update} method, but can be
|
514
|
+
# manually created to set at runtime on {Workflow.update_handlers}.
|
515
|
+
class Update
|
516
|
+
attr_reader :name, :to_invoke, :raw_args, :unfinished_policy, :validator_to_invoke
|
517
|
+
|
518
|
+
# @!visibility private
|
519
|
+
def self._name_from_parameter(update)
|
520
|
+
case update
|
521
|
+
when Workflow::Definition::Update
|
522
|
+
update.name || raise(ArgumentError, 'Cannot call dynamic update directly')
|
523
|
+
when String, Symbol
|
524
|
+
update.to_s
|
525
|
+
else
|
526
|
+
raise ArgumentError, 'Update is not a definition or string/symbol'
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
# Create an update definition manually. See {Definition.workflow_update} for more details on some of the
|
531
|
+
# parameters.
|
532
|
+
#
|
533
|
+
# @param name [String, nil] Name or nil if dynamic.
|
534
|
+
# @param to_invoke [Symbol, Proc] Method name or proc to invoke.
|
535
|
+
# @param raw_args [Boolean] Whether the parameters should be raw values.
|
536
|
+
# @param unfinished_policy [HandlerUnfinishedPolicy] How the workflow reacts when this handler is still running
|
537
|
+
# on workflow completion.
|
538
|
+
# @param validator_to_invoke [Symbol, Proc, nil] Method name or proc validator to invoke.
|
539
|
+
def initialize(
|
540
|
+
name:,
|
541
|
+
to_invoke:,
|
542
|
+
raw_args: false,
|
543
|
+
unfinished_policy: HandlerUnfinishedPolicy::WARN_AND_ABANDON,
|
544
|
+
validator_to_invoke: nil
|
545
|
+
)
|
546
|
+
@name = name
|
547
|
+
@to_invoke = to_invoke
|
548
|
+
@raw_args = raw_args
|
549
|
+
@unfinished_policy = unfinished_policy
|
550
|
+
@validator_to_invoke = validator_to_invoke
|
551
|
+
end
|
552
|
+
|
553
|
+
# @!visibility private
|
554
|
+
def _with_validator_to_invoke(validator_to_invoke)
|
555
|
+
Update.new(
|
556
|
+
name:,
|
557
|
+
to_invoke:,
|
558
|
+
raw_args:,
|
559
|
+
unfinished_policy:,
|
560
|
+
validator_to_invoke:
|
561
|
+
)
|
562
|
+
end
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/workflow'
|
4
|
+
|
5
|
+
module Temporalio
|
6
|
+
module Workflow
|
7
|
+
# Handle for interacting with an external workflow.
|
8
|
+
#
|
9
|
+
# This is created via {Workflow.external_workflow_handle}, it is never instantiated directly.
|
10
|
+
class ExternalWorkflowHandle
|
11
|
+
# @!visibility private
|
12
|
+
def initialize
|
13
|
+
raise NotImplementedError, 'Cannot instantiate an external handle directly'
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [String] ID for the workflow.
|
17
|
+
def id
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String, nil] Run ID for the workflow.
|
22
|
+
def run_id
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
# Signal the external workflow.
|
27
|
+
#
|
28
|
+
# @param signal [Workflow::Definition::Signal, Symbol, String] Signal definition or name.
|
29
|
+
# @param args [Array<Object>] Signal args.
|
30
|
+
# @param cancellation [Cancellation] Cancellation for canceling the signalling.
|
31
|
+
def signal(signal, *args, cancellation: Workflow.cancellation)
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Cancel the external workflow.
|
36
|
+
def cancel
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|