temporalio 0.0.2 → 0.1.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.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -23
  3. data/bridge/Cargo.lock +168 -59
  4. data/bridge/Cargo.toml +4 -2
  5. data/bridge/sdk-core/README.md +19 -6
  6. data/bridge/sdk-core/client/src/lib.rs +215 -39
  7. data/bridge/sdk-core/client/src/metrics.rs +17 -8
  8. data/bridge/sdk-core/client/src/raw.rs +4 -4
  9. data/bridge/sdk-core/client/src/retry.rs +32 -20
  10. data/bridge/sdk-core/core/Cargo.toml +22 -9
  11. data/bridge/sdk-core/core/src/abstractions.rs +203 -14
  12. data/bridge/sdk-core/core/src/core_tests/activity_tasks.rs +76 -41
  13. data/bridge/sdk-core/core/src/core_tests/determinism.rs +165 -2
  14. data/bridge/sdk-core/core/src/core_tests/local_activities.rs +204 -83
  15. data/bridge/sdk-core/core/src/core_tests/queries.rs +3 -4
  16. data/bridge/sdk-core/core/src/core_tests/workers.rs +1 -3
  17. data/bridge/sdk-core/core/src/core_tests/workflow_tasks.rs +397 -54
  18. data/bridge/sdk-core/core/src/ephemeral_server/mod.rs +106 -12
  19. data/bridge/sdk-core/core/src/internal_flags.rs +136 -0
  20. data/bridge/sdk-core/core/src/lib.rs +16 -9
  21. data/bridge/sdk-core/core/src/telemetry/log_export.rs +1 -1
  22. data/bridge/sdk-core/core/src/telemetry/metrics.rs +69 -35
  23. data/bridge/sdk-core/core/src/telemetry/mod.rs +29 -13
  24. data/bridge/sdk-core/core/src/telemetry/prometheus_server.rs +17 -12
  25. data/bridge/sdk-core/core/src/test_help/mod.rs +62 -12
  26. data/bridge/sdk-core/core/src/worker/activities/activity_heartbeat_manager.rs +112 -156
  27. data/bridge/sdk-core/core/src/worker/activities/activity_task_poller_stream.rs +89 -0
  28. data/bridge/sdk-core/core/src/worker/activities/local_activities.rs +352 -122
  29. data/bridge/sdk-core/core/src/worker/activities.rs +233 -157
  30. data/bridge/sdk-core/core/src/worker/client/mocks.rs +22 -2
  31. data/bridge/sdk-core/core/src/worker/client.rs +18 -2
  32. data/bridge/sdk-core/core/src/worker/mod.rs +165 -58
  33. data/bridge/sdk-core/core/src/worker/workflow/bridge.rs +1 -3
  34. data/bridge/sdk-core/core/src/worker/workflow/driven_workflow.rs +3 -5
  35. data/bridge/sdk-core/core/src/worker/workflow/history_update.rs +856 -277
  36. data/bridge/sdk-core/core/src/worker/workflow/machines/activity_state_machine.rs +100 -43
  37. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_external_state_machine.rs +7 -7
  38. data/bridge/sdk-core/core/src/worker/workflow/machines/cancel_workflow_state_machine.rs +5 -4
  39. data/bridge/sdk-core/core/src/worker/workflow/machines/child_workflow_state_machine.rs +87 -27
  40. data/bridge/sdk-core/core/src/worker/workflow/machines/complete_workflow_state_machine.rs +5 -4
  41. data/bridge/sdk-core/core/src/worker/workflow/machines/continue_as_new_workflow_state_machine.rs +5 -4
  42. data/bridge/sdk-core/core/src/worker/workflow/machines/fail_workflow_state_machine.rs +5 -4
  43. data/bridge/sdk-core/core/src/worker/workflow/machines/local_activity_state_machine.rs +137 -62
  44. data/bridge/sdk-core/core/src/worker/workflow/machines/mod.rs +25 -17
  45. data/bridge/sdk-core/core/src/worker/workflow/machines/modify_workflow_properties_state_machine.rs +7 -6
  46. data/bridge/sdk-core/core/src/worker/workflow/machines/patch_state_machine.rs +103 -152
  47. data/bridge/sdk-core/core/src/worker/workflow/machines/signal_external_state_machine.rs +7 -7
  48. data/bridge/sdk-core/core/src/worker/workflow/machines/timer_state_machine.rs +9 -9
  49. data/bridge/sdk-core/core/src/worker/workflow/machines/transition_coverage.rs +2 -2
  50. data/bridge/sdk-core/core/src/worker/workflow/machines/upsert_search_attributes_state_machine.rs +14 -7
  51. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines/local_acts.rs +5 -16
  52. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_machines.rs +201 -121
  53. data/bridge/sdk-core/core/src/worker/workflow/machines/workflow_task_state_machine.rs +11 -14
  54. data/bridge/sdk-core/core/src/worker/workflow/managed_run/managed_wf_test.rs +30 -15
  55. data/bridge/sdk-core/core/src/worker/workflow/managed_run.rs +1026 -376
  56. data/bridge/sdk-core/core/src/worker/workflow/mod.rs +460 -384
  57. data/bridge/sdk-core/core/src/worker/workflow/run_cache.rs +40 -57
  58. data/bridge/sdk-core/core/src/worker/workflow/wft_extraction.rs +125 -0
  59. data/bridge/sdk-core/core/src/worker/workflow/wft_poller.rs +1 -4
  60. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/saved_wf_inputs.rs +117 -0
  61. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream/tonic_status_serde.rs +24 -0
  62. data/bridge/sdk-core/core/src/worker/workflow/workflow_stream.rs +448 -718
  63. data/bridge/sdk-core/core-api/Cargo.toml +2 -1
  64. data/bridge/sdk-core/core-api/src/errors.rs +1 -34
  65. data/bridge/sdk-core/core-api/src/lib.rs +6 -2
  66. data/bridge/sdk-core/core-api/src/telemetry.rs +0 -6
  67. data/bridge/sdk-core/core-api/src/worker.rs +14 -1
  68. data/bridge/sdk-core/fsm/rustfsm_procmacro/src/lib.rs +18 -15
  69. data/bridge/sdk-core/fsm/rustfsm_trait/src/lib.rs +8 -3
  70. data/bridge/sdk-core/histories/evict_while_la_running_no_interference-16_history.bin +0 -0
  71. data/bridge/sdk-core/protos/api_upstream/temporal/api/command/v1/message.proto +5 -17
  72. data/bridge/sdk-core/protos/api_upstream/temporal/api/common/v1/message.proto +11 -0
  73. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/command_type.proto +1 -6
  74. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/event_type.proto +6 -6
  75. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/failed_cause.proto +5 -0
  76. data/bridge/sdk-core/protos/api_upstream/temporal/api/enums/v1/update.proto +22 -6
  77. data/bridge/sdk-core/protos/api_upstream/temporal/api/history/v1/message.proto +48 -19
  78. data/bridge/sdk-core/protos/api_upstream/temporal/api/namespace/v1/message.proto +2 -0
  79. data/bridge/sdk-core/protos/api_upstream/temporal/api/operatorservice/v1/request_response.proto +3 -0
  80. data/bridge/sdk-core/protos/api_upstream/temporal/api/{enums/v1/interaction_type.proto → protocol/v1/message.proto} +29 -11
  81. data/bridge/sdk-core/protos/api_upstream/temporal/api/sdk/v1/task_complete_metadata.proto +63 -0
  82. data/bridge/sdk-core/protos/api_upstream/temporal/api/update/v1/message.proto +111 -0
  83. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/request_response.proto +59 -28
  84. data/bridge/sdk-core/protos/api_upstream/temporal/api/workflowservice/v1/service.proto +2 -2
  85. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_result/activity_result.proto +1 -0
  86. data/bridge/sdk-core/protos/local/temporal/sdk/core/activity_task/activity_task.proto +1 -0
  87. data/bridge/sdk-core/protos/local/temporal/sdk/core/child_workflow/child_workflow.proto +1 -0
  88. data/bridge/sdk-core/protos/local/temporal/sdk/core/common/common.proto +1 -0
  89. data/bridge/sdk-core/protos/local/temporal/sdk/core/core_interface.proto +1 -0
  90. data/bridge/sdk-core/protos/local/temporal/sdk/core/external_data/external_data.proto +1 -0
  91. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_activation/workflow_activation.proto +7 -0
  92. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_commands/workflow_commands.proto +1 -0
  93. data/bridge/sdk-core/protos/local/temporal/sdk/core/workflow_completion/workflow_completion.proto +6 -0
  94. data/bridge/sdk-core/sdk/Cargo.toml +3 -2
  95. data/bridge/sdk-core/sdk/src/lib.rs +87 -20
  96. data/bridge/sdk-core/sdk/src/workflow_future.rs +9 -8
  97. data/bridge/sdk-core/sdk-core-protos/Cargo.toml +5 -2
  98. data/bridge/sdk-core/sdk-core-protos/build.rs +36 -1
  99. data/bridge/sdk-core/sdk-core-protos/src/history_builder.rs +100 -87
  100. data/bridge/sdk-core/sdk-core-protos/src/history_info.rs +5 -1
  101. data/bridge/sdk-core/sdk-core-protos/src/lib.rs +175 -57
  102. data/bridge/sdk-core/sdk-core-protos/src/task_token.rs +12 -2
  103. data/bridge/sdk-core/test-utils/Cargo.toml +3 -1
  104. data/bridge/sdk-core/test-utils/src/canned_histories.rs +106 -296
  105. data/bridge/sdk-core/test-utils/src/histfetch.rs +1 -1
  106. data/bridge/sdk-core/test-utils/src/lib.rs +82 -23
  107. data/bridge/sdk-core/test-utils/src/wf_input_saver.rs +50 -0
  108. data/bridge/sdk-core/test-utils/src/workflows.rs +29 -0
  109. data/bridge/sdk-core/tests/fuzzy_workflow.rs +130 -0
  110. data/bridge/sdk-core/tests/{load_tests.rs → heavy_tests.rs} +125 -51
  111. data/bridge/sdk-core/tests/integ_tests/ephemeral_server_tests.rs +25 -3
  112. data/bridge/sdk-core/tests/integ_tests/heartbeat_tests.rs +5 -3
  113. data/bridge/sdk-core/tests/integ_tests/metrics_tests.rs +218 -16
  114. data/bridge/sdk-core/tests/integ_tests/polling_tests.rs +4 -47
  115. data/bridge/sdk-core/tests/integ_tests/queries_tests.rs +5 -128
  116. data/bridge/sdk-core/tests/integ_tests/visibility_tests.rs +83 -25
  117. data/bridge/sdk-core/tests/integ_tests/workflow_tests/activities.rs +93 -69
  118. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_external.rs +1 -0
  119. data/bridge/sdk-core/tests/integ_tests/workflow_tests/cancel_wf.rs +6 -13
  120. data/bridge/sdk-core/tests/integ_tests/workflow_tests/child_workflows.rs +1 -0
  121. data/bridge/sdk-core/tests/integ_tests/workflow_tests/continue_as_new.rs +6 -2
  122. data/bridge/sdk-core/tests/integ_tests/workflow_tests/determinism.rs +3 -10
  123. data/bridge/sdk-core/tests/integ_tests/workflow_tests/local_activities.rs +72 -191
  124. data/bridge/sdk-core/tests/integ_tests/workflow_tests/modify_wf_properties.rs +1 -0
  125. data/bridge/sdk-core/tests/integ_tests/workflow_tests/patches.rs +7 -28
  126. data/bridge/sdk-core/tests/integ_tests/workflow_tests/replay.rs +12 -7
  127. data/bridge/sdk-core/tests/integ_tests/workflow_tests/resets.rs +1 -0
  128. data/bridge/sdk-core/tests/integ_tests/workflow_tests/signals.rs +18 -14
  129. data/bridge/sdk-core/tests/integ_tests/workflow_tests/stickyness.rs +6 -20
  130. data/bridge/sdk-core/tests/integ_tests/workflow_tests/timers.rs +10 -21
  131. data/bridge/sdk-core/tests/integ_tests/workflow_tests/upsert_search_attrs.rs +6 -4
  132. data/bridge/sdk-core/tests/integ_tests/workflow_tests.rs +10 -11
  133. data/bridge/sdk-core/tests/main.rs +3 -13
  134. data/bridge/sdk-core/tests/runner.rs +75 -36
  135. data/bridge/sdk-core/tests/wf_input_replay.rs +32 -0
  136. data/bridge/src/connection.rs +41 -25
  137. data/bridge/src/lib.rs +269 -14
  138. data/bridge/src/runtime.rs +1 -1
  139. data/bridge/src/test_server.rs +153 -0
  140. data/bridge/src/worker.rs +89 -16
  141. data/lib/gen/temporal/api/command/v1/message_pb.rb +4 -18
  142. data/lib/gen/temporal/api/common/v1/message_pb.rb +4 -0
  143. data/lib/gen/temporal/api/enums/v1/command_type_pb.rb +1 -3
  144. data/lib/gen/temporal/api/enums/v1/event_type_pb.rb +3 -3
  145. data/lib/gen/temporal/api/enums/v1/failed_cause_pb.rb +2 -0
  146. data/lib/gen/temporal/api/enums/v1/update_pb.rb +6 -4
  147. data/lib/gen/temporal/api/history/v1/message_pb.rb +27 -19
  148. data/lib/gen/temporal/api/namespace/v1/message_pb.rb +1 -0
  149. data/lib/gen/temporal/api/operatorservice/v1/request_response_pb.rb +3 -0
  150. data/lib/gen/temporal/api/protocol/v1/message_pb.rb +30 -0
  151. data/lib/gen/temporal/api/sdk/v1/task_complete_metadata_pb.rb +23 -0
  152. data/lib/gen/temporal/api/testservice/v1/request_response_pb.rb +49 -0
  153. data/lib/gen/temporal/api/testservice/v1/service_pb.rb +21 -0
  154. data/lib/gen/temporal/api/update/v1/message_pb.rb +72 -0
  155. data/lib/gen/temporal/api/workflowservice/v1/request_response_pb.rb +26 -16
  156. data/lib/gen/temporal/sdk/core/activity_result/activity_result_pb.rb +13 -9
  157. data/lib/gen/temporal/sdk/core/activity_task/activity_task_pb.rb +10 -6
  158. data/lib/gen/temporal/sdk/core/child_workflow/child_workflow_pb.rb +13 -9
  159. data/lib/gen/temporal/sdk/core/common/common_pb.rb +7 -3
  160. data/lib/gen/temporal/sdk/core/core_interface_pb.rb +9 -3
  161. data/lib/gen/temporal/sdk/core/external_data/external_data_pb.rb +7 -3
  162. data/lib/gen/temporal/sdk/core/workflow_activation/workflow_activation_pb.rb +27 -21
  163. data/lib/gen/temporal/sdk/core/workflow_commands/workflow_commands_pb.rb +28 -24
  164. data/lib/gen/temporal/sdk/core/workflow_completion/workflow_completion_pb.rb +12 -5
  165. data/lib/temporalio/activity/context.rb +13 -8
  166. data/lib/temporalio/activity/info.rb +1 -1
  167. data/lib/temporalio/bridge/connect_options.rb +15 -0
  168. data/lib/temporalio/bridge/retry_config.rb +24 -0
  169. data/lib/temporalio/bridge/tls_options.rb +19 -0
  170. data/lib/temporalio/client/implementation.rb +8 -8
  171. data/lib/temporalio/connection/retry_config.rb +44 -0
  172. data/lib/temporalio/connection/service.rb +20 -0
  173. data/lib/temporalio/connection/test_service.rb +92 -0
  174. data/lib/temporalio/connection/tls_options.rb +51 -0
  175. data/lib/temporalio/connection/workflow_service.rb +731 -0
  176. data/lib/temporalio/connection.rb +55 -720
  177. data/lib/temporalio/interceptor/activity_inbound.rb +22 -0
  178. data/lib/temporalio/interceptor/activity_outbound.rb +24 -0
  179. data/lib/temporalio/interceptor/chain.rb +5 -5
  180. data/lib/temporalio/interceptor/client.rb +8 -4
  181. data/lib/temporalio/interceptor.rb +22 -0
  182. data/lib/temporalio/retry_policy.rb +13 -3
  183. data/lib/temporalio/testing/time_skipping_handle.rb +32 -0
  184. data/lib/temporalio/testing/time_skipping_interceptor.rb +23 -0
  185. data/lib/temporalio/testing/workflow_environment.rb +112 -0
  186. data/lib/temporalio/testing.rb +175 -0
  187. data/lib/temporalio/version.rb +1 -1
  188. data/lib/temporalio/worker/activity_runner.rb +26 -4
  189. data/lib/temporalio/worker/activity_worker.rb +44 -18
  190. data/lib/temporalio/worker/sync_worker.rb +47 -11
  191. data/lib/temporalio/worker.rb +27 -21
  192. data/lib/temporalio/workflow/async.rb +46 -0
  193. data/lib/temporalio/workflow/future.rb +138 -0
  194. data/lib/temporalio/workflow/info.rb +76 -0
  195. data/temporalio.gemspec +4 -3
  196. metadata +67 -17
  197. data/bridge/sdk-core/Cargo.lock +0 -2606
  198. data/bridge/sdk-core/protos/api_upstream/temporal/api/interaction/v1/message.proto +0 -87
  199. data/lib/bridge.so +0 -0
  200. data/lib/gen/temporal/api/enums/v1/interaction_type_pb.rb +0 -25
  201. data/lib/gen/temporal/api/interaction/v1/message_pb.rb +0 -49
  202. data/lib/gen/temporal/sdk/core/bridge/bridge_pb.rb +0 -222
