forest_liana 9.3.16 → 9.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d542e8e2e471ab7b14b4b47d71692e98a4889c09a95e72c61775cb1744c93d9
4
- data.tar.gz: c3528129a4b519ef9d11f1c381476c1d300ffbe7ecd6845d754185bdb230f7bc
3
+ metadata.gz: 855ec9302b2f8b3c54c16efb2919b9324c22feecaa132a02075914478ae3b040
4
+ data.tar.gz: eb8275f28303c3b76a95460854037be8f619a8fa74541a91dd80128fb3e49457
5
5
  SHA512:
6
- metadata.gz: b7ff03164f24e175ef86a4076ae2b5987df415734351f17850db84e075ed152203530030786e8de98959f646611429f95bb83afe7ec651659604737bdd5aaf53
7
- data.tar.gz: 1e17aa30b9fa4cfd2914e14f198fe8a0ba999b29c60e1074629ab61b9dcff00417e34d5613552fe40a6c3164da28c0f31de25db41ca69317bda9357212a0aada
6
+ metadata.gz: 7e140338ed979e85df67414887d3fc986b0eb58abe88649dcdc140b565f43b5e72f788d3952213e3c82ff5057b23b5c63bd3543be7279f534f77893efc3b1179
7
+ data.tar.gz: ec56a023d16948a3a21736b43ae89f3ab0cf5a9bec456b2a34d90299ceefdf9b0e0d9809d938e40f88e8bae30d7ce96cc63f8334ab0124bd95943d730e3aa999
@@ -52,12 +52,14 @@ module ForestLiana
52
52
  return render status: 500, json: { error: 'Error in smart action load hook: hook must return an array of fields' }
53
53
  end
54
54
 
55
+ result = SmartActionFormParser.extract_fields_and_layout(result)
56
+
55
57
  # Validate that the fields are well formed.
56
58
  begin
57
59
  # action.hooks[:change] is a hashmap here
58
60
  # to do the validation, only the hook names are require
59
61
  change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
60
- ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result, action.name, change_hooks_name)
62
+ ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result[:fields], action.name, change_hooks_name)
61
63
  rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
62
64
  FOREST_LOGGER.warn invalid_field_error.message
63
65
  rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
@@ -67,8 +69,8 @@ module ForestLiana
67
69
  end
68
70
 
69
71
  # Apply result on fields (transform the object back to an array), preserve order.
70
- fields = result.map do |field|
71
- updated_field = result.find{|f| f[:field] == field[:field]}
72
+ fields = result[:fields].map do |field|
73
+ updated_field = result[:fields].find{|f| f[:field] == field[:field]}
72
74
 
73
75
  # Reset `value` when not present in `enums` (which means `enums` has changed).
74
76
  if updated_field[:enums].is_a?(Array)
@@ -88,7 +90,10 @@ module ForestLiana
88
90
  updated_field.transform_keys { |key| key.to_s.camelize(:lower) }
89
91
  end
90
92
 
91
- render serializer: nil, json: { fields: fields }, status: :ok
93
+ response = { fields: fields }
94
+ response[:layout] = result[:layout] unless result[:layout].all? { |element| element[:component] == 'input' }
95
+
96
+ render serializer: nil, json: response, status: :ok
92
97
  end
93
98
 
94
99
  def load
@@ -5,7 +5,7 @@ class ForestLiana::Model::Action
5
5
  extend ActiveModel::Naming
6
6
 
7
7
  attr_accessor :id, :name, :base_url, :endpoint, :http_method, :fields, :redirect,
8
- :type, :download, :hooks
8
+ :type, :download, :hooks, :description, :submit_button_label
9
9
 
10
10
  def initialize(attributes = {})
11
11
  if attributes.key?(:global)
@@ -74,6 +74,8 @@ class ForestLiana::Model::Action
74
74
  @type ||= "bulk"
75
75
  @download ||= false
76
76
  @hooks = !@hooks.nil? ? @hooks.symbolize_keys : nil
77
+ @description ||= nil
78
+ @submit_button_label ||= nil
77
79
  end
78
80
 
79
81
  def persisted?
@@ -41,6 +41,8 @@ module ForestLiana
41
41
  'download',
42
42
  'fields',
43
43
  'hooks',
