conductor_ruby 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 (143) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +142 -0
  3. data/LICENSE +190 -0
  4. data/README.md +517 -0
  5. data/examples/agentic_workflows/llm_chat.rb +106 -0
  6. data/examples/dynamic_workflow.rb +177 -0
  7. data/examples/event_handler.rb +94 -0
  8. data/examples/event_listener_examples.rb +430 -0
  9. data/examples/helloworld/greetings_worker.rb +24 -0
  10. data/examples/helloworld/helloworld.rb +99 -0
  11. data/examples/kitchensink.rb +213 -0
  12. data/examples/metadata_journey.rb +189 -0
  13. data/examples/metrics_example.rb +284 -0
  14. data/examples/new_dsl_demo.rb +141 -0
  15. data/examples/orkes/http_poll.rb +83 -0
  16. data/examples/orkes/secrets_example.rb +69 -0
  17. data/examples/orkes/wait_for_webhook.rb +90 -0
  18. data/examples/prompt_journey.rb +245 -0
  19. data/examples/rag_workflow.rb +167 -0
  20. data/examples/schedule_journey.rb +244 -0
  21. data/examples/simple_worker.rb +125 -0
  22. data/examples/simple_workflow.rb +89 -0
  23. data/examples/task_context_example.rb +257 -0
  24. data/examples/task_listener_example.rb +192 -0
  25. data/examples/worker_configuration_example.rb +282 -0
  26. data/examples/workflow_dsl.rb +316 -0
  27. data/examples/workflow_ops.rb +305 -0
  28. data/lib/conductor/client/authorization_client.rb +238 -0
  29. data/lib/conductor/client/integration_client.rb +108 -0
  30. data/lib/conductor/client/metadata_client.rb +139 -0
  31. data/lib/conductor/client/prompt_client.rb +58 -0
  32. data/lib/conductor/client/scheduler_client.rb +132 -0
  33. data/lib/conductor/client/schema_client.rb +32 -0
  34. data/lib/conductor/client/secret_client.rb +48 -0
  35. data/lib/conductor/client/task_client.rb +168 -0
  36. data/lib/conductor/client/workflow_client.rb +242 -0
  37. data/lib/conductor/configuration/authentication_settings.rb +17 -0
  38. data/lib/conductor/configuration.rb +103 -0
  39. data/lib/conductor/exceptions.rb +86 -0
  40. data/lib/conductor/http/api/application_resource_api.rb +107 -0
  41. data/lib/conductor/http/api/authorization_resource_api.rb +56 -0
  42. data/lib/conductor/http/api/event_resource_api.rb +133 -0
  43. data/lib/conductor/http/api/gateway_auth_resource_api.rb +48 -0
  44. data/lib/conductor/http/api/group_resource_api.rb +76 -0
  45. data/lib/conductor/http/api/integration_resource_api.rb +145 -0
  46. data/lib/conductor/http/api/metadata_resource_api.rb +231 -0
  47. data/lib/conductor/http/api/prompt_resource_api.rb +81 -0
  48. data/lib/conductor/http/api/role_resource_api.rb +60 -0
  49. data/lib/conductor/http/api/scheduler_resource_api.rb +211 -0
  50. data/lib/conductor/http/api/schema_resource_api.rb +82 -0
  51. data/lib/conductor/http/api/secret_resource_api.rb +134 -0
  52. data/lib/conductor/http/api/task_resource_api.rb +321 -0
  53. data/lib/conductor/http/api/token_resource_api.rb +42 -0
  54. data/lib/conductor/http/api/user_resource_api.rb +59 -0
  55. data/lib/conductor/http/api/workflow_bulk_resource_api.rb +91 -0
  56. data/lib/conductor/http/api/workflow_resource_api.rb +451 -0
  57. data/lib/conductor/http/api_client.rb +437 -0
  58. data/lib/conductor/http/models/authentication_config.rb +67 -0
  59. data/lib/conductor/http/models/authorization_request.rb +39 -0
  60. data/lib/conductor/http/models/base_model.rb +162 -0
  61. data/lib/conductor/http/models/bulk_response.rb +39 -0
  62. data/lib/conductor/http/models/conductor_application.rb +39 -0
  63. data/lib/conductor/http/models/conductor_user.rb +53 -0
  64. data/lib/conductor/http/models/create_or_update_application_request.rb +24 -0
  65. data/lib/conductor/http/models/create_or_update_role_request.rb +27 -0
  66. data/lib/conductor/http/models/event_handler.rb +130 -0
  67. data/lib/conductor/http/models/generate_token_request.rb +27 -0
  68. data/lib/conductor/http/models/group.rb +36 -0
  69. data/lib/conductor/http/models/integration.rb +70 -0
  70. data/lib/conductor/http/models/integration_api.rb +53 -0
  71. data/lib/conductor/http/models/integration_api_update.rb +43 -0
  72. data/lib/conductor/http/models/integration_update.rb +36 -0
  73. data/lib/conductor/http/models/permission.rb +24 -0
  74. data/lib/conductor/http/models/poll_data.rb +33 -0
  75. data/lib/conductor/http/models/prompt_template.rb +59 -0
  76. data/lib/conductor/http/models/prompt_template_test_request.rb +43 -0
  77. data/lib/conductor/http/models/rerun_workflow_request.rb +37 -0
  78. data/lib/conductor/http/models/role.rb +27 -0
  79. data/lib/conductor/http/models/schema_def.rb +59 -0
  80. data/lib/conductor/http/models/search_result.rb +187 -0
  81. data/lib/conductor/http/models/skip_task_request.rb +27 -0
  82. data/lib/conductor/http/models/start_workflow_request.rb +68 -0
  83. data/lib/conductor/http/models/subject_ref.rb +35 -0
  84. data/lib/conductor/http/models/tag_object.rb +36 -0
  85. data/lib/conductor/http/models/target_ref.rb +39 -0
  86. data/lib/conductor/http/models/task.rb +156 -0
  87. data/lib/conductor/http/models/task_def.rb +95 -0
  88. data/lib/conductor/http/models/task_exec_log.rb +30 -0
  89. data/lib/conductor/http/models/task_result.rb +115 -0
  90. data/lib/conductor/http/models/task_result_status.rb +24 -0
  91. data/lib/conductor/http/models/token.rb +33 -0
  92. data/lib/conductor/http/models/upsert_group_request.rb +30 -0
  93. data/lib/conductor/http/models/upsert_user_request.rb +39 -0
  94. data/lib/conductor/http/models/workflow.rb +202 -0
  95. data/lib/conductor/http/models/workflow_def.rb +73 -0
  96. data/lib/conductor/http/models/workflow_schedule.rb +100 -0
  97. data/lib/conductor/http/models/workflow_state_update.rb +30 -0
  98. data/lib/conductor/http/models/workflow_status_constants.rb +57 -0
  99. data/lib/conductor/http/models/workflow_task.rb +169 -0
  100. data/lib/conductor/http/models/workflow_test_request.rb +67 -0
  101. data/lib/conductor/http/rest_client.rb +211 -0
  102. data/lib/conductor/orkes/models/access_key.rb +56 -0
  103. data/lib/conductor/orkes/models/granted_permission.rb +27 -0
  104. data/lib/conductor/orkes/models/metadata_tag.rb +15 -0
  105. data/lib/conductor/orkes/models/rate_limit_tag.rb +15 -0
  106. data/lib/conductor/orkes/orkes_clients.rb +69 -0
  107. data/lib/conductor/version.rb +5 -0
  108. data/lib/conductor/worker/events/conductor_event.rb +40 -0
  109. data/lib/conductor/worker/events/global_dispatcher.rb +37 -0
  110. data/lib/conductor/worker/events/http_events.rb +25 -0
  111. data/lib/conductor/worker/events/listener_registry.rb +40 -0
  112. data/lib/conductor/worker/events/listeners.rb +34 -0
  113. data/lib/conductor/worker/events/sync_event_dispatcher.rb +78 -0
  114. data/lib/conductor/worker/events/task_runner_events.rb +271 -0
  115. data/lib/conductor/worker/events/workflow_events.rb +49 -0
  116. data/lib/conductor/worker/fiber_executor.rb +532 -0
  117. data/lib/conductor/worker/ractor_task_runner.rb +501 -0
  118. data/lib/conductor/worker/task_context.rb +114 -0
  119. data/lib/conductor/worker/task_definition_registrar.rb +322 -0
  120. data/lib/conductor/worker/task_handler.rb +360 -0
  121. data/lib/conductor/worker/task_in_progress.rb +60 -0
  122. data/lib/conductor/worker/task_runner.rb +538 -0
  123. data/lib/conductor/worker/telemetry/metrics_collector.rb +196 -0
  124. data/lib/conductor/worker/telemetry/prometheus_backend.rb +224 -0
  125. data/lib/conductor/worker/worker.rb +355 -0
  126. data/lib/conductor/worker/worker_config.rb +154 -0
  127. data/lib/conductor/worker/worker_registry.rb +71 -0
  128. data/lib/conductor/workflow/dsl/input_ref.rb +37 -0
  129. data/lib/conductor/workflow/dsl/output_ref.rb +44 -0
  130. data/lib/conductor/workflow/dsl/parallel_builder.rb +49 -0
  131. data/lib/conductor/workflow/dsl/switch_builder.rb +74 -0
  132. data/lib/conductor/workflow/dsl/task_ref.rb +178 -0
  133. data/lib/conductor/workflow/dsl/workflow_builder.rb +1016 -0
  134. data/lib/conductor/workflow/dsl/workflow_definition.rb +150 -0
  135. data/lib/conductor/workflow/llm/chat_message.rb +47 -0
  136. data/lib/conductor/workflow/llm/embedding_model.rb +19 -0
  137. data/lib/conductor/workflow/llm/tool_call.rb +43 -0
  138. data/lib/conductor/workflow/llm/tool_spec.rb +46 -0
  139. data/lib/conductor/workflow/task_type.rb +68 -0
  140. data/lib/conductor/workflow/timeout_policy.rb +31 -0
  141. data/lib/conductor/workflow/workflow_executor.rb +373 -0
  142. data/lib/conductor.rb +192 -0
  143. metadata +359 -0