@@ -0,0 +1,22 @@
1
+ module Temporalio
2
+ module Interceptor
3
+ # A mixin for implementing inbound Activity interceptors.
4
+ module ActivityInbound
5
+ class ExecuteActivityInput < Struct.new(
6
+ :activity,
7
+ :args,
8
+ :headers,
9
+ keyword_init: true,
10
+ ); end
11
+
12
+ # Interceptor for {Temporalio::Activity#execute}.
13
+ #
14
+ # @param input [ExecuteActivityInput]
15
+ #
16
+ # @return [any] Activity execution result
17
+ def execute_activity(input)
18
+ yield(input)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ module Temporalio
2
+ module Interceptor
3
+ # A mixin for implementing outbound Activity interceptors.
4
+ module ActivityOutbound
5
+ # Interceptor for {Temporalio::Activity::Context#info}.
6
+ #
7
+ # @yieldreturn Temporalio::Activity::Info
8
+ #
9
+ # @return [Temporalio::Activity::Info]
10
+ def activity_info
11
+ yield
12
+ end
13
+
14
+ # Interceptor for {Temporalio::Activity::Context#heartbeat}.
15
+ #
16
+ # @yieldparam Array[untyped]
17
+ #
18
+ # @param details [any] A list of details supplied with the heartbeat.
19
+ def heartbeat(*details)
20
+ yield(*details)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -6,18 +6,18 @@ module Temporalio
6
6
  @interceptors = interceptors