44
+ 'description',
45
+ 'submit_button_label',
44
46
  ]
45
47
  KEYS_ACTION_FIELD = [
46
48
  'field',
@@ -0,0 +1,69 @@
1
+ module ForestLiana
2
+ class SmartActionFormParser
3
+ def self.extract_fields_and_layout(form)
4
+ fields = []
5
+ layout = []
6
+ form&.each do |element|
7
+ if element[:type] == 'Layout'
8
+ validate_layout_element(element)
9
+ element[:component] = element[:component].camelize(:lower)
10
+ if %w[page row].include?(element[:component])
11
+ extract = extract_fields_and_layout_for_component(element)
12
+ layout << element
13
+ fields.concat(extract[:fields])
14
+ else
15
+ layout << element
16
+ end
17
+ else
18
+ fields << element
19
+ # frontend rule
20
+ layout << { component: 'input', fieldId: element[:field] }
21
+ end
22
+ end
23
+
24
+ { fields: fields, layout: layout }
25
+ end
26
+
27
+ def self.extract_fields_and_layout_for_component(element)
28
+ # 'page' is in camel case because at this step the 'component' attribute is already convert for the response
29
+ key = element[:component] == 'page' ? :elements : :fields
30
+ extract = extract_fields_and_layout(element[key])
31
+ element[key] = extract[:layout]
32
+
33
+ extract
34
+ end
35
+
36
+ def self.validate_layout_element(element)
37
+ valid_components = %w[Page Row Separator HtmlBlock]
38
+ unless valid_components.include?(element[:component])
39
+ raise ForestLiana::Errors::HTTP422Error.new(
40
+ "#{element[:component]} is not a valid component. Valid components are #{valid_components.join(' or ')}"
41
+ )
42
+ end
43
+
44
+ if element[:component] == 'Page'
45
+ unless element[:elements].is_a? Array
46
+ raise ForestLiana::Errors::HTTP422Error.new(
47
+ "Page components must contain an array of fields or layout elements in property 'elements'"
48
+ )
49
+ end
50
+
51
+ if element[:elements].any? { |element| element[:component] === 'Page' }
52
+ raise ForestLiana::Errors::HTTP422Error.new('Pages cannot contain other pages')
53
+ end
54
+ end
55
+
56
+ if element[:component] == 'Row'
57
+ unless element[:fields].is_a? Array
58
+ raise ForestLiana::Errors::HTTP422Error.new(
59
+ "Row components must contain an array of fields in property 'fields'"
60
+ )
61
+ end
62
+
63
+ if element[:fields].any? { |element| element[:type] === 'Layout' }
64
+ raise ForestLiana::Errors::HTTP422Error.new('Row components can only contain fields')
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -52,6 +52,8 @@ module ForestLiana
52
52
  'download',
53
53
  'fields',
54
54
  'hooks',
55
+ 'description',
56
+ 'submit_button_label',
55
57
  ]
56
58
  KEYS_ACTION_FIELD = [
57
59
  'field',
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "9.3.16"
2
+ VERSION = "9.4.0"
3
3
  end
@@ -43,6 +43,89 @@ class Forest::Island
43
43
  }
44
44
  }
45
45
 
