forest_liana 9.20.0 → 9.20.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c37d0a61be7a37b80bab933cf4892cef1aceb25a5f8351d56c66dbd4d07e6c4d
4
- data.tar.gz: d857a4ebb09833f6c99afb44d4daf2e06b396689331c6e28359fe78117e78727
3
+ metadata.gz: 7e6590ae4597f44757af9a98c3f4d8ba9e83652eedd857f45aeb1fe365e8a8ae
4
+ data.tar.gz: 5aa5f3150171dd65dac411007c311b9cb5409c9eb97d1a415d31e908001b0d34
5
5
  SHA512:
6
- metadata.gz: d8e83baf30e64e0c1a0fed567e62c7f1ed8b1763924a813967090616cf703e103b5f53609b0579d811bd286fd107aef17fa50ec14d92dac67ed8cc77cfc51b12
7
- data.tar.gz: d2c6655ba0c37f0a917018e32ac3afdc0bbcbc3de9ebb82bce59cf2e4043a53145c7897184322a1c372ea9e51c223cc03c59714d312b728b7466044bd8a575a1
6
+ metadata.gz: 6d848b970f6f68edd2a162f40e546740878c6fc7e2b298a3c494f6641fce908b41e2412a9e3457d13edef7d6433ce9f5704b77604851b2fd7aeca5eb60cfe691
7
+ data.tar.gz: 2828ff99f57028970cc2ec4e1662b70203d0d077ca0bbb45350dd4d29d09bffaffd1ef907b923e666cfc28704f97db62358bdf6709cf4b7c16bac822cd1afa15
@@ -58,7 +58,7 @@ module ForestLiana
58
58
  begin
59
59
  # action.hooks[:change] is a hashmap here
60
60
  # to do the validation, only the hook names are require
61
- change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
61
+ change_hooks_name = action.hooks && action.hooks[:change] ? action.hooks[:change].keys : nil
62
62
  ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result[:fields], action.name, change_hooks_name)
63
63
  rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
64
64
  FOREST_LOGGER.warn invalid_field_error.message
@@ -107,10 +107,15 @@ module ForestLiana
107
107
  # Get the smart action hook load context
108
108
  context = get_smart_action_load_ctx(action.fields)
109
109
 
110
- # Call the user-defined load hook.
111
- result = action.hooks[:load].(context)
110
+ if action.hooks && action.hooks[:load].present?
111
+ # Call the user-defined load hook.
112
+ result = action.hooks[:load].(context)
112
113
 
113
- handle_result(result, action)
114
+ handle_result(result, action)
115
+ else
116
+ # No load hook declared: answer the static form (Node agent parity) instead of a 404.
117
+ handle_result(context[:fields], action)
118
+ end
114
119
  end
115
120
  end
116
121
 
@@ -1,9 +1,8 @@
1
1
  ForestLiana.apimap.each do |collection|
2
2
  if !collection.actions.empty?
3
3
  collection.actions.each do |action|
4
- if action.hooks && action.hooks[:load].present?
5
- post action.endpoint.sub('forest', '') + '/hooks/load' => 'actions#load', action_name: ActiveSupport::Inflector.parameterize(action.name)
6
- end
4
+ # Unconditional: clients probe /hooks/load even when no load hook is declared.
5
+ post action.endpoint.sub('forest', '') + '/hooks/load' => 'actions#load', action_name: ActiveSupport::Inflector.parameterize(action.name)
7
6
  if action.hooks && action.hooks[:change].present?
8
7
  post action.endpoint.sub('forest', '') + '/hooks/change' => 'actions#change', action_name: ActiveSupport::Inflector.parameterize(action.name)
9
8
  end
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.20.0"
2
+ VERSION = "9.20.1"
3
3
  end
@@ -28,6 +28,19 @@ class Forest::Island
28
28
 
29
29
  action 'test'
30
30
 
31
+ action 'static_form_action',
32
+ fields: [{
33
+ field: 'static_field',
34
+ type: 'String',
35
+ default_value: nil,
36
+ enums: nil,
37
+ is_required: false,
38
+ is_read_only: false,
39
+ reference: nil,
40
+ description: nil,
41
+ widget: nil
42
+ }]
43
+
31
44
  action 'my_action',