7
7
  end
8
8
 
9
- def invoke(method, input)
9
+ def invoke(method, *input)
10
10
  chain = interceptors.dup
11
11
 
12
- traverse_chain = lambda do |i|
12
+ traverse_chain = lambda do |*i|
13
13
  if chain.empty?
14
- yield(i)
14
+ yield(*i)
15
15
  else
16
- chain.shift.public_send(method, i, &traverse_chain)
16
+ chain.shift.public_send(method, *i, &traverse_chain)
17
17
  end
18
18
  end
19
19
 
20
- traverse_chain.call(input)
20
+ traverse_chain.call(*input)
21
21
  end
22
22
 
23
23
  private
@@ -1,9 +1,7 @@
1
1
  module Temporalio
2
2
  module Interceptor
3
- # Base class for implementing Client side interceptors.
4
- #
5
- # @abstract
6
- class Client
3
+ # A mixin for implementing Client side interceptors.
4
+ module Client
7
5
  class StartWorkflowInput < Struct.new(
8
6
  :workflow,
9
7
  :args,
@@ -80,6 +78,8 @@ module Temporalio
80
78
  # Interceptor for {Temporalio::Client#start_workflow}.
81
79
  #
82
80
  # @param input [StartWorkflowInput]
81
+ #
82
+ # @return [Temporalio::Client::WorkflowHandle] A handle to interact with the workflow.
83
83
  def start_workflow(input)
84
84
  yield(input)
85
85
  end
@@ -87,6 +87,8 @@ module Temporalio
87
87
  # Interceptor for {Temporalio::Client::WorkflowHandle#describe}.
88
88
  #
89
89
  # @param input [DescribeWorkflowInput]
90
+ #
91
+ # @return [Temporalio::Workflow::ExecutionInfo] Information about the workflow.
90
92
  def describe_workflow(input)
91
93
  yield(input)
92
94
  end
@@ -94,6 +96,8 @@ module Temporalio
94
96
  # Interceptor for {Temporalio::Client::WorkflowHandle#query}.
95
97
  #
96
98
  # @param input [QueryWorkflowInput]
99
+ #
100
+ # @return [any] Query result
97
101
  def query_workflow(input)
98
102
  yield(input)
99
103
  end
@@ -0,0 +1,22 @@
1
+ require 'temporalio/interceptor/activity_inbound'
2
+ require 'temporalio/interceptor/activity_outbound'
3
+
4
+ module Temporalio
5
+ module Interceptor
6
+ # NOTE: Using #each_with_object here and below instead of a simple #select because RBS can't
7
+ # reconcile that resulting array only has WorkflowInbound or WorkflowOutbound in it.
8
+ def self.filter(interceptors, type)
9
+ interceptor_class =
10
+ case type
11
+ when :activity_inbound
12
+ Temporalio::Interceptor::ActivityInbound
13
+ when :activity_outbound
14
+ Temporalio::Interceptor::ActivityOutbound
15
+ end
16
+
17
+ interceptors.each_with_object([]) do |i, result|
18
+ result << i if i.is_a?(interceptor_class)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -24,13 +24,23 @@ module Temporalio
24
24
  # @return [Array<String>] List of error types that are not retryable.
25
25
  attr_reader :non_retriable_errors
26
26
 
27
+ def self.from_proto(proto)
28
+ new(
29
+ initial_interval: proto.initial_interval&.to_f&.round || 0,
30
+ backoff: proto.backoff_coefficient&.to_f || 0.0,
31
+ max_interval: proto.maximum_interval&.to_f&.round || 0,
32
+ max_attempts: proto.maximum_attempts || 1,
33
+ non_retriable_errors: proto.non_retryable_error_types || [],
34
+ ).freeze
35
+ end
36
+
27
37
  # @param initial_interval [Integer] Backoff interval (in seconds) for the first retry.
28
38
  # @param backoff [Float] Coefficient to multiply previous backoff interval by to get new
29
39
  # interval.
30
40
  # @param max_interval [Integer] Maximum backoff interval between retries. Default 100x
31
41
  # {#initial_interval}.
32
42
  # @param max_attempts [Integer] Maximum number of attempts. If 0, there is no maximum.
33
- # @param non_retriable_errors [Array<String>] List of error types that are not retryable.
43
+ # @param non_retriable_errors [Array<Class, String>] List of error types that are not retryable.
34
44
  def initialize(
35
45
  initial_interval: 1,
36
46
  backoff: 2.0,
@@ -42,7 +52,7 @@ module Temporalio
42
52
  @backoff = backoff
43
53
  @max_interval = max_interval
44
54
  @max_attempts = max_attempts
45
- @non_retriable_errors = non_retriable_errors
55
+ @non_retriable_errors = non_retriable_errors.map(&:to_s).compact
46
56
  end
47
57
 
48
58
  def validate!
@@ -75,7 +85,7 @@ module Temporalio
75
85
  backoff_coefficient: backoff,
76
86
  maximum_interval: max_interval ? Google::Protobuf::Duration.new(seconds: max_interval) : nil,
77
87
  maximum_attempts: max_attempts,
78
- non_retryable_error_types: non_retriable_errors.map(&:name).compact,
88
+ non_retryable_error_types: non_retriable_errors,
79
89
  )
80
90
  end
81
91
  end
@@ -0,0 +1,32 @@
1
+ require 'forwardable'
2
+
3
+ module Temporalio
4
+ module Testing
5
+ class TimeSkippingHandle
6
+ extend Forwardable
7
+
8
+ # Proxy all the WorkflowHandle calls to the original handle except for :result
9
+ def_delegators :handle, :id, :run_id, :result_run_id, :first_execution_run_id, :describe,
10
+ :cancel, :query, :signal, :terminate
11
+
12
+ def initialize(handle, env)
13
+ @handle = handle
14
+ @env = env
15
+ end
16
+
17
+ def result(follow_runs: true, rpc_metadata: {}, rpc_timeout: nil)
18
+ env.with_time_skipping do
19
+ handle.result(
20
+ follow_runs: follow_runs,
21
+ rpc_metadata: rpc_metadata,
22
+ rpc_timeout: rpc_timeout,
23
+ )
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :handle, :env
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ require 'temporalio/interceptor/client'
2
+ require 'temporalio/testing/time_skipping_handle'
3
+
4
+ module Temporalio
5
+ module Testing
6
+ class TimeSkippingInterceptor
7
+ include Temporalio::Interceptor::Client
8
+
9
+ def initialize(env)
10
+ @env = env
11
+ end
12
+
13
+ def start_workflow(input)
14
+ handle = yield(input)
15
+ Temporalio::Testing::TimeSkippingHandle.new(handle, env)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :env
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,112 @@
1
+ require 'google/protobuf/well_known_types'
2
+ require 'temporalio/client'
3
+ require 'temporalio/testing/time_skipping_interceptor'
4
+
5
+ module Temporalio
6
+ module Testing
7
+ # Workflow environment for testing workflows.
8
+ #
9
+ # Most developers will want to use the {Temporalio::Testing.start_time_skipping_environment} to
10
+ # start a test server process that automatically skips time as needed. Alternatively,
11
+ # {Temporalio::Testing.start_local_environment} may be used for a full, local Temporal server
12
+ # with more features.
13
+ #
14
+ # @note Unless using the above mentioned methods with an explicit block, you will need to call
15
+ # {#shutdown} after you're finished with this environment.
16
+ class WorkflowEnvironment
17
+ # @return [Temporalio::Connection] A connection to this environment.
18
+ attr_reader :connection
19
+
20
+ # @return [String] A namespace for this environment.
21
+ attr_reader :namespace
22
+
23
+ # @api private
24
+ def initialize(server, connection, namespace)
25
+ @server = server
26
+ @connection = connection
27
+ @namespace = namespace
28
+ end
29
+
30
+ # A default client to be used with this environment.
31
+ #
32
+ # @return [Temporalio::Client] A default client.
33
+ def client
34
+ @client ||= begin
35
+ # TODO: Add a workflow interceptor for interpreting assertion error
36
+ interceptors = [TimeSkippingInterceptor.new(self)]
37
+ Temporalio::Client.new(connection, namespace, interceptors: interceptors)
38
+ end
39
+ end
40
+
41
+ # Sleep in this environment.
42
+ #
43
+ # This awaits a regular {Kernel.sleep} in regular environments, or manually skips time in
44
+ # time-skipping environments.
45
+ #
46
+ # @param duration [Integer] Amount of time to sleep.
47
+ def sleep(duration)
48
+ return Kernel.sleep(duration) unless supports_time_skipping?
49
+
50
+ request = Temporalio::Api::TestService::V1::SleepRequest.new(
51
+ duration: Google::Protobuf::Duration.new(seconds: duration),
52
+ )
53
+ connection.test_service.unlock_time_skipping_with_sleep(request)
54
+
55
+ nil
56
+ end
57
+
58
+ # Get the current time known to this environment.
59
+ #
60
+ # For non-time-skipping environments this is simply the system time. For time-skipping
61
+ # environments this is whatever time has been skipped to.
62
+ #
63
+ # @return [Time]
64
+ def current_time
65
+ return Time.now unless supports_time_skipping?
66
+
67
+ connection.test_service.get_current_time&.time&.to_time
68
+ end
69
+
70
+ # Whether this environment supports time skipping.
71
+ #
72
+ # @return [Boolean]
73
+ def supports_time_skipping?
74
+ server.has_test_service?
75
+ end
76
+
77
+ # Shut down this environment.
78
+ def shutdown
79
+ server.shutdown
80
+ end
81
+
82
+ # Unlock time skipping.
83
+ #
84
+ # This will have no effect in an environment that does not support time-skipping (e.g.
85
+ # Temporalite).
86
+ #
87
+ # @yield A block to be called once time skipping has been unlocked.
88
+ #
89
+ # @return [any] The return value of the block.
90
+ def with_time_skipping
91
+ return yield unless supports_time_skipping?
92
+
93
+ begin
94
+ # Unlock to start time skipping, lock again to stop it
95
+ connection.test_service.unlock_time_skipping(
96
+ Temporalio::Api::TestService::V1::UnlockTimeSkippingRequest.new,
97
+ )
98
+
99
+ yield
100
+ ensure
101
+ connection.test_service.lock_time_skipping(
102
+ Temporalio::Api::TestService::V1::LockTimeSkippingRequest.new,
103
+ )
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ attr_reader :server
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,175 @@
1
+ require 'temporalio/bridge'
2
+ require 'temporalio/connection'
3
+ require 'temporalio/runtime'
4
+ require 'temporalio/testing/workflow_environment'
5
+ require 'temporalio/version'
6
+
7
+ module Temporalio
8
+ module Testing
9
+ DEFAULT_NAMESPACE = 'default'.freeze
10
+
11
+ class << self
12
+ # Start a full Temporal server locally, downloading if necessary.
13
+ #
14
+ # This environment is good for testing full server capabilities, but does not support time
15
+ # skipping like {.start_time_skipping} does.
16
+ # {Temporalio::Testing::WorkflowEnvironment#supports_time_skipping} will always return `false`
17
+ # for this environment. {Temporalio::Testing::WorkflowEnvironment#sleep} will sleep the actual
18
+ # amount of time and {Temporalio::Testing::WorkflowEnvironment#get_current_time}` will return
19
+ # the current time.
20
+ #
21
+ # Internally, this uses [Temporalite](https://github.com/temporalio/temporalite). Which is a
22
+ # self-contained binary for Temporal using Sqlite persistence. This will download Temporalite
23
+ # to a temporary directory by default if it has not already been downloaded before and
24
+ # `:existing_path` is not set.
25
+ #
26
+ # In the future, the Temporalite implementation may be changed to another implementation.
27
+ # Therefore, all `temporalite_` prefixed parameters are Temporalite specific and may not apply
28
+ # to newer versions.
29
+ #
30
+ # @param namespace [String] Namespace name to use for this environment.
31
+ # @param ip [String] IP address to bind to, or 127.0.0.1 by default.
32
+ # @param port [Integer] Port number to bind to, or an OS-provided port by default.
33
+ # @param download_dir [String] Directory to download binary to if a download is needed. If
34
+ # unset, this is the system's temporary directory.
35
+ # @param ui [Boolean] If `true`, will start a UI in Temporalite.
36
+ # @param temporalite_existing_path [String] Existing path to the Temporalite binary. If
37
+ # present, no download will be attempted to fetch the binary.
38
+ # @param temporalite_database_filename [String] Path to the Sqlite database to use for
39
+ # Temporalite. Unset default means only in-memory Sqlite will be used.
40
+ # @param temporalite_log_format [String] Log format for Temporalite.
41
+ # @param temporalite_log_level [String] Log level to use for Temporalite.
42
+ # @param temporalite_download_version [String] Specific Temporalite version to download.
43
+ # Defaults to `default` which downloads the version known to work best with this SDK.
44
+ # @param temporalite_extra_args [Array<String>] Extra arguments for the Temporalite binary.
45
+ #
46
+ # @yield Optionally you can provide a block which will ensure that the environment has been
47
+ # shut down after. The newly created {Temporalio::Testing::WorkflowEnvironment} will be
48
+ # passed into the block as a single argument. Alternatively you will need to call
49
+ # {Temporalio::Testing::WorkflowEnvironment#shutdown} explicitly after you're done with it.
50
+ #
51
+ # @return [Temporalio::Testing::WorkflowEnvironment] The newly started Temporalite workflow
52
+ # environment.
53
+ def start_local_environment(
54
+ namespace: DEFAULT_NAMESPACE,
55
+ ip: '127.0.0.1',
56
+ port: nil,
57
+ download_dir: nil,
58
+ ui: false,
59
+ temporalite_existing_path: nil,
60
+ temporalite_database_filename: nil,
61
+ temporalite_log_format: 'pretty',
62
+ temporalite_log_level: 'warn',
63
+ temporalite_download_version: 'default',
64
+ temporalite_extra_args: [],
65
+ &block
66
+ )
67
+ # TODO: Sync with the SDK's logger level when implemented
68
+ runtime = Temporalio::Runtime.instance
69
+ server = Temporalio::Bridge::TestServer.start_temporalite(
70
+ runtime.core_runtime,
71
+ temporalite_existing_path,
72
+ 'sdk-ruby',
73
+ Temporalio::VERSION,
74
+ temporalite_download_version,
75
+ download_dir,
76
+ namespace,
77
+ ip,
78
+ port,
79
+ temporalite_database_filename,
80
+ ui,
81
+ temporalite_log_format,
82
+ temporalite_log_level,
83
+ temporalite_extra_args,
84
+ )
85
+ env = init_workflow_environment_for(server, namespace)
86
+
87
+ return env unless block
88
+
89
+ run_server(server, env, &block)
90
+ end
91
+
92
+ # Start a time skipping workflow environment.
93
+ #
94
+ # Time can be manually skipped forward using {Temporalio::Testing::WorkflowEnvironment#sleep}.
95
+ # The currently known time can be obtained via
96
+ # {Temporalio::Testing::WorkflowEnvironment#get_current_time}.
97
+ #
98
+ # @note Auto time skipping is not yet implemented.
99
+ #
100
+ # Internally, this environment lazily downloads a test-server binary for the current OS/arch
101
+ # into the temp directory if it is not already there. Then the executable is started and will
102
+ # be killed when {Temporalio::Testing::WorkflowEnvironment#shutdown} is called (which is
103
+ # implicitly done if a block is provided to this method).
104
+ #
105
+ # Users can reuse this environment for testing multiple independent workflows, but not
106
+ # concurrently. Time skipping, which is automatically done when awaiting a workflow result
107
+ # (pending implementation) and manually done on
108
+ # {Temporalio::Testing::WorkflowEnvironment#sleep}, is global to the environment, not to the
109
+ # workflow under test.
110
+ #
111
+ # In the future, the test server implementation may be changed to another implementation.
112
+ # Therefore, all `test_server_` prefixed parameters are test server specific and may not apply
113
+ # to newer versions.
114
+ #
115
+ # @param port [Integer] Port number to bind to, or an OS-provided port by default.
116
+ # @param download_dir [String] Directory to download binary to if a download is needed.
117
+ # If unset, this is the system's temporary directory.
118
+ # @param test_server_existing_path [String] Existing path to the test server binary. If
119
+ # present, no download will be attempted to fetch the binary.
120
+ # @param test_server_download_version [String] Specific test server version to download.
121
+ # Defaults to `default` which downloads the version known to work best with this SDK.
122
+ # @param test_server_extra_args [Array<String>] Extra arguments for the test server binary.
123
+ #
124
+ # @yield Optionally you can provide a block which will ensure that the environment has been
125
+ # shut down after. The newly created {Temporalio::Testing::WorkflowEnvironment} will be
126
+ # passed into the block as a single argument. Alternatively you will need to call
127
+ # {Temporalio::Testing::WorkflowEnvironment#shutdown} explicitly after you're done with it.
128
+ #
129
+ # @return [Temporalio::Testing::WorkflowEnvironment] The newly started TestServer workflow
130
+ # environment.
131
+ def start_time_skipping_environment(
132
+ port: nil,
133
+ download_dir: nil,
134
+ test_server_existing_path: nil,
135
+ test_server_download_version: 'default',
136
+ test_server_extra_args: [],
137
+ &block
138
+ )
139
+ # TODO: Use interceptors to inject a time skipping WorkflowHandle.
140
+ runtime = Temporalio::Runtime.instance
141
+ server = Temporalio::Bridge::TestServer.start(
142
+ runtime.core_runtime,
143
+ test_server_existing_path,
144
+ 'sdk-ruby',
145
+ Temporalio::VERSION,
146
+ test_server_download_version,
147
+ download_dir,
148
+ port,
149
+ test_server_extra_args,
150
+ )
151
+ env = init_workflow_environment_for(server, DEFAULT_NAMESPACE)
152
+
153
+ return env unless block
154
+
155
+ run_server(server, env, &block)
156
+ end
157
+
158
+ private
159
+
160
+ def init_workflow_environment_for(server, namespace)
161
+ connection = Temporalio::Connection.new(server.target)
162
+ Temporalio::Testing::WorkflowEnvironment.new(server, connection, namespace)
163
+ rescue Temporalio::Bridge::Error # Shutdown if unable to connect to the server
164
+ server.shutdown
165
+ raise
166
+ end
167
+
168
+ def run_server(server, env, &block)
169
+ block.call(env)
170
+ ensure
171
+ server.shutdown
172
+ end
173
+ end
174
+ end
175
+ end
@@ -1,3 +1,3 @@
1
1
  module Temporalio
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  end
@@ -3,6 +3,7 @@ require 'temporalio/activity/context'
3
3
  require 'temporalio/activity/info'
4
4
  require 'temporalio/error/failure'
5
5
  require 'temporalio/errors'
6
+ require 'temporalio/interceptor/activity_inbound'
6
7
 
7
8
  module Temporalio
8
9
  class Worker
@@ -12,20 +13,39 @@ module Temporalio
12
13
  #
13
14
  # @api private
14
15
  class ActivityRunner
15
- def initialize(activity_class, start, task_queue, task_token, worker, converter)
16
+ def initialize(
17
+ activity_class,
18
+ start,
19
+ task_queue,
20
+ task_token,
21
+ worker,
22
+ converter,
23
+ inbound_interceptors,
24
+ outbound_interceptors
25
+ )
16
26
  @activity_class = activity_class
17
27
  @start = start
18
28
  @task_queue = task_queue
19
29
  @task_token = task_token
20
30
  @worker = worker
21
31
  @converter = converter
32
+ @inbound_interceptors = inbound_interceptors
33
+ @outbound_interceptors = outbound_interceptors
22
34
  end
23
35
 
24
36
  def run
25
37
  activity = activity_class.new(context)
26
- input = converter.from_payload_array(start.input.to_a)
38
+ args = converter.from_payload_array(start.input.to_a)
39
+ headers = converter.from_payload_map(start.header_fields)
40
+ input = Temporalio::Interceptor::ActivityInbound::ExecuteActivityInput.new(
41
+ activity: activity_class,
42
+ args: args,
43
+ headers: headers || {},
44
+ )
27
45
 
28
- result = activity.execute(*input)
46
+ result = inbound_interceptors.invoke(:execute_activity, input) do |i|
47
+ activity.execute(*i.args)
48
+ end
29
49
 
30
50
  converter.to_payload(result)
31
51
  rescue StandardError => e
@@ -48,7 +68,8 @@ module Temporalio
48
68
 
49
69
  private
50
70
 
51
- attr_reader :activity_class, :start, :task_queue, :task_token, :worker, :converter
71
+ attr_reader :activity_class, :start, :task_queue, :task_token, :worker, :converter,
72
+ :inbound_interceptors, :outbound_interceptors
52
73
 
53
74
  def context
54
75
  return @context if @context
@@ -57,6 +78,7 @@ module Temporalio
57
78
  @context = Temporalio::Activity::Context.new(
58
79
  generate_activity_info,
59
80
  heartbeat_proc,
81
+ outbound_interceptors,
60
82
  shielded: activity_class._shielded,
61
83
  )
62
84
  end