46
+ action 'my_action_with_layout',
47
+ fields: [foo],
48
+ hooks: {
49
+ :load => -> (context) {
50
+ [
51
+ {
52
+ type: 'Layout',
53
+ component: 'Page',
54
+ elements: [
55
+ {
56
+ type: 'Layout',
57
+ component: 'HtmlBlock',
58
+ content: '<p>test</p>',
59
+ },
60
+ {
61
+ type: 'Layout',
62
+ component: 'Separator',
63
+ },
64
+ foo,
65
+ {
66
+ field: 'field 1',
67
+ type: 'String',
68
+ },
69
+ {
70
+ type: 'Layout',
71
+ component: 'Separator',
72
+ },
73
+ {
74
+ field: 'field 2',
75
+ type: 'String',
76
+ }
77
+ ]
78
+ },
79
+ ]
80
+ },
81
+ :change => {
82
+ 'on_foo_changed' => -> (context) {
83
+ [
84
+ {
85
+ type: 'Layout',
86
+ component: 'Page',
87
+ elements: [
88
+ {
89
+ type: 'Layout',
90
+ component: 'HtmlBlock',
91
+ content: '<div style="text-align:center;">
92
+ <p>
93
+ <strong>Hi #{ctx.form_values["firstName"]} #{ctx.form_values["lastName"]}</strong>,
94
+ <br/>here you can put
95
+ <strong style="color: red;">all the html</strong> you want.
96
+ </p>
97
+ </div>
98
+ <div style="display: flex; flex-flow: row wrap; justify-content: space-around;">
99
+ <a href="https://www.w3schools.com" target="_blank">
100
+ <img src="https://www.w3schools.com/html/w3schools.jpg">
101
+ </a>
102
+ <iframe src="https://www.youtube.com/embed/xHPKuu9-yyw?autoplay=1&mute=1"></iframe>
103
+ </div>',
104
+ },
105
+ {
106
+ type: 'Layout',
107
+ component: 'Separator',
108
+ },
109
+ foo,
110
+ {
111
+ field: 'field 1',
112
+ type: 'String',
113
+ },
114
+ {
115
+ type: 'Layout',
116
+ component: 'Separator',
117
+ },
118
+ {
119
+ field: 'field 2',
120
+ type: 'String',
121
+ }
122
+ ]
123
+ },
124
+ ]
125
+ }
126
+ }
127
+ }
128
+
46
129
  action 'fail_action',
47
130
  fields: [foo],
