tasker-rb 0.1.1

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/DEVELOPMENT.md +548 -0
  3. data/README.md +87 -0
  4. data/ext/tasker_core/Cargo.lock +4720 -0
  5. data/ext/tasker_core/Cargo.toml +76 -0
  6. data/ext/tasker_core/extconf.rb +38 -0
  7. data/ext/tasker_core/src/CLAUDE.md +7 -0
  8. data/ext/tasker_core/src/bootstrap.rs +320 -0
  9. data/ext/tasker_core/src/bridge.rs +400 -0
  10. data/ext/tasker_core/src/client_ffi.rs +173 -0
  11. data/ext/tasker_core/src/conversions.rs +131 -0
  12. data/ext/tasker_core/src/diagnostics.rs +57 -0
  13. data/ext/tasker_core/src/event_handler.rs +179 -0
  14. data/ext/tasker_core/src/event_publisher_ffi.rs +239 -0
  15. data/ext/tasker_core/src/ffi_logging.rs +245 -0
  16. data/ext/tasker_core/src/global_event_system.rs +16 -0
  17. data/ext/tasker_core/src/in_process_event_ffi.rs +319 -0
  18. data/ext/tasker_core/src/lib.rs +41 -0
  19. data/ext/tasker_core/src/observability_ffi.rs +339 -0
  20. data/lib/tasker_core/batch_processing/batch_aggregation_scenario.rb +85 -0
  21. data/lib/tasker_core/batch_processing/batch_worker_context.rb +238 -0
  22. data/lib/tasker_core/bootstrap.rb +394 -0
  23. data/lib/tasker_core/domain_events/base_publisher.rb +220 -0
  24. data/lib/tasker_core/domain_events/base_subscriber.rb +178 -0
  25. data/lib/tasker_core/domain_events/publisher_registry.rb +253 -0
  26. data/lib/tasker_core/domain_events/subscriber_registry.rb +152 -0
  27. data/lib/tasker_core/domain_events.rb +43 -0
  28. data/lib/tasker_core/errors/CLAUDE.md +7 -0
  29. data/lib/tasker_core/errors/common.rb +305 -0
  30. data/lib/tasker_core/errors/error_classifier.rb +61 -0
  31. data/lib/tasker_core/errors.rb +4 -0
  32. data/lib/tasker_core/event_bridge.rb +330 -0
  33. data/lib/tasker_core/handlers.rb +159 -0
  34. data/lib/tasker_core/internal.rb +31 -0
  35. data/lib/tasker_core/logger.rb +234 -0
  36. data/lib/tasker_core/models.rb +337 -0
  37. data/lib/tasker_core/observability/types.rb +158 -0
  38. data/lib/tasker_core/observability.rb +292 -0
  39. data/lib/tasker_core/registry/handler_registry.rb +453 -0
  40. data/lib/tasker_core/registry/resolver_chain.rb +258 -0
  41. data/lib/tasker_core/registry/resolvers/base_resolver.rb +90 -0
  42. data/lib/tasker_core/registry/resolvers/class_constant_resolver.rb +156 -0
  43. data/lib/tasker_core/registry/resolvers/explicit_mapping_resolver.rb +146 -0
  44. data/lib/tasker_core/registry/resolvers/method_dispatch_wrapper.rb +144 -0
  45. data/lib/tasker_core/registry/resolvers/registry_resolver.rb +229 -0
  46. data/lib/tasker_core/registry/resolvers.rb +42 -0
  47. data/lib/tasker_core/registry.rb +12 -0
  48. data/lib/tasker_core/step_handler/api.rb +48 -0
  49. data/lib/tasker_core/step_handler/base.rb +354 -0
  50. data/lib/tasker_core/step_handler/batchable.rb +50 -0
  51. data/lib/tasker_core/step_handler/decision.rb +53 -0
  52. data/lib/tasker_core/step_handler/mixins/api.rb +452 -0
  53. data/lib/tasker_core/step_handler/mixins/batchable.rb +465 -0
  54. data/lib/tasker_core/step_handler/mixins/decision.rb +252 -0
  55. data/lib/tasker_core/step_handler/mixins.rb +66 -0
  56. data/lib/tasker_core/subscriber.rb +212 -0
  57. data/lib/tasker_core/task_handler/base.rb +254 -0
  58. data/lib/tasker_core/tasker_rb.so +0 -0
  59. data/lib/tasker_core/template_discovery.rb +181 -0
  60. data/lib/tasker_core/tracing.rb +166 -0
  61. data/lib/tasker_core/types/batch_processing_outcome.rb +301 -0
  62. data/lib/tasker_core/types/client_types.rb +145 -0
  63. data/lib/tasker_core/types/decision_point_outcome.rb +177 -0
  64. data/lib/tasker_core/types/error_types.rb +72 -0
  65. data/lib/tasker_core/types/simple_message.rb +151 -0
  66. data/lib/tasker_core/types/step_context.rb +328 -0
  67. data/lib/tasker_core/types/step_handler_call_result.rb +307 -0
  68. data/lib/tasker_core/types/step_message.rb +112 -0
  69. data/lib/tasker_core/types/step_types.rb +207 -0
  70. data/lib/tasker_core/types/task_template.rb +240 -0
  71. data/lib/tasker_core/types/task_types.rb +148 -0
  72. data/lib/tasker_core/types.rb +132 -0
  73. data/lib/tasker_core/version.rb +13 -0
  74. data/lib/tasker_core/worker/CLAUDE.md +7 -0
  75. data/lib/tasker_core/worker/event_poller.rb +224 -0
  76. data/lib/tasker_core/worker/in_process_domain_event_poller.rb +271 -0
  77. data/lib/tasker_core.rb +160 -0
  78. metadata +322 -0
