forest_liana 9.3.16 → 9.4.0

Sign up to get free protection for your applications and to get access to all the features.
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