48
131
  hooks: {
@@ -41,6 +41,57 @@ describe 'Requesting Actions routes', :type => :request do
41
41
  describe 'hooks' do
42
42
  island = ForestLiana.apimap.find {|collection| collection.name.to_s == ForestLiana.name_for(Island)}
43
43
 
44
+ describe 'call /load on layout form' do
45
+ params = {
46
+ data: {
47
+ attributes: { ids: [1], collection_name: 'Island' }
48
+ }
49
+ }
50
+
51
+ it 'should respond 200 with expected response on load' do
52
+ post '/forest/actions/my_action_with_layout/hooks/load', params: JSON.dump(params), headers: headers
53
+ result = JSON.parse(response.body)
54
+
55
+ expect(response.status).to eq(200)
56
+ expect(result).to eq(
57
+ {
58
+ "fields" => [
59
+ {
60
+ "field"=>"foo",
61
+ "type"=>"String",
62
+ "defaultValue"=>nil,
63
+ "enums"=>nil,
64
+ "isRequired"=>false,
65
+ "isReadOnly"=>false,
66
+ "reference"=>nil,
67
+ "description"=>nil,
68
+ "hook"=>"on_foo_changed",
69
+ "position"=>0,
70
+ "widgetEdit"=>nil,
71
+ "value"=>nil
72
+ },
73
+ { "field"=>"field 1", "type"=>"String"},
74
+ {"field"=>"field 2", "type"=>"String" }
75
+ ],
76
+ "layout"=>[
77
+ {
78
+ "type"=>"Layout",
79
+ "component"=>"page",
80
+ "elements"=>[
81
+ {"type"=>"Layout", "component"=>"htmlBlock", "content"=>"<p>test</p>"},
82
+ {"type"=>"Layout", "component"=>"separator"},
83
+ {"component"=>"input", "fieldId"=>"foo"},
84
+ {"component"=>"input", "fieldId"=>"field 1"},
85
+ {"type"=>"Layout", "component"=>"separator"},
86
+ {"component"=>"input", "fieldId"=>"field 2"}
87
+ ]
88
+ }
89
+ ]
90
+ }
91
+ )
92
+ end
93
+ end
94
+
44
95
  describe 'call /load' do
45
96
  params = {
46
97
  data: {
@@ -54,6 +105,8 @@ describe 'Requesting Actions routes', :type => :request do
54
105
  foo = action.fields.select { |field| field[:field] == 'foo' }.first
55
106
  expect(response.status).to eq(200)
56
107
  expect(JSON.parse(response.body)).to eq({'fields' => [foo.merge({:value => nil}).transform_keys { |key| key.to_s.camelize(:lower) }.stringify_keys]})
108
+ # action form without layout elements should not have the key layout
109
+ expect(JSON.parse(response.body)).not_to have_key('layout')
57
110
  end
58
111
 
59
112
  it 'should respond 422 with bad params' do
@@ -0,0 +1,55 @@
1
+ module ForestLiana
2
+ describe SmartActionFormParser do
3
+ describe "self.validate_layout_element" do
4
+ it "raise an error with an invalid component" do
5
+ expect { SmartActionFormParser.validate_layout_element({ type: 'Layout', component: 'foo' }) }
6
+ .to raise_error(
7
+ ForestLiana::Errors::HTTP422Error,
8
+ 'foo is not a valid component. Valid components are Page or Row or Separator or HtmlBlock'
9
+ )
10
+ end
11
+
12
+ it "raise an error with an invalid Page" do
13
+ expect do
14
+ SmartActionFormParser.validate_layout_element(
15
+ { type: 'Layout', component: 'Page', elements: 'foo' }
16
+ )
17
+ end.to raise_error(
18
+ ForestLiana::Errors::HTTP422Error,
19
+ "Page components must contain an array of fields or layout elements in property 'elements'"
20
+ )
21
+ end
22
+
23
+ it "raise an error with a Page that contains page" do
24
+ expect do
25
+ SmartActionFormParser.validate_layout_element(
26
+ { type: 'Layout', component: 'Page', elements: [{ type: 'Layout', component: 'Page', elements: [] }] }
27
+ )
28
+ end.to raise_error(ForestLiana::Errors::HTTP422Error, 'Pages cannot contain other pages')
29
+ end
30
+
31
+ it "should raise an error with an invalid Row" do
32
+ expect do
33
+ SmartActionFormParser.validate_layout_element(
34
+ { type: 'Layout', component: 'Row', fields: 'foo' }
35
+ )
36
+ end.to raise_error(
37
+ ForestLiana::Errors::HTTP422Error,
38
+ "Row components must contain an array of fields in property 'fields'"
39
+ )
40
+ end
41
+
42
+ it "raise an error with a row that contains layout element" do
43
+ expect do
44
+ SmartActionFormParser.validate_layout_element(
45
+ {
46
+ type: 'Layout',
47
+ component: 'Row',
48
+ fields: [ { type: 'Layout', component: 'HtmlBlock', fields: 'Row components can only contain fields' }]
49
+ }
50
+ )
51
+ end.to raise_error(ForestLiana::Errors::HTTP422Error, 'Row components can only contain fields')
52
+ end
53
+ end
54
+ end
55
+ end
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.3.16
4
+ version: 9.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-18 00:00:00.000000000 Z
11
+ date: 2024-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -305,6 +305,7 @@ files:
305
305
  - app/services/forest_liana/scope_manager.rb
306
306
  - app/services/forest_liana/search_query_builder.rb
307
307
  - app/services/forest_liana/smart_action_field_validator.rb
308
+ - app/services/forest_liana/smart_action_form_parser.rb
308
309
  - app/services/forest_liana/stat_getter.rb
309
310
  - app/services/forest_liana/stripe_base_getter.rb
310
311
  - app/services/forest_liana/stripe_invoice_getter.rb
@@ -441,6 +442,7 @@ files:
441
442
  - spec/services/forest_liana/schema_adapter_spec.rb
442
443
  - spec/services/forest_liana/scope_manager_spec.rb
443
444
  - spec/services/forest_liana/smart_action_field_validator_spec.rb
445
+ - spec/services/forest_liana/smart_action_form_parser_spec.rb
444
446
  - spec/services/forest_liana/utils/context_variables_injector_spec.rb
445
447
  - spec/services/forest_liana/utils/context_variables_spec.rb
446
448
  - spec/services/forest_liana/value_stat_getter_spec.rb
@@ -741,6 +743,7 @@ test_files:
741
743
  - spec/services/forest_liana/schema_adapter_spec.rb
742
744
  - spec/services/forest_liana/scope_manager_spec.rb
743
745
  - spec/services/forest_liana/smart_action_field_validator_spec.rb
746
+ - spec/services/forest_liana/smart_action_form_parser_spec.rb
744
747
  - spec/services/forest_liana/utils/context_variables_injector_spec.rb
745
748
  - spec/services/forest_liana/utils/context_variables_spec.rb
746
749
  - spec/services/forest_liana/value_stat_getter_spec.rb