32
45
  fields: [foo],
33
46
  hooks: {
@@ -130,6 +130,25 @@ describe 'Requesting Actions routes', :type => :request do
130
130
  expect(response.status).to eq(422)
131
131
  end
132
132
 
133
+ it 'should respond 200 with the static form when the action has no load hook' do
134
+ post '/forest/actions/static_form_action/hooks/load', params: JSON.dump(params), headers: headers
135
+ action = island.actions.select { |action| action.name == 'static_form_action' }.first
136
+ static_field = action.fields.select { |field| field[:field] == 'static_field' }.first
137
+ expect(response.status).to eq(200)
138
+ expect(JSON.parse(response.body)).to eq({'fields' => [static_field.merge({:value => nil}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]})
139
+
140
+ first_body = response.body
141
+ post '/forest/actions/static_form_action/hooks/load', params: JSON.dump(params), headers: headers
142
+ expect(response.status).to eq(200)
143
+ expect(response.body).to eq(first_body)
144
+ end
145
+
146
+ it 'should respond 200 with empty fields when the action has no form at all' do
147
+ post '/forest/actions/test/hooks/load', params: JSON.dump(params), headers: headers
148
+ expect(response.status).to eq(200)
149
+ expect(JSON.parse(response.body)).to eq({'fields' => []})
150
+ end
151
+
133
152
  it 'should respond 500 with bad hook result type' do
134
153
  post '/forest/actions/fail_action/hooks/load', params: JSON.dump(params), headers: headers
135
154
  expect(response.status).to eq(500)
@@ -1,4 +1,7 @@
1
1
  require 'rails_helper'
2
+ require 'zlib'
3
+ require 'stringio'
4
+ require 'socket'
2
5
 
3
6
  describe 'Workflow executor proxy', type: :request do
4
7
  let(:run_id) { 'run_abc123' }
@@ -207,6 +210,53 @@ describe 'Workflow executor proxy', type: :request do
207
210
  end
208
211
  end
209
212
 
213
+ # Real local server + real HTTParty (no stub) to exercise actual gzip decompression.
214
+ describe 'when the executor gzip-compresses the response (nginx in front)' do
215
+ let(:gz_body) do
216
+ io = StringIO.new
217
+ writer = Zlib::GzipWriter.new(io)
218
+ writer.write({ 'id' => run_id, 'state' => 'pending' }.to_json)
219
+ writer.close
220
+ io.string
221
+ end
222
+
223
+ around do |example|
224
+ server = TCPServer.new('127.0.0.1', 0)
225
+ port = server.addr[1]
226
+ thread = Thread.new do
227
+ client = server.accept
228
+ client.readpartial(4096)
229
+ client.write(
230
+ "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n" \
231
+ "Content-Encoding: gzip\r\nContent-Length: #{gz_body.bytesize}\r\n" \
232
+ "Connection: close\r\n\r\n"
233
+ )
234
+ client.write(gz_body)
235
+ client.close
236
+ rescue IOError, Errno::ECONNRESET, Errno::EPIPE
237
+ nil
238
+ end
239
+
240
+ original = ForestLiana.workflow_executor_url
241
+ ForestLiana.workflow_executor_url = "http://127.0.0.1:#{port}"
242
+ example.run
243
+ ensure
244
+ ForestLiana.workflow_executor_url = original
245
+ thread&.kill
246
+ server&.close
247
+ end
248
+
249
+ before { allow(HTTParty).to receive(:get).and_call_original }
250
+
251
+ it 'decompresses the body and drops the stale content-encoding' do
252
+ get "/forest/_internal/executor/runs/#{run_id}", headers: auth_headers
253
+
254
+ expect(response.status).to eq(200)
255
+ expect(JSON.parse(response.body)).to eq('id' => run_id, 'state' => 'pending')
256
+ expect(response.headers['content-encoding']).to be_nil
257
+ end
258
+ end
259
+
210
260
  describe 'when ForestLiana.workflow_executor_url is blank' do
211
261
  around do |example|
212
262
  original = ForestLiana.workflow_executor_url
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forest_liana
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.20.0
4
+ version: 9.20.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-26 00:00:00.000000000 Z
11
+ date: 2026-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails