forest_admin_agent 1.30.7 → 1.31.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.
- checksums.yaml +4 -4
- data/lib/forest_admin_agent/auth/oauth2/forest_resource_owner.rb +1 -1
- data/lib/forest_admin_agent/http/router.rb +2 -1
- data/lib/forest_admin_agent/routes/workflow/workflow_executor_proxy.rb +144 -0
- data/lib/forest_admin_agent/utils/schema/schema_emitter.rb +1 -1
- data/lib/forest_admin_agent/version.rb +1 -1
- metadata +3 -3
- data/sig/forest_admin_agent/routes/abstract_route.rbs +0 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0734ce0f980bab395e86e186c571c177f69542ed1784004a848df4f6bddf345
|
|
4
|
+
data.tar.gz: 87f57b3ea71d7c1c67597be08d15835e0387a81ac65fff8988bab4821cd74208
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ef22c59d7a92eb560bee230de72590c5f3ddd3ddb278ae45215fd09f9e503e7f4aecffd2174abab274bea1c75fd74bc5eb33d1120ee28e53355f6b6b38c114f2
|
|
7
|
+
data.tar.gz: 3133bc41d2eb45f5c948007a161312b1ab2f760c6dfaadc1852453a15877e3eca54489bbc9400cbf51d450822a09dceca624bf261d65f7bb222b72f4fe077cc0
|
|
@@ -63,7 +63,8 @@ module ForestAdminAgent
|
|
|
63
63
|
{ name: 'associate_related', handler: -> { Resources::Related::AssociateRelated.new.routes } },
|
|
64
64
|
{ name: 'dissociate_related', handler: -> { Resources::Related::DissociateRelated.new.routes } },
|
|
65
65
|
{ name: 'update_related', handler: -> { Resources::Related::UpdateRelated.new.routes } },
|
|
66
|
-
{ name: 'update_field', handler: -> { Resources::UpdateField.new.routes } }
|
|
66
|
+
{ name: 'update_field', handler: -> { Resources::UpdateField.new.routes } },
|
|
67
|
+
{ name: 'workflow_executor_proxy', handler: -> { Workflow::WorkflowExecutorProxy.new.routes } }
|
|
67
68
|
]
|
|
68
69
|
|
|
69
70
|
all_routes = {}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module ForestAdminAgent
|
|
4
|
+
module Routes
|
|
5
|
+
module Workflow
|
|
6
|
+
# Forwards workflow-execution traffic from the agent to the workflow executor.
|
|
7
|
+
# Mounted only when the integrator sets `workflow_executor_url`
|
|
8
|
+
class WorkflowExecutorProxy < AbstractAuthenticatedRoute
|
|
9
|
+
AGENT_PREFIX = '/_internal/workflow-executions'.freeze
|
|
10
|
+
EXECUTOR_PREFIX = '/runs'.freeze
|
|
11
|
+
FORWARDED_HEADERS = %w[Authorization Cookie].freeze
|
|
12
|
+
ROUTING_KEYS = %w[run_id route_alias controller action format].freeze
|
|
13
|
+
OPEN_TIMEOUT = 2
|
|
14
|
+
GET_TIMEOUT = 10
|
|
15
|
+
TRIGGER_TIMEOUT = 120
|
|
16
|
+
|
|
17
|
+
def setup_routes
|
|
18
|
+
return self unless executor_configured?
|
|
19
|
+
|
|
20
|
+
add_route(
|
|
21
|
+
'forest_workflow_run_show',
|
|
22
|
+
'get',
|
|
23
|
+
"#{AGENT_PREFIX}/:run_id",
|
|
24
|
+
->(args) { handle_request(:get, args) }
|
|
25
|
+
)
|
|
26
|
+
add_route(
|
|
27
|
+
'forest_workflow_run_trigger',
|
|
28
|
+
'post',
|
|
29
|
+
"#{AGENT_PREFIX}/:run_id/trigger",
|
|
30
|
+
->(args) { handle_request(:post, args) }
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def handle_request(method, args = {})
|
|
37
|
+
build(args)
|
|
38
|
+
|
|
39
|
+
base_url = configured_executor_url
|
|
40
|
+
run_id = args.dig(:params, 'run_id') || args.dig(:params, :run_id)
|
|
41
|
+
path = build_path(run_id, method)
|
|
42
|
+
response = forward(method, base_url, path, args)
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
content: response.body,
|
|
46
|
+
status: response.status,
|
|
47
|
+
headers: forwarded_response_headers(response)
|
|
48
|
+
}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def executor_configured?
|
|
54
|
+
url = ForestAdminAgent::Facades::Container.config_from_cache[:workflow_executor_url]
|
|
55
|
+
!(url.nil? || url.to_s.strip.empty?)
|
|
56
|
+
rescue StandardError
|
|
57
|
+
# Container not yet populated (e.g. boot-order edge case): treat as disabled.
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def configured_executor_url
|
|
62
|
+
url = ForestAdminAgent::Facades::Container.config_from_cache[:workflow_executor_url]
|
|
63
|
+
if url.nil? || url.to_s.strip.empty?
|
|
64
|
+
raise Http::Exceptions::NotFoundError, 'Workflow executor proxy is not configured'
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
url.to_s.sub(%r{/+\z}, '')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def build_path(run_id, method)
|
|
71
|
+
suffix = method == :post ? '/trigger' : ''
|
|
72
|
+
"#{EXECUTOR_PREFIX}/#{run_id}#{suffix}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def forward(method, base_url, path, args)
|
|
76
|
+
query = forwarded_query_params(args[:params])
|
|
77
|
+
headers = forwarded_request_headers(args[:headers])
|
|
78
|
+
body = forwarded_body(method, args[:params])
|
|
79
|
+
target_url = "#{base_url}#{path}"
|
|
80
|
+
|
|
81
|
+
client = build_client(timeout_for(method))
|
|
82
|
+
client.run_request(method, target_url, body, headers) do |req|
|
|
83
|
+
req.params.update(query) unless query.empty?
|
|
84
|
+
end
|
|
85
|
+
rescue Faraday::TimeoutError => e
|
|
86
|
+
raise Http::Exceptions::ServiceUnavailableError.new('Workflow executor timed out', cause: e)
|
|
87
|
+
rescue Faraday::ConnectionFailed => e
|
|
88
|
+
raise Http::Exceptions::ServiceUnavailableError.new('Workflow executor unreachable', cause: e)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def timeout_for(method)
|
|
92
|
+
method == :get ? GET_TIMEOUT : TRIGGER_TIMEOUT
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_client(request_timeout)
|
|
96
|
+
Faraday.new(request: { open_timeout: OPEN_TIMEOUT, timeout: request_timeout }) do |f|
|
|
97
|
+
f.request :json
|
|
98
|
+
f.response :json, content_type: /\bjson$/
|
|
99
|
+
f.adapter Faraday.default_adapter
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Strip Rails-injected routing keys; keep only true client query params.
|
|
104
|
+
def forwarded_query_params(params)
|
|
105
|
+
return {} unless params.is_a?(Hash)
|
|
106
|
+
|
|
107
|
+
params.each_with_object({}) do |(key, value), acc|
|
|
108
|
+
next if ROUTING_KEYS.include?(key.to_s)
|
|
109
|
+
next if value.is_a?(Hash) || value.is_a?(Array) # 'data' body, etc.
|
|
110
|
+
|
|
111
|
+
acc[key.to_s] = value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def forwarded_request_headers(headers)
|
|
116
|
+
return {} unless headers.is_a?(Hash)
|
|
117
|
+
|
|
118
|
+
FORWARDED_HEADERS.each_with_object({}) do |name, acc|
|
|
119
|
+
value = headers[name] || headers[name.downcase] || headers["HTTP_#{name.upcase}"]
|
|
120
|
+
acc[name] = value if value && !value.to_s.empty?
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def forwarded_body(method, params)
|
|
125
|
+
return nil if method == :get
|
|
126
|
+
return nil unless params.is_a?(Hash)
|
|
127
|
+
|
|
128
|
+
# JSON request bodies arrive parsed under :data when sent as JSON:API,
|
|
129
|
+
# or as the raw top-level params hash otherwise. Prefer :data when
|
|
130
|
+
# present; fall back to a sanitized copy of params.
|
|
131
|
+
body = params['data'] || params[:data]
|
|
132
|
+
return body if body
|
|
133
|
+
|
|
134
|
+
params.reject { |key, _| ROUTING_KEYS.include?(key.to_s) }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def forwarded_response_headers(response)
|
|
138
|
+
content_type = response.headers['content-type'] || response.headers['Content-Type']
|
|
139
|
+
content_type ? { 'Content-Type' => content_type } : {}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: forest_admin_agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.31.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthieu
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-
|
|
12
|
+
date: 2026-06-08 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activesupport
|
|
@@ -375,6 +375,7 @@ files:
|
|
|
375
375
|
- lib/forest_admin_agent/routes/security/authentication.rb
|
|
376
376
|
- lib/forest_admin_agent/routes/security/scope_invalidation.rb
|
|
377
377
|
- lib/forest_admin_agent/routes/system/health_check.rb
|
|
378
|
+
- lib/forest_admin_agent/routes/workflow/workflow_executor_proxy.rb
|
|
378
379
|
- lib/forest_admin_agent/serializer/forest_chart_serializer.rb
|
|
379
380
|
- lib/forest_admin_agent/serializer/forest_serializer.rb
|
|
380
381
|
- lib/forest_admin_agent/serializer/forest_serializer_override.rb
|
|
@@ -413,7 +414,6 @@ files:
|
|
|
413
414
|
- sig/forest_admin_agent/builder/agent_factory.rbs
|
|
414
415
|
- sig/forest_admin_agent/facades/container.rbs
|
|
415
416
|
- sig/forest_admin_agent/http/router.rbs
|
|
416
|
-
- sig/forest_admin_agent/routes/abstract_route.rbs
|
|
417
417
|
- sig/forest_admin_agent/routes/security/authentication.rbs
|
|
418
418
|
- sig/forest_admin_agent/routes/system/health_check.rbs
|
|
419
419
|
homepage: https://www.forestadmin.com
|