@@ -0,0 +1,242 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+ require_relative '../configuration'
5
+ require_relative '../http/api_client'
6
+ require_relative '../http/api/workflow_resource_api'
7
+
8
+ module Conductor
9
+ module Client
10
+ # WorkflowClient - High-level client for workflow operations
11
+ class WorkflowClient
12
+ attr_reader :workflow_api
13
+
14
+ # Initialize WorkflowClient
15
+ # @param [Configuration] configuration Optional configuration
16
+ def initialize(configuration = nil)
17
+ @configuration = configuration || Configuration.new
18
+ api_client = Http::ApiClient.new(configuration: @configuration)
19
+ @workflow_api = Http::Api::WorkflowResourceApi.new(api_client)
20
+ end
21
+
22
+ # Start a new workflow
23
+ # @param [StartWorkflowRequest] request Start workflow request
24
+ # @return [String] Workflow ID
25
+ def start_workflow(request)
26
+ @workflow_api.start_workflow(request)
27
+ end
28
+
29
+ # Start a workflow with name and input, or with a StartWorkflowRequest
30
+ # @param [String, StartWorkflowRequest] name_or_request Workflow name or StartWorkflowRequest
31
+ # @param [Hash] input Workflow input data (default: {}, ignored if request object passed)
32
+ # @param [Integer] version Workflow version (optional, ignored if request object passed)
33
+ # @param [String] correlation_id Correlation ID (optional, ignored if request object passed)
34
+ # @return [String] Workflow ID
35
+ def start(name_or_request, input: {}, version: nil, correlation_id: nil)
36
+ # Handle both StartWorkflowRequest objects and simple name/input arguments
37
+ if name_or_request.is_a?(Http::Models::StartWorkflowRequest)
38
+ start_workflow(name_or_request)
39
+ else
40
+ request = Http::Models::StartWorkflowRequest.new(
41
+ name: name_or_request,
42
+ input: input,
43
+ version: version,
44
+ correlation_id: correlation_id
45
+ )
46
+ start_workflow(request)
47
+ end
48
+ end
49
+
50
+ # Get workflow execution status
51
+ # @param [String] workflow_id Workflow ID
52
+ # @param [Boolean] include_tasks Include task details (default: true)
53
+ # @return [Workflow] Workflow object
54
+ def get_workflow(workflow_id, include_tasks: true)
55
+ @workflow_api.get_execution_status(workflow_id, include_tasks: include_tasks)
56
+ end
57
+
58
+ # Delete a workflow
59
+ # @param [String] workflow_id Workflow ID
60
+ # @param [Boolean] archive_workflow Archive workflow before deleting (default: true)
61
+ # @return [void]
62
+ def delete_workflow(workflow_id, archive_workflow: true)
63
+ @workflow_api.delete(workflow_id, archive_workflow: archive_workflow)
64
+ end
65
+
66
+ # Terminate a running workflow
67
+ # @param [String] workflow_id Workflow ID
68
+ # @param [String] reason Termination reason (optional)
69
+ # @return [void]
70
+ def terminate_workflow(workflow_id, reason: nil)
71
+ @workflow_api.terminate(workflow_id, reason: reason)
72
+ end
73
+
74
+ # Pause a workflow
75
+ # @param [String] workflow_id Workflow ID
76
+ # @return [void]
77
+ def pause_workflow(workflow_id)
78
+ @workflow_api.pause_workflow(workflow_id)
79
+ end
80
+
81
+ # Resume a paused workflow
82
+ # @param [String] workflow_id Workflow ID
83
+ # @return [void]
84
+ def resume_workflow(workflow_id)
85
+ @workflow_api.resume_workflow(workflow_id)
86
+ end
87
+
88
+ # Restart a completed workflow
89
+ # @param [String] workflow_id Workflow ID
90
+ # @param [Boolean] use_latest_def Use latest workflow definition (default: false)
91
+ # @return [void]
92
+ def restart_workflow(workflow_id, use_latest_def: false)
93
+ @workflow_api.restart(workflow_id, use_latest_def: use_latest_def)
94
+ end
95
+
96
+ # Retry a failed workflow
97
+ # @param [String] workflow_id Workflow ID
98
+ # @param [Boolean] resume_subworkflow_tasks Resume subworkflow tasks (default: false)
99
+ # @return [void]
100
+ def retry_workflow(workflow_id, resume_subworkflow_tasks: false)
101
+ @workflow_api.retry(workflow_id, resume_subworkflow_tasks: resume_subworkflow_tasks)
102
+ end
103
+
104
+ # Rerun a workflow from a specific task
105
+ # @param [String] workflow_id Workflow ID
106
+ # @param [RerunWorkflowRequest] rerun_request Rerun request
107
+ # @return [String] New workflow ID
108
+ def rerun_workflow(workflow_id, rerun_request)
109
+ @workflow_api.rerun(workflow_id, rerun_request)
110
+ end
111
+
112
+ # Get workflows by correlation ID
113
+ # @param [String] name Workflow name
114
+ # @param [String] correlation_id Correlation ID
115
+ # @param [Boolean] include_closed Include closed workflows (default: false)
116
+ # @param [Boolean] include_tasks Include task details (default: false)
117
+ # @return [Array<Workflow>] List of workflows
118
+ def get_by_correlation_id(name, correlation_id, include_closed: false, include_tasks: false)
119
+ @workflow_api.get_workflows(name, correlation_id, include_closed: include_closed, include_tasks: include_tasks)
120
+ end
121
+
122
+ # Get running workflows by name
123
+ # @param [String] name Workflow name
124
+ # @param [Integer] version Workflow version (optional)
125
+ # @param [Integer] start_time Start time in epoch millis (optional)
126
+ # @param [Integer] end_time End time in epoch millis (optional)
127
+ # @return [Array<String>] List of workflow IDs
128
+ def get_running_workflows(name, version: nil, start_time: nil, end_time: nil)
129
+ @workflow_api.get_running_workflow(name, version: version, start_time: start_time, end_time: end_time)
130
+ end
131
+
132
+ # Register a workflow definition
133
+ # @param [WorkflowDef] workflow_def Workflow definition to register
134
+ # @param [Boolean] overwrite Overwrite existing definition (default: false)
135
+ # @return [void]
136
+ def register_workflow(workflow_def, overwrite: false)
137
+ @workflow_api.register_workflow(workflow_def, overwrite: overwrite)
138
+ end
139
+
140
+ # Get a workflow definition
141
+ # @param [String] name Workflow name
142
+ # @param [Integer] version Workflow version (optional)
143
+ # @return [WorkflowDef] Workflow definition
144
+ def get_workflow_def(name, version: nil)
145
+ @workflow_api.get_workflow_def(name, version: version)
146
+ end
147
+
148
+ # Delete a workflow definition
149
+ # @param [String] name Workflow name
150
+ # @param [Integer] version Workflow version
151
+ # @return [void]
152
+ def unregister_workflow(name, version:)
153
+ @workflow_api.unregister_workflow(name, version: version)
154
+ end
155
+
156
+ # Execute a workflow synchronously and wait for completion
157
+ # @param [StartWorkflowRequest] request Start workflow request
158
+ # @param [String] request_id Unique request ID (optional, auto-generated)
159
+ # @param [String] wait_until_task_ref Wait until task ref (optional)
160
+ # @param [Integer] wait_for_seconds Max wait time (default: 30)
161
+ # @return [WorkflowRun] Workflow run result
162
+ def execute_workflow(request, request_id: nil, wait_until_task_ref: nil, wait_for_seconds: 30)
163
+ request_id ||= SecureRandom.uuid
164
+ @workflow_api.execute_workflow(
165
+ request,
166
+ name: request.name,
167
+ version: request.version || 1,
168
+ request_id: request_id,
169
+ wait_until_task_ref: wait_until_task_ref,
170
+ wait_for_seconds: wait_for_seconds
171
+ )
172
+ end
173
+
174
+ # Search for workflows
175
+ # @param [Integer] start Start index (default: 0)
176
+ # @param [Integer] size Page size (default: 100)
177
+ # @param [String] free_text Free text search (default: '*')
178
+ # @param [String] query Query string (optional)
179
+ # @return [SearchResult] Search results
180
+ def search(start: 0, size: 100, free_text: '*', query: nil)
181
+ @workflow_api.search(start: start, size: size, free_text: free_text, query: query)
182
+ end
183
+
184
+ # Get workflows by multiple correlation IDs (batch)
185
+ # @param [String] name Workflow name
186
+ # @param [Array<String>] correlation_ids Correlation IDs
187
+ # @param [Boolean] include_closed Include closed workflows (default: false)
188
+ # @param [Boolean] include_tasks Include task details (default: false)
189
+ # @return [Hash<String, Array<Workflow>>]
190
+ def get_by_correlation_ids(name, correlation_ids, include_closed: false, include_tasks: false)
191
+ @workflow_api.get_workflows_batch(name, correlation_ids, include_closed: include_closed,
192
+ include_tasks: include_tasks)
193
+ end
194
+
195
+ # Update workflow variables
196
+ # @param [String] workflow_id Workflow ID
197
+ # @param [Hash] variables Variables to update
198
+ # @return [Workflow]
199
+ def update_variables(workflow_id, variables)
200
+ @workflow_api.update_workflow_state(workflow_id, variables)
201
+ end
202
+
203
+ # Update workflow and task state
204
+ # @param [String] workflow_id Workflow ID
205
+ # @param [WorkflowStateUpdate] state_update State update request
206
+ # @param [String] wait_until_task_ref Wait until task ref (optional)
207
+ # @param [Integer] wait_for_seconds Wait time (default: 10)
208
+ # @return [WorkflowRun]
209
+ def update_state(workflow_id, state_update, wait_until_task_ref: nil, wait_for_seconds: 10)
210
+ @workflow_api.update_workflow_and_task_state(
211
+ workflow_id, state_update,
212
+ wait_until_task_ref: wait_until_task_ref,
213
+ wait_for_seconds: wait_for_seconds
214
+ )
215
+ end
216
+
217
+ # Test a workflow with mocked task outputs
218
+ # @param [WorkflowTestRequest] request Test request
219
+ # @return [Workflow]
220
+ def test_workflow(request)
221
+ @workflow_api.test_workflow(request)
222
+ end
223
+
224
+ # Skip a task in a running workflow
225
+ # @param [String] workflow_id Workflow ID
226
+ # @param [String] task_reference_name Task reference name
227
+ # @param [SkipTaskRequest] request Skip task request (optional)
228
+ # @return [void]
229
+ def skip_task_from_workflow(workflow_id, task_reference_name, request: nil)
230
+ @workflow_api.skip_task_from_workflow(workflow_id, task_reference_name, request: request)
231
+ end
232
+
233
+ # Remove (permanently delete) a workflow
234
+ # @param [String] workflow_id Workflow ID
235
+ # @param [Boolean] archive_workflow Archive before deleting (default: true)
236
+ # @return [void]
237
+ def remove_workflow(workflow_id, archive_workflow: true)
238
+ @workflow_api.delete(workflow_id, archive_workflow: archive_workflow)
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ # Authentication settings for Conductor server
5
+ class AuthenticationSettings
6
+ attr_accessor :key_id, :key_secret
7
+
8
+ def initialize(key_id: nil, key_secret: nil)
9
+ @key_id = key_id || ENV.fetch('CONDUCTOR_AUTH_KEY', nil)
10
+ @key_secret = key_secret || ENV.fetch('CONDUCTOR_AUTH_SECRET', nil)
11
+ end
12
+
13
+ def configured?
14
+ !key_id.nil? && !key_secret.nil? && !key_id.empty? && !key_secret.empty?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/authentication_settings'
4
+
5
+ module Conductor
6
+ # Configuration for Conductor client
7
+ class Configuration
8
+ # Class-level auth token cache (shared across instances, like Python SDK)
9
+ @auth_token = nil
10
+ @token_update_time = 0
11
+
12
+ class << self
13
+ attr_accessor :auth_token, :token_update_time
14
+ end
15
+
16
+ attr_accessor :base_url, :server_api_url, :debug, :authentication_settings,
17
+ :verify_ssl, :ssl_ca_cert, :cert_file, :key_file,
18
+ :proxy, :auth_token_ttl_min
19
+
20
+ attr_reader :host, :ui_host
21
+
22
+ def initialize(base_url: nil, server_api_url: nil, debug: false,
23
+ authentication_settings: nil, auth_key: nil, auth_secret: nil,
24
+ auth_token_ttl_min: 45, verify_ssl: true)
25
+ @debug = debug
26
+ @verify_ssl = verify_ssl
27
+ @ssl_ca_cert = nil
28
+ @cert_file = nil
29
+ @key_file = nil
30
+ @proxy = nil
31
+ @auth_token_ttl_min = auth_token_ttl_min
32
+
33
+ # Resolve server URL
34
+ @host = resolve_host(server_api_url, base_url)
35
+ @ui_host = resolve_ui_host
36
+
37
+ # Resolve authentication
38
+ @authentication_settings = resolve_auth_settings(authentication_settings, auth_key, auth_secret)
39
+ end
40
+
41
+ def auth_token_ttl_msec
42
+ @auth_token_ttl_min * 60 * 1000
43
+ end
44
+
45
+ def auth_configured?
46
+ @authentication_settings&.configured? || false
47
+ end
48
+
49
+ def disable_auth!
50
+ @authentication_settings = nil
51
+ end
52
+
53
+ def update_token(token)
54
+ self.class.auth_token = token
55
+ self.class.token_update_time = (Time.now.to_f * 1000).to_i
56
+ end
57
+
58
+ def auth_token
59
+ self.class.auth_token
60
+ end
61
+
62
+ def token_update_time
63
+ self.class.token_update_time
64
+ end
65
+
66
+ # Alias for server URL (used in some places)
67
+ def server_url
68
+ @host
69
+ end
70
+
71
+ private
72
+
73
+ def resolve_host(server_api_url, base_url)
74
+ return server_api_url if server_api_url
75
+ return "#{base_url}/api" if base_url
76
+
77
+ # Fall back to environment variable or default
78
+ env_url = ENV.fetch('CONDUCTOR_SERVER_URL', nil)
79
+ return env_url if env_url
80
+
81
+ 'http://localhost:8080/api'
82
+ end
83
+
84
+ def resolve_ui_host
85
+ env_ui_url = ENV.fetch('CONDUCTOR_UI_SERVER_URL', nil)
86
+ return env_ui_url if env_ui_url
87
+
88
+ # Derive UI host from API host
89
+ @host.sub(%r{/api$}, '')
90
+ end
91
+
92
+ def resolve_auth_settings(auth_settings, auth_key, auth_secret)
93
+ return auth_settings if auth_settings
94
+
95
+ # Try explicit parameters first
96
+ return AuthenticationSettings.new(key_id: auth_key, key_secret: auth_secret) if auth_key && auth_secret
97
+
98
+ # Try environment variables
99
+ settings = AuthenticationSettings.new
100
+ settings.configured? ? settings : nil
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Conductor
4
+ # Base exception for all Conductor errors
5
+ class ConductorError < StandardError; end
6
+
7
+ # Configuration error (invalid settings, missing dependencies)
8
+ class ConfigurationError < ConductorError; end
9
+
10
+ # API-level exception (HTTP errors from server)
11
+ class ApiError < ConductorError
12
+ attr_reader :status, :code, :reason, :body, :headers
13
+
14
+ def initialize(message = nil, status: nil, code: nil, reason: nil, body: nil, headers: nil)
15
+ @status = status
16
+ @code = code || status
17
+ @reason = reason
18
+ @body = body
19
+ @headers = headers
20
+ super(message || build_message)
21
+ end
22
+
23
+ def not_found?
24
+ @code == 404
25
+ end
26
+
27
+ private
28
+
29
+ def build_message
30
+ msg = "(#{@status}) #{@reason}"
31
+ msg += "\nBody: #{@body}" if @body
32
+ msg
33
+ end
34
+ end
35
+
36
+ # Authorization error (401/403 with special token handling)
37
+ class AuthorizationError < ApiError
38
+ attr_reader :error_code
39
+
40
+ def initialize(message = nil, status: nil, body: nil, headers: nil)
41
+ @error_code = parse_error_code(body)
42
+ super(message, status: status, body: body, headers: headers)
43
+ end
44
+
45
+ def token_expired?
46
+ @error_code == 'EXPIRED_TOKEN'
47
+ end
48
+
49
+ def invalid_token?
50
+ @error_code == 'INVALID_TOKEN'
51
+ end
52
+
53
+ private
54
+
55
+ def parse_error_code(body)
56
+ return nil unless body
57
+
58
+ data = begin
59
+ JSON.parse(body)
60
+ rescue StandardError
61
+ nil
62
+ end
63
+ data&.dig('error') || ''
64
+ end
65
+
66
+ def build_message
67
+ msg = "Authorization error: #{@error_code} (status: #{@status})"
68
+ msg += "\nReason: #{@reason}" if @reason
69
+ msg += "\nBody: #{@body}" if @body
70
+ msg
71
+ end
72
+ end
73
+
74
+ # Non-retryable worker error (terminal failure)
75
+ class NonRetryableError < ConductorError; end
76
+
77
+ # Task in progress (not an error - for long-running tasks)
78
+ class TaskInProgress
79
+ attr_reader :callback_after_seconds, :output
80
+
81
+ def initialize(callback_after: 60, output: {})
82
+ @callback_after_seconds = callback_after
83
+ @output = output
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../api_client'
4
+
5
+ module Conductor
6
+ module Http
7
+ module Api
8
+ # ApplicationResourceApi - API for application management operations (Orkes)
9
+ class ApplicationResourceApi
10
+ attr_accessor :api_client
11
+
12
+ def initialize(api_client = nil)
13
+ @api_client = api_client || ApiClient.new
14
+ end
15
+
16
+ # Create an application
17
+ def create_application(body)
18
+ @api_client.call_api('/applications', 'POST', body: body, return_type: 'ConductorApplication',
19
+ return_http_data_only: true)
20
+ end
21
+
22
+ # Get an application by ID
23
+ def get_application(id)
24
+ @api_client.call_api('/applications/{id}', 'GET', path_params: { id: id },
25
+ return_type: 'ConductorApplication', return_http_data_only: true)
26
+ end
27
+
28
+ # List all applications
29
+ def list_applications
30
+ @api_client.call_api('/applications', 'GET', return_type: 'Array<ConductorApplication>',
31
+ return_http_data_only: true)
32
+ end
33
+
34
+ # Update an application
35
+ def update_application(body, id)
36
+ @api_client.call_api('/applications/{id}', 'PUT', path_params: { id: id }, body: body,
37
+ return_type: 'ConductorApplication', return_http_data_only: true)
38
+ end
39
+
40
+ # Delete an application
41
+ def delete_application(id)
42
+ @api_client.call_api('/applications/{id}', 'DELETE', path_params: { id: id }, return_http_data_only: true)
43
+ end
44
+
45
+ # Add a role to an application
46
+ def add_role_to_application_user(application_id, role)
47
+ @api_client.call_api('/applications/{applicationId}/roles/{role}', 'POST',
48
+ path_params: { applicationId: application_id, role: role }, return_http_data_only: true)
49
+ end
50
+
51
+ # Remove a role from an application
52
+ def remove_role_from_application_user(application_id, role)
53
+ @api_client.call_api('/applications/{applicationId}/roles/{role}', 'DELETE',
54
+ path_params: { applicationId: application_id, role: role }, return_http_data_only: true)
55
+ end
56
+
57
+ # Set tags for an application
58
+ def put_tags_for_application(tags, id)
59
+ @api_client.call_api('/applications/{id}/tags', 'PUT', path_params: { id: id }, body: tags,
60
+ return_http_data_only: true)
61
+ end
62
+
63
+ # Get tags for an application
64
+ def get_tags_for_application(id)
65
+ @api_client.call_api('/applications/{id}/tags', 'GET', path_params: { id: id },
66
+ return_type: 'Array<TagObject>', return_http_data_only: true)
67
+ end
68
+
69
+ # Delete tags for an application
70
+ def delete_tags_for_application(tags, id)
71
+ @api_client.call_api('/applications/{id}/tags', 'DELETE', path_params: { id: id }, body: tags,
72
+ return_http_data_only: true)
73
+ end
74
+
75
+ # Create an access key for an application
76
+ def create_access_key(id)
77
+ @api_client.call_api('/applications/{id}/accessKeys', 'POST', path_params: { id: id }, return_type: 'Object',
78
+ return_http_data_only: true)
79
+ end
80
+
81
+ # Get access keys for an application
82
+ def get_access_keys(id)
83
+ @api_client.call_api('/applications/{id}/accessKeys', 'GET', path_params: { id: id },
84
+ return_type: 'Array<Object>', return_http_data_only: true)
85
+ end
86
+
87
+ # Toggle access key status
88
+ def toggle_access_key_status(application_id, key_id)
89
+ @api_client.call_api('/applications/{applicationId}/accessKeys/{keyId}/status', 'POST',
90
+ path_params: { applicationId: application_id, keyId: key_id }, return_type: 'Object', return_http_data_only: true)
91
+ end
92
+
93
+ # Delete an access key
94
+ def delete_access_key(application_id, key_id)
95
+ @api_client.call_api('/applications/{applicationId}/accessKeys/{keyId}', 'DELETE',
96
+ path_params: { applicationId: application_id, keyId: key_id }, return_http_data_only: true)
97
+ end
98
+
99
+ # Get application by access key ID
100
+ def get_app_by_access_key_id(access_key_id)
101
+ @api_client.call_api('/applications/key/{accessKeyId}', 'GET', path_params: { accessKeyId: access_key_id },
102
+ return_type: 'Object', return_http_data_only: true)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../api_client'
4
+
5
+ module Conductor
6
+ module Http
7
+ module Api
8
+ # AuthorizationResourceApi - API for permission management operations (Orkes)
9
+ class AuthorizationResourceApi
10
+ attr_accessor :api_client
11
+
12
+ def initialize(api_client = nil)
13
+ @api_client = api_client || ApiClient.new
14
+ end
15
+
16
+ # Grant permissions
17
+ # @param [AuthorizationRequest] body Authorization request
18
+ # @return [void]
19
+ def grant_permissions(body)
20
+ @api_client.call_api(
21
+ '/auth/authorization',
22
+ 'POST',
23
+ body: body,
24
+ return_http_data_only: true
25
+ )
26
+ end
27
+
28
+ # Get permissions for a target
29
+ # @param [String] type Target type
30
+ # @param [String] id Target ID
31
+ # @return [Hash]
32
+ def get_permissions(type, id)
33
+ @api_client.call_api(
34
+ '/auth/authorization/{type}/{id}',
35
+ 'GET',
36
+ path_params: { type: type, id: id },
37
+ return_type: 'Object',
38
+ return_http_data_only: true
39
+ )
40
+ end
41
+
42
+ # Remove permissions
43
+ # @param [AuthorizationRequest] body Authorization request
44
+ # @return [void]
45
+ def remove_permissions(body)
46
+ @api_client.call_api(
47
+ '/auth/authorization',
48
+ 'DELETE',
49
+ body: body,
50
+ return_http_data_only: true
51
+ )
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end