@@ -0,0 +1,292 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'observability/types'
5
+
6
+ module TaskerCore
7
+ # Observability - Access worker health, metrics, and configuration via FFI
8
+ #
9
+ # TAS-77: This module provides Ruby-friendly access to worker observability data
10
+ # without requiring the HTTP web API. Data is fetched directly from Rust via FFI.
11
+ #
12
+ # @example Check worker health
13
+ # health = TaskerCore::Observability.health_basic
14
+ # puts health.status # => "healthy"
15
+ # puts health.worker_id
16
+ #
17
+ # @example Get detailed health for K8s probes
18
+ # if TaskerCore::Observability.ready?
19
+ # puts "Worker ready to receive requests"
20
+ # end
21
+ #
22
+ # @example Get metrics
23
+ # events = TaskerCore::Observability.event_stats
24
+ # puts "Events routed: #{events.router.total_routed}"
25
+ #
26
+ # @example Query configuration
27
+ # config = TaskerCore::Observability.config
28
+ # puts "Environment: #{config.metadata.environment}"
29
+ #
30
+ # @example List templates
31
+ # templates_json = TaskerCore::Observability.templates_list
32
+ # templates = JSON.parse(templates_json)
33
+ #
34
+ module Observability
35
+ class << self
36
+ # ========================================================================
37
+ # Health Methods
38
+ # ========================================================================
39
+
40
+ # Get basic health status
41
+ #
42
+ # @return [Types::BasicHealth] Basic health response
43
+ def health_basic
44
+ json = TaskerCore::FFI.health_basic
45
+ data = JSON.parse(json)
46
+ Types::BasicHealth.new(
47
+ status: data['status'],
48
+ worker_id: data['worker_id'],
49
+ timestamp: data['timestamp']
50
+ )
51
+ end
52
+
53
+ # Get liveness status (Kubernetes liveness probe)
54
+ #
55
+ # @return [Types::BasicHealth] Liveness response
56
+ def health_live
57
+ json = TaskerCore::FFI.health_live
58
+ data = JSON.parse(json)
59
+ Types::BasicHealth.new(
60
+ status: data['status'],
61
+ worker_id: data['worker_id'],
62
+ timestamp: data['timestamp']
63
+ )
64
+ end
65
+
66
+ # Get readiness status (Kubernetes readiness probe)
67
+ #
68
+ # @return [Types::DetailedHealth] Detailed health with checks
69
+ def health_ready
70
+ json = TaskerCore::FFI.health_ready
71
+ parse_detailed_health(json)
72
+ end
73
+
74
+ # Get detailed health information
75
+ #
76
+ # @return [Types::DetailedHealth] Comprehensive health data
77
+ def health_detailed
78
+ json = TaskerCore::FFI.health_detailed
79
+ parse_detailed_health(json)
80
+ end
81
+
82
+ # Check if worker is ready to receive requests
83
+ #
84
+ # @return [Boolean] true if worker is ready
85
+ def ready?
86
+ health_ready.status == 'healthy'
87
+ rescue StandardError
88
+ false
89
+ end
90
+
91
+ # Check if worker is alive
92
+ #
93
+ # @return [Boolean] true if worker is alive
94
+ def alive?
95
+ # Liveness probe returns 'alive' status, not 'healthy'
96
+ health_live.status == 'alive'
97
+ rescue StandardError
98
+ false
99
+ end
100
+
101
+ # ========================================================================
102
+ # Metrics Methods
103
+ # ========================================================================
104
+
105
+ # Get worker metrics as JSON string
106
+ #
107
+ # @return [String] JSON-formatted metrics
108
+ def metrics_worker
109
+ TaskerCore::FFI.metrics_worker
110
+ end
111
+
112
+ # Get domain event statistics
113
+ #
114
+ # @return [Types::DomainEventStats] Event routing statistics
115
+ def event_stats
116
+ json = TaskerCore::FFI.metrics_events
117
+ data = JSON.parse(json)
118
+
119
+ Types::DomainEventStats.new(
120
+ router: Types::EventRouterStats.new(
121
+ total_routed: data['router']['total_routed'],
122
+ durable_routed: data['router']['durable_routed'],
123
+ fast_routed: data['router']['fast_routed'],
124
+ broadcast_routed: data['router']['broadcast_routed'],
125
+ fast_delivery_errors: data['router']['fast_delivery_errors'],
126
+ routing_errors: data['router']['routing_errors']
127
+ ),
128
+ in_process_bus: Types::InProcessEventBusStats.new(
129
+ total_events_dispatched: data['in_process_bus']['total_events_dispatched'],
130
+ rust_handler_dispatches: data['in_process_bus']['rust_handler_dispatches'],
131
+ ffi_channel_dispatches: data['in_process_bus']['ffi_channel_dispatches'],
132
+ rust_handler_errors: data['in_process_bus']['rust_handler_errors'],
133
+ ffi_channel_drops: data['in_process_bus']['ffi_channel_drops'],
134
+ rust_subscriber_patterns: data['in_process_bus']['rust_subscriber_patterns'],
135
+ rust_handler_count: data['in_process_bus']['rust_handler_count'],
136
+ ffi_subscriber_count: data['in_process_bus']['ffi_subscriber_count']
137
+ ),
138
+ captured_at: data['captured_at'],
139
+ worker_id: data['worker_id']
140
+ )
141
+ end
142
+
143
+ # Get Prometheus-formatted metrics
144
+ #
145
+ # @return [String] Prometheus text format metrics
146
+ def prometheus_metrics
147
+ TaskerCore::FFI.metrics_prometheus
148
+ end
149
+
150
+ # ========================================================================
151
+ # Template Methods
152
+ # ========================================================================
153
+
154
+ # List all templates as JSON
155
+ #
156
+ # @param include_cache_stats [Boolean] Include cache statistics
157
+ # @return [String] JSON-formatted template list
158
+ def templates_list(include_cache_stats: false)
159
+ TaskerCore::FFI.templates_list(include_cache_stats)
160
+ end
161
+
162
+ # Get a specific template as JSON
163
+ #
164
+ # @param namespace [String] Template namespace
165
+ # @param name [String] Template name
166
+ # @param version [String] Template version
167
+ # @return [String] JSON-formatted template
168
+ def template_get(namespace:, name:, version:)
169
+ TaskerCore::FFI.template_get(namespace, name, version)
170
+ end
171
+
172
+ # Validate a template for worker execution
173
+ #
174
+ # @param namespace [String] Template namespace
175
+ # @param name [String] Template name
176
+ # @param version [String] Template version
177
+ # @return [Types::TemplateValidation] Validation result
178
+ def template_validate(namespace:, name:, version:)
179
+ json = TaskerCore::FFI.template_validate(namespace, name, version)
180
+ data = JSON.parse(json)
181
+
182
+ Types::TemplateValidation.new(
183
+ valid: data['valid'],
184
+ namespace: data['namespace'],
185
+ name: data['name'],
186
+ version: data['version'],
187
+ handler_count: data['handler_count'],
188
+ issues: data['issues'] || [],
189
+ handler_metadata: data['handler_metadata']
190
+ )
191
+ end
192
+
193
+ # Get template cache statistics
194
+ #
195
+ # @return [Types::CacheStats] Cache statistics
196
+ def cache_stats
197
+ json = TaskerCore::FFI.templates_cache_stats
198
+ data = JSON.parse(json)
199
+
200
+ # Map Rust field names to Ruby struct names
201
+ Types::CacheStats.new(
202
+ total_entries: data['total_cached'] || 0,
203
+ hits: data['cache_hits'] || 0,
204
+ misses: data['cache_misses'] || 0,
205
+ evictions: data['cache_evictions'] || 0,
206
+ last_maintenance: nil # Not provided by Rust API
207
+ )
208
+ end
209
+
210
+ # TAS-169: Removed cache_clear and template_refresh methods.
211
+ # Cache operations are now internal-only. Restart worker to refresh templates.
212
+ # Distributed cache status is available via health_detailed.
213
+
214
+ # ========================================================================
215
+ # Config Methods
216
+ # ========================================================================
217
+
218
+ # Get runtime configuration (safe fields only, no secrets)
219
+ #
220
+ # @return [Types::RuntimeConfig] Configuration data
221
+ def config
222
+ json = TaskerCore::FFI.config_runtime
223
+ data = JSON.parse(json)
224
+
225
+ Types::RuntimeConfig.new(
226
+ metadata: Types::ConfigMetadata.new(
227
+ timestamp: data['metadata']['timestamp'],
228
+ environment: data['metadata']['environment'],
229
+ version: data['metadata']['version']
230
+ ),
231
+ worker_id: data['worker_id'],
232
+ worker_type: data['worker_type'],
233
+ auth: Types::SafeAuthConfig.new(
234
+ enabled: data['auth']['enabled'],
235
+ verification_method: data['auth']['verification_method'],
236
+ jwt_issuer: data['auth']['jwt_issuer'],
237
+ jwt_audience: data['auth']['jwt_audience'],
238
+ api_key_header: data['auth']['api_key_header'],
239
+ api_key_count: data['auth']['api_key_count'],
240
+ strict_validation: data['auth']['strict_validation'],
241
+ allowed_algorithms: data['auth']['allowed_algorithms']
242
+ ),
243
+ messaging: Types::SafeMessagingConfig.new(
244
+ backend: data['messaging']['backend'],
245
+ queues: data['messaging']['queues']
246
+ )
247
+ )
248
+ end
249
+
250
+ # Get the current environment name
251
+ #
252
+ # @return [String] Environment name
253
+ def environment
254
+ TaskerCore::FFI.config_environment
255
+ end
256
+
257
+ private
258
+
259
+ # Parse detailed health JSON into typed struct
260
+ def parse_detailed_health(json)
261
+ data = JSON.parse(json)
262
+
263
+ checks = data['checks'].transform_values do |check|
264
+ Types::HealthCheck.new(
265
+ status: check['status'],
266
+ message: check['message'],
267
+ duration_ms: check['duration_ms'],
268
+ last_checked: check['last_checked']
269
+ )
270
+ end
271
+
272
+ system_info = Types::WorkerSystemInfo.new(
273
+ version: data['system_info']['version'],
274
+ environment: data['system_info']['environment'],
275
+ uptime_seconds: data['system_info']['uptime_seconds'],
276
+ worker_type: data['system_info']['worker_type'],
277
+ database_pool_size: data['system_info']['database_pool_size'],
278
+ command_processor_active: data['system_info']['command_processor_active'],
279
+ supported_namespaces: data['system_info']['supported_namespaces']
280
+ )
281
+
282
+ Types::DetailedHealth.new(
283
+ status: data['status'],
284
+ timestamp: data['timestamp'],
285
+ worker_id: data['worker_id'],
286
+ checks: checks,
287
+ system_info: system_info
288
+ )
289
+ end
290
+ end
291
+ end
292
+ end