temporalio 0.2.0-aarch64-linux
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/Gemfile +23 -0
- data/Rakefile +387 -0
- data/lib/temporalio/activity/complete_async_error.rb +11 -0
- data/lib/temporalio/activity/context.rb +107 -0
- data/lib/temporalio/activity/definition.rb +77 -0
- data/lib/temporalio/activity/info.rb +63 -0
- data/lib/temporalio/activity.rb +69 -0
- data/lib/temporalio/api/batch/v1/message.rb +31 -0
- data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +93 -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 +36 -0
- data/lib/temporalio/api/cloud/namespace/v1/message.rb +35 -0
- data/lib/temporalio/api/cloud/operation/v1/message.rb +27 -0
- data/lib/temporalio/api/cloud/region/v1/message.rb +23 -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 +41 -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/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 +42 -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/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 +189 -0
- data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
- data/lib/temporalio/api/workflowservice.rb +3 -0
- data/lib/temporalio/api.rb +13 -0
- data/lib/temporalio/cancellation.rb +150 -0
- data/lib/temporalio/client/activity_id_reference.rb +32 -0
- data/lib/temporalio/client/async_activity_handle.rb +110 -0
- data/lib/temporalio/client/connection/cloud_service.rb +648 -0
- data/lib/temporalio/client/connection/operator_service.rb +249 -0
- data/lib/temporalio/client/connection/service.rb +41 -0
- data/lib/temporalio/client/connection/workflow_service.rb +1218 -0
- data/lib/temporalio/client/connection.rb +270 -0
- data/lib/temporalio/client/interceptor.rb +316 -0
- data/lib/temporalio/client/workflow_execution.rb +103 -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 +446 -0
- data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
- data/lib/temporalio/client/workflow_update_handle.rb +67 -0
- data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
- data/lib/temporalio/client.rb +404 -0
- data/lib/temporalio/common_enums.rb +24 -0
- data/lib/temporalio/converters/data_converter.rb +102 -0
- data/lib/temporalio/converters/failure_converter.rb +200 -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 +62 -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 +73 -0
- data/lib/temporalio/converters.rb +9 -0
- data/lib/temporalio/error/failure.rb +219 -0
- data/lib/temporalio/error.rb +147 -0
- data/lib/temporalio/internal/bridge/3.1/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/3.2/temporalio_bridge.so +0 -0
- data/lib/temporalio/internal/bridge/3.3/temporalio_bridge.so +0 -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 +36 -0
- data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
- data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +52 -0
- data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +54 -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 +90 -0
- data/lib/temporalio/internal/bridge/runtime.rb +53 -0
- data/lib/temporalio/internal/bridge/testing.rb +46 -0
- data/lib/temporalio/internal/bridge/worker.rb +83 -0
- data/lib/temporalio/internal/bridge.rb +36 -0
- data/lib/temporalio/internal/client/implementation.rb +525 -0
- data/lib/temporalio/internal/proto_utils.rb +54 -0
- data/lib/temporalio/internal/worker/activity_worker.rb +345 -0
- data/lib/temporalio/internal/worker/multi_runner.rb +169 -0
- data/lib/temporalio/internal.rb +7 -0
- data/lib/temporalio/retry_policy.rb +51 -0
- data/lib/temporalio/runtime.rb +271 -0
- data/lib/temporalio/scoped_logger.rb +96 -0
- data/lib/temporalio/search_attributes.rb +300 -0
- data/lib/temporalio/testing/activity_environment.rb +132 -0
- data/lib/temporalio/testing/workflow_environment.rb +137 -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 +254 -0
- data/lib/temporalio/worker/activity_executor.rb +55 -0
- data/lib/temporalio/worker/interceptor.rb +88 -0
- data/lib/temporalio/worker/tuner.rb +151 -0
- data/lib/temporalio/worker.rb +426 -0
- data/lib/temporalio/workflow_history.rb +22 -0
- data/lib/temporalio.rb +7 -0
- data/temporalio.gemspec +28 -0
- metadata +191 -0
@@ -0,0 +1,271 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'temporalio/internal/bridge/runtime'
|
4
|
+
|
5
|
+
module Temporalio
|
6
|
+
# Runtime for Temporal Ruby SDK.
|
7
|
+
#
|
8
|
+
# Only one global {Runtime} needs to exist. Users are encouraged to use {default}. To configure it, create a runtime
|
9
|
+
# before any clients are created, and set it via {default=}. Every time a new runtime is created, a new internal Rust
|
10
|
+
# thread pool is created.
|
11
|
+
class Runtime
|
12
|
+
# Telemetry options for the runtime.
|
13
|
+
#
|
14
|
+
# @!attribute logging
|
15
|
+
# @return [LoggingOptions, nil] Logging options, default is new {LoggingOptions} with no parameters. Can be set
|
16
|
+
# to nil to disable logging.
|
17
|
+
# @!attribute metrics
|
18
|
+
# @return [MetricsOptions, nil] Metrics options.
|
19
|
+
TelemetryOptions = Struct.new(
|
20
|
+
:logging,
|
21
|
+
:metrics,
|
22
|
+
keyword_init: true
|
23
|
+
) do
|
24
|
+
# @!visibility private
|
25
|
+
def initialize(**kwargs)
|
26
|
+
# @type var kwargs: untyped
|
27
|
+
kwargs[:logging] = LoggingOptions.new unless kwargs.key?(:logging)
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!visibility private
|
32
|
+
def _to_bridge
|
33
|
+
# @type self: TelemetryOptions
|
34
|
+
Internal::Bridge::Runtime::TelemetryOptions.new(
|
35
|
+
logging: logging&._to_bridge,
|
36
|
+
metrics: metrics&._to_bridge
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Logging options for runtime telemetry.
|
42
|
+
#
|
43
|
+
# @!attribute log_filter
|
44
|
+
# @return [LoggingFilterOptions, String] Logging filter for Core, default is new {LoggingFilterOptions} with no
|
45
|
+
# parameters.
|
46
|
+
LoggingOptions = Struct.new(
|
47
|
+
:log_filter,
|
48
|
+
# TODO(cretz): forward_to
|
49
|
+
keyword_init: true
|
50
|
+
) do
|
51
|
+
# @!visibility private
|
52
|
+
def initialize(**kwargs)
|
53
|
+
# @type var kwargs: untyped
|
54
|
+
kwargs[:log_filter] = LoggingFilterOptions.new unless kwargs.key?(:log_filter)
|
55
|
+
super
|
56
|
+
end
|
57
|
+
|
58
|
+
# @!visibility private
|
59
|
+
def _to_bridge
|
60
|
+
# @type self: LoggingOptions
|
61
|
+
Internal::Bridge::Runtime::LoggingOptions.new(
|
62
|
+
log_filter: if log_filter.is_a?(String)
|
63
|
+
log_filter
|
64
|
+
elsif log_filter.is_a?(LoggingFilterOptions)
|
65
|
+
log_filter._to_bridge
|
66
|
+
else
|
67
|
+
raise 'Log filter must be string or LoggingFilterOptions'
|
68
|
+
end
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Logging filter options for Core.
|
74
|
+
#
|
75
|
+
# @!attribute core_level
|
76
|
+
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for Core log messages, default is +'WARN'+.
|
77
|
+
# @!attribute other_level
|
78
|
+
# @return ['TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'] Log level for other Rust log messages, default is +'WARN'+.
|
79
|
+
LoggingFilterOptions = Struct.new(
|
80
|
+
:core_level,
|
81
|
+
:other_level,
|
82
|
+
keyword_init: true
|
83
|
+
) do
|
84
|
+
# @!visibility private
|
85
|
+
def initialize(**kwargs)
|
86
|
+
# @type var kwargs: untyped
|
87
|
+
kwargs[:core_level] = 'WARN' unless kwargs.key?(:core_level)
|
88
|
+
kwargs[:other_level] = 'ERROR' unless kwargs.key?(:other_level)
|
89
|
+
super
|
90
|
+
end
|
91
|
+
|
92
|
+
# @!visibility private
|
93
|
+
def _to_bridge
|
94
|
+
# @type self: LoggingFilterOptions
|
95
|
+
"#{other_level},temporal_sdk_core=#{core_level},temporal_client=#{core_level},temporal_sdk=#{core_level}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Metrics options for runtime telemetry. Either {opentelemetry} or {prometheus} required, but not both.
|
100
|
+
#
|
101
|
+
# @!attribute opentelemetry
|
102
|
+
# @return [OpenTelemetryMetricsOptions, nil] OpenTelemetry options if using OpenTelemetry. This is mutually
|
103
|
+
# exclusive with +prometheus+.
|
104
|
+
# @!attribute prometheus
|
105
|
+
# @return [PrometheusMetricsOptions, nil] Prometheus options if using Prometheus. This is mutually exclusive with
|
106
|
+
# +opentelemetry+.
|
107
|
+
# @!attribute attach_service_name
|
108
|
+
# @return [Boolean] Whether to put the service_name on every metric, default +true+.
|
109
|
+
# @!attribute global_tags
|
110
|
+
# @return [Hash<String, String>, nil] Resource tags to be applied to all metrics.
|
111
|
+
# @!attribute metric_prefix
|
112
|
+
# @return [String, nil] Prefix to put on every Temporal metric. If unset, defaults to +temporal_+.
|
113
|
+
MetricsOptions = Struct.new(
|
114
|
+
:opentelemetry,
|
115
|
+
:prometheus,
|
116
|
+
:attach_service_name,
|
117
|
+
:global_tags,
|
118
|
+
:metric_prefix,
|
119
|
+
keyword_init: true
|
120
|
+
) do
|
121
|
+
# @!visibility private
|
122
|
+
def initialize(**kwargs)
|
123
|
+
# @type var kwargs: untyped
|
124
|
+
kwargs[:attach_service_name] = true unless kwargs.key?(:attach_service_name)
|
125
|
+
super
|
126
|
+
end
|
127
|
+
|
128
|
+
# @!visibility private
|
129
|
+
def _to_bridge
|
130
|
+
# @type self: MetricsOptions
|
131
|
+
Internal::Bridge::Runtime::MetricsOptions.new(
|
132
|
+
opentelemetry: opentelemetry&._to_bridge,
|
133
|
+
prometheus: prometheus&._to_bridge,
|
134
|
+
attach_service_name:,
|
135
|
+
global_tags:,
|
136
|
+
metric_prefix:
|
137
|
+
)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Options for exporting metrics to OpenTelemetry.
|
142
|
+
#
|
143
|
+
# @!attribute url
|
144
|
+
# @return [String] URL for OpenTelemetry endpoint.
|
145
|
+
# @!attribute headers
|
146
|
+
# @return [Hash<String, String>, nil] Headers for OpenTelemetry endpoint.
|
147
|
+
# @!attribute metric_periodicity
|
148
|
+
# @return [Float, nil] How frequently metrics should be exported, unset uses internal default.
|
149
|
+
# @!attribute metric_temporality
|
150
|
+
# @return [MetricTemporality] How frequently metrics should be exported, default is
|
151
|
+
# {MetricTemporality::CUMULATIVE}.
|
152
|
+
# @!attribute durations_as_seconds
|
153
|
+
# @return [Boolean] Whether to use float seconds instead of integer milliseconds for durations, default is
|
154
|
+
# +false+.
|
155
|
+
OpenTelemetryMetricsOptions = Struct.new(
|
156
|
+
:url,
|
157
|
+
:headers,
|
158
|
+
:metric_periodicity,
|
159
|
+
:metric_temporality,
|
160
|
+
:durations_as_seconds,
|
161
|
+
keyword_init: true
|
162
|
+
) do
|
163
|
+
# OpenTelemetry metric temporality.
|
164
|
+
module MetricTemporality # rubocop:disable Lint/ConstantDefinitionInBlock
|
165
|
+
CUMULATIVE = 1
|
166
|
+
DELTA = 2
|
167
|
+
end
|
168
|
+
|
169
|
+
# @!visibility private
|
170
|
+
def initialize(**kwargs)
|
171
|
+
# @type var kwargs: untyped
|
172
|
+
kwargs[:metric_temporality] = MetricTemporality::CUMULATIVE unless kwargs.key?(:metric_temporality)
|
173
|
+
kwargs[:durations_as_seconds] = false unless kwargs.key?(:durations_as_seconds)
|
174
|
+
super
|
175
|
+
end
|
176
|
+
|
177
|
+
# @!visibility private
|
178
|
+
def _to_bridge
|
179
|
+
# @type self: OpenTelemetryMetricsOptions
|
180
|
+
Internal::Bridge::Runtime::OpenTelemetryMetricsOptions.new(
|
181
|
+
url:,
|
182
|
+
headers:,
|
183
|
+
metric_periodicity:,
|
184
|
+
metric_temporality_delta: case metric_temporality
|
185
|
+
when MetricTemporality::CUMULATIVE then false
|
186
|
+
when MetricTemporality::DELTA then true
|
187
|
+
else raise 'Unrecognized metric temporality'
|
188
|
+
end,
|
189
|
+
durations_as_seconds:
|
190
|
+
)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Options for exporting metrics to Prometheus.
|
195
|
+
#
|
196
|
+
# @!attribute bind_address
|
197
|
+
# @return [String] Address to bind to for Prometheus endpoint.
|
198
|
+
# @!attribute counters_total_suffix
|
199
|
+
# @return [Boolean] If +true+, all counters will include a +_total+ suffix, default is +false+.
|
200
|
+
# @!attribute unit_suffix
|
201
|
+
# @return [Boolean] If +true+, all histograms will include the unit in their name as a suffix, default is +false+.
|
202
|
+
# @!attribute durations_as_seconds
|
203
|
+
# @return [Boolean] Whether to use float seconds instead of integer milliseconds for durations, default is
|
204
|
+
# +false+.
|
205
|
+
PrometheusMetricsOptions = Struct.new(
|
206
|
+
:bind_address,
|
207
|
+
:counters_total_suffix,
|
208
|
+
:unit_suffix,
|
209
|
+
:durations_as_seconds,
|
210
|
+
keyword_init: true
|
211
|
+
) do
|
212
|
+
# @!visibility private
|
213
|
+
def initialize(**kwargs)
|
214
|
+
# @type var kwargs: untyped
|
215
|
+
kwargs[:counters_total_suffix] = false unless kwargs.key?(:counters_total_suffix)
|
216
|
+
kwargs[:unit_suffix] = false unless kwargs.key?(:unit_suffix)
|
217
|
+
kwargs[:durations_as_seconds] = false unless kwargs.key?(:durations_as_seconds)
|
218
|
+
super
|
219
|
+
end
|
220
|
+
|
221
|
+
# @!visibility private
|
222
|
+
def _to_bridge
|
223
|
+
# @type self: PrometheusMetricsOptions
|
224
|
+
Internal::Bridge::Runtime::PrometheusMetricsOptions.new(
|
225
|
+
bind_address:,
|
226
|
+
counters_total_suffix:,
|
227
|
+
unit_suffix:,
|
228
|
+
durations_as_seconds:
|
229
|
+
)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Default runtime, lazily created upon first access. If needing a different default, make sure it is updated via
|
234
|
+
# {default=} before this is called (either directly or as a parameter to something like {Client}).
|
235
|
+
#
|
236
|
+
# @return [Runtime] Default runtime.
|
237
|
+
def self.default
|
238
|
+
@default ||= Runtime.new
|
239
|
+
end
|
240
|
+
|
241
|
+
# Set the default runtime. Must be called before {default} accessed.
|
242
|
+
#
|
243
|
+
# @param runtime [Runtime] Runtime to set as default.
|
244
|
+
# @raise If default has already been accessed.
|
245
|
+
def self.default=(runtime)
|
246
|
+
raise 'Runtime already set or requested' unless @default.nil?
|
247
|
+
|
248
|
+
@default = runtime
|
249
|
+
end
|
250
|
+
|
251
|
+
# Create new Runtime. For most users, this should only be done once globally. In addition to creating a Rust thread
|
252
|
+
# pool, this also consumes a Ruby thread for its lifetime.
|
253
|
+
#
|
254
|
+
# @param telemetry [TelemetryOptions] Telemetry options to set.
|
255
|
+
def initialize(telemetry: TelemetryOptions.new)
|
256
|
+
@core_runtime = Internal::Bridge::Runtime.new(
|
257
|
+
Internal::Bridge::Runtime::Options.new(telemetry: telemetry._to_bridge)
|
258
|
+
)
|
259
|
+
# We need a thread to run the command loop
|
260
|
+
# TODO(cretz): Is this something users should be concerned about or need control over?
|
261
|
+
Thread.new do
|
262
|
+
@core_runtime.run_command_loop
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
# @!visibility private
|
267
|
+
def _core_runtime
|
268
|
+
@core_runtime
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
module Temporalio
|
7
|
+
# Implementation via delegator to {::Logger} that puts scoped values on the log message and appends them to the log
|
8
|
+
# message.
|
9
|
+
class ScopedLogger < SimpleDelegator
|
10
|
+
# @!attribute scoped_values_getter
|
11
|
+
# @return [Proc, nil] Proc to call to get scoped values when needed.
|
12
|
+
attr_accessor :scoped_values_getter
|
13
|
+
|
14
|
+
# @!attribute scoped_values_getter
|
15
|
+
# @return [Boolean] Whether the scoped value appending is disabled.
|
16
|
+
attr_accessor :disable_scoped_values
|
17
|
+
|
18
|
+
# @see Logger.add
|
19
|
+
def add(severity, message = nil, progname = nil)
|
20
|
+
return true if (severity || Logger::Unknown) < level
|
21
|
+
return super if scoped_values_getter.nil? || @disable_scoped_values
|
22
|
+
|
23
|
+
scoped_values = scoped_values_getter.call
|
24
|
+
return super if scoped_values.nil?
|
25
|
+
|
26
|
+
if message.nil?
|
27
|
+
if block_given?
|
28
|
+
message = yield
|
29
|
+
else
|
30
|
+
message = progname
|
31
|
+
progname = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
# For exceptions we need to dup and append here, for everything else we
|
35
|
+
# need to delegate to a log message
|
36
|
+
new_message = if message.is_a?(Exception)
|
37
|
+
message.exception("#{message.message} #{scoped_values}")
|
38
|
+
else
|
39
|
+
LogMessage.new(message, scoped_values)
|
40
|
+
end
|
41
|
+
super(severity, new_message, progname)
|
42
|
+
end
|
43
|
+
alias log add
|
44
|
+
|
45
|
+
# @see Logger.debug
|
46
|
+
def debug(progname = nil, &)
|
47
|
+
add(Logger::DEBUG, nil, progname, &)
|
48
|
+
end
|
49
|
+
|
50
|
+
# @see Logger.info
|
51
|
+
def info(progname = nil, &)
|
52
|
+
add(Logger::INFO, nil, progname, &)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @see Logger.warn
|
56
|
+
def warn(progname = nil, &)
|
57
|
+
add(Logger::WARN, nil, progname, &)
|
58
|
+
end
|
59
|
+
|
60
|
+
# @see Logger.error
|
61
|
+
def error(progname = nil, &)
|
62
|
+
add(Logger::ERROR, nil, progname, &)
|
63
|
+
end
|
64
|
+
|
65
|
+
# @see Logger.fatal
|
66
|
+
def fatal(progname = nil, &)
|
67
|
+
add(Logger::FATAL, nil, progname, &)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @see Logger.unknown
|
71
|
+
def unknown(progname = nil, &)
|
72
|
+
add(Logger::UNKNOWN, nil, progname, &)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Scoped log message wrapping original log message.
|
76
|
+
class LogMessage
|
77
|
+
# @return [Object] Original log message.
|
78
|
+
attr_reader :message
|
79
|
+
|
80
|
+
# @return [Object] Scoped values.
|
81
|
+
attr_reader :scoped_values
|
82
|
+
|
83
|
+
# @!visibility private
|
84
|
+
def initialize(message, scoped_values)
|
85
|
+
@message = message
|
86
|
+
@scoped_values = scoped_values
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [String] Message with scoped values appended.
|
90
|
+
def inspect
|
91
|
+
message_str = message.is_a?(String) ? message : message.inspect
|
92
|
+
"#{message_str} #{scoped_values}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,300 @@
|
|
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 frozen.
|
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
|
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
|
+
end
|
108
|
+
|
109
|
+
# @!visibility private
|
110
|
+
def self.from_proto(proto)
|
111
|
+
return nil unless proto
|
112
|
+
raise ArgumentError, 'Expected proto search attribute' unless proto.is_a?(Api::Common::V1::SearchAttributes)
|
113
|
+
|
114
|
+
SearchAttributes.new(proto.indexed_fields.map do |key_name, payload| # rubocop:disable Style/MapToHash
|
115
|
+
key = Key.new(key_name, IndexedValueType::PROTO_VALUES[payload.metadata['type']])
|
116
|
+
value = value_from_payload(payload)
|
117
|
+
[key, value]
|
118
|
+
end.to_h)
|
119
|
+
end
|
120
|
+
|
121
|
+
# @!visibility private
|
122
|
+
def self.value_from_payload(payload)
|
123
|
+
value = Converters::PayloadConverter.default.from_payload(payload)
|
124
|
+
# Time needs to be converted
|
125
|
+
value = Time.iso8601(value) if payload.metadata['type'] == 'DateTime' && value.is_a?(String)
|
126
|
+
value
|
127
|
+
end
|
128
|
+
|
129
|
+
# Create a search attribute collection.
|
130
|
+
#
|
131
|
+
# @param existing [SearchAttributes, Hash<Key, Object>, nil] Existing collection. This can be another
|
132
|
+
# {SearchAttributes} instance or a {::Hash}.
|
133
|
+
def initialize(existing = nil)
|
134
|
+
if existing.nil?
|
135
|
+
@raw_hash = {}
|
136
|
+
elsif existing.is_a?(SearchAttributes)
|
137
|
+
@raw_hash = existing.to_h
|
138
|
+
elsif existing.is_a?(Hash)
|
139
|
+
@raw_hash = {}
|
140
|
+
existing.each { |key, value| self[key] = value }
|
141
|
+
else
|
142
|
+
raise ArgumentError, 'Existing must be nil, a SearchAttributes instance, or a valid Hash'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Set a search attribute value for a key. This will replace any existing value for the {Key#name }regardless of
|
147
|
+
# {Key#type}.
|
148
|
+
#
|
149
|
+
# @param key [Key] A key to set. This must be a {Key} and the value must be proper for the {Key#type}.
|
150
|
+
# @param value [Object, nil] The value to set. If `nil`, the key is removed. The value must be proper for the `key`.
|
151
|
+
def []=(key, value)
|
152
|
+
# Key must be a Key
|
153
|
+
raise ArgumentError, 'Key must be a key' unless key.is_a?(Key)
|
154
|
+
|
155
|
+
key.validate_value(value) unless value.nil?
|
156
|
+
|
157
|
+
# Remove any key with the same name and set
|
158
|
+
delete(key)
|
159
|
+
# We only set the value if it's non-nil, otherwise it's a delete
|
160
|
+
@raw_hash[key] = value unless value.nil?
|
161
|
+
end
|
162
|
+
|
163
|
+
# Get a search attribute value for a key.
|
164
|
+
#
|
165
|
+
# @param key [Key, String] The key to find. If this is a {Key}, it will use key equality (i.e. name and type) to
|
166
|
+
# search. If this is a {::String}, the type is not checked when finding the proper key.
|
167
|
+
# @return [Object, nil] Value if found or `nil` if not.
|
168
|
+
def [](key)
|
169
|
+
# Key must be a Key or a string
|
170
|
+
if key.is_a?(Key)
|
171
|
+
@raw_hash[key]
|
172
|
+
elsif key.is_a?(String)
|
173
|
+
@raw_hash.find { |hash_key, _| hash_key.name == key }&.last
|
174
|
+
else
|
175
|
+
raise ArgumentError, 'Key must be a key or string'
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Delete a search attribute key
|
180
|
+
#
|
181
|
+
# @param key [Key, String] The key to delete. Regardless of whether this is a {Key} or a {::String}, the key with
|
182
|
+
# the matching name will be deleted. This means a {Key} with a matching name but different type may be deleted.
|
183
|
+
def delete(key)
|
184
|
+
# Key must be a Key or a string, but we delete all values for the
|
185
|
+
# name no matter what
|
186
|
+
name = if key.is_a?(Key)
|
187
|
+
key.name
|
188
|
+
elsif key.is_a?(String)
|
189
|
+
key
|
190
|
+
else
|
191
|
+
raise ArgumentError, 'Key must be a key or string'
|
192
|
+
end
|
193
|
+
@raw_hash.delete_if { |hash_key, _| hash_key.name == name }
|
194
|
+
end
|
195
|
+
|
196
|
+
# Like {::Hash#each}.
|
197
|
+
def each(&)
|
198
|
+
@raw_hash.each(&)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @return [Hash<Key, Object>] Copy of the search attributes as a hash.
|
202
|
+
def to_h
|
203
|
+
@raw_hash.dup
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [SearchAttributes] Copy of the search attributes.
|
207
|
+
def dup
|
208
|
+
SearchAttributes.new(self)
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [Boolean] Whether the set of attributes is empty.
|
212
|
+
def empty?
|
213
|
+
length.zero?
|
214
|
+
end
|
215
|
+
|
216
|
+
# @return [Integer] Number of attributes.
|
217
|
+
def length
|
218
|
+
@raw_hash.length
|
219
|
+
end
|
220
|
+
|
221
|
+
alias size length
|
222
|
+
|
223
|
+
# Return a new search attributes collection with updates applied.
|
224
|
+
#
|
225
|
+
# @param updates [Update] Updates created via {Key#value_set} or {Key#value_unset}.
|
226
|
+
# @return [SearchAttributes] New collection.
|
227
|
+
def update(*updates)
|
228
|
+
attrs = dup
|
229
|
+
attrs.update!(*updates)
|
230
|
+
attrs
|
231
|
+
end
|
232
|
+
|
233
|
+
# Update this search attribute collection with given updates.
|
234
|
+
#
|
235
|
+
# @param updates [Update] Updates created via {Key#value_set} or {Key#value_unset}.
|
236
|
+
def update!(*updates)
|
237
|
+
updates.each do |update|
|
238
|
+
raise ArgumentError, 'Update must be an update' unless update.is_a?(Update)
|
239
|
+
|
240
|
+
self[update.key] = update.value
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# @!visibility private
|
245
|
+
def to_proto
|
246
|
+
Api::Common::V1::SearchAttributes.new(
|
247
|
+
indexed_fields: @raw_hash.to_h do |key, value|
|
248
|
+
# We use a default converter, but if type is a time, we need ISO format
|
249
|
+
value = value.iso8601 if key.type == IndexedValueType::TIME
|
250
|
+
|
251
|
+
# Convert to payload
|
252
|
+
payload = Converters::PayloadConverter.default.to_payload(value)
|
253
|
+
payload.metadata['type'] = IndexedValueType::PROTO_NAMES[key.type]
|
254
|
+
|
255
|
+
[key.name, payload]
|
256
|
+
end
|
257
|
+
)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Type for a search attribute key/value.
|
261
|
+
#
|
262
|
+
# @see https://docs.temporal.io/visibility#supported-types
|
263
|
+
module IndexedValueType
|
264
|
+
# Text type, values must be {::String}.
|
265
|
+
TEXT = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_TEXT
|
266
|
+
|
267
|
+
# Keyword type, values must be {::String}.
|
268
|
+
KEYWORD = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_KEYWORD
|
269
|
+
|
270
|
+
# Integer type, values must be {::Integer}.
|
271
|
+
INTEGER = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_INT
|
272
|
+
|
273
|
+
# Float type, values must be {::Float} or {::Integer}.
|
274
|
+
FLOAT = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_DOUBLE
|
275
|
+
|
276
|
+
# Boolean type, values must be {::TrueClass} or {::FalseClass}.
|
277
|
+
BOOLEAN = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_BOOL
|
278
|
+
|
279
|
+
# Time type, values must be {::Time}.
|
280
|
+
TIME = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_DATETIME
|
281
|
+
|
282
|
+
# Keyword list type, values must be {::Array<String>}.
|
283
|
+
KEYWORD_LIST = Api::Enums::V1::IndexedValueType::INDEXED_VALUE_TYPE_KEYWORD_LIST
|
284
|
+
|
285
|
+
# @!visibility private
|
286
|
+
PROTO_NAMES = {
|
287
|
+
TEXT => 'Text',
|
288
|
+
KEYWORD => 'Keyword',
|
289
|
+
INTEGER => 'Int',
|
290
|
+
FLOAT => 'Double',
|
291
|
+
BOOLEAN => 'Bool',
|
292
|
+
TIME => 'DateTime',
|
293
|
+
KEYWORD_LIST => 'KeywordList'
|
294
|
+
}.freeze
|
295
|
+
|
296
|
+
# @!visibility private
|
297
|
+
PROTO_VALUES = PROTO_NAMES.invert.freeze
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|