forest_liana 6.3.8 → 7.0.0.beta.2

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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/forest_liana/actions_controller.rb +44 -29
  3. data/app/controllers/forest_liana/application_controller.rb +2 -2
  4. data/app/controllers/forest_liana/associations_controller.rb +1 -1
  5. data/app/controllers/forest_liana/base_controller.rb +1 -1
  6. data/app/controllers/forest_liana/resources_controller.rb +6 -6
  7. data/app/serializers/forest_liana/intercom_attribute_serializer.rb +1 -1
  8. data/app/serializers/forest_liana/intercom_conversation_serializer.rb +1 -1
  9. data/app/serializers/forest_liana/mixpanel_event_serializer.rb +1 -1
  10. data/app/serializers/forest_liana/serializer_factory.rb +1 -1
  11. data/app/serializers/forest_liana/stat_serializer.rb +1 -1
  12. data/app/serializers/forest_liana/stripe_bank_account_serializer.rb +1 -1
  13. data/app/serializers/forest_liana/stripe_card_serializer.rb +1 -1
  14. data/app/serializers/forest_liana/stripe_invoice_serializer.rb +1 -1
  15. data/app/serializers/forest_liana/stripe_payment_serializer.rb +1 -1
  16. data/app/serializers/forest_liana/stripe_subscription_serializer.rb +1 -1
  17. data/app/services/forest_liana/apimap_sorter.rb +1 -0
  18. data/app/services/forest_liana/smart_action_field_validator.rb +49 -0
  19. data/config/initializers/errors.rb +17 -0
  20. data/config/initializers/logger.rb +16 -13
  21. data/lib/forest_liana.rb +2 -0
  22. data/lib/forest_liana/bootstrapper.rb +2 -2
  23. data/lib/forest_liana/schema_file_updater.rb +8 -0
  24. data/lib/forest_liana/version.rb +1 -1
  25. data/spec/config/initializers/logger_spec.rb +30 -0
  26. data/spec/lib/forest_liana/schema_file_updater_spec.rb +94 -0
  27. data/spec/requests/actions_controller_spec.rb +22 -20
  28. data/spec/services/forest_liana/smart_action_field_validator_spec.rb +70 -0
  29. metadata +12 -8
  30. data/app/helpers/forest_liana/is_same_data_structure_helper.rb +0 -44
  31. data/spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb +0 -87
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0fd5ef4e67fd45b6a701da7dbf8da36d6f6abb3d9e2be4d0031d0495a10727a
4
- data.tar.gz: 9c823060491af5ff42f098e041ffe9f2ac672865c4a790e4b8bb3f1bb1e7447b
3
+ metadata.gz: 6be4888e921b11142c5bc7774e6a0c9ce9a91b7e0b8ceb8a812cff8e297b540a
4
+ data.tar.gz: '043584e75c96d49b34a4ab6ec0e0f0315700eb86a7d061fb03de66c095840b1c'
5
5
  SHA512:
6
- metadata.gz: c6a0639f27ebecd6f7e66e83bbdc527a08e0483435e6485c80ed66f45832c886ef50cb46402a4b74b1ff8cc6b8f284419f53fd1ebebf532c57fe089d2d0bdec6
7
- data.tar.gz: 0e329ab499ed3b2414c50097f3b2cad74733c0bc51db835b25bb1a9713229f8feb6f42ad1b7fa3a038f73e357dc46bc3cad0fc6f6ad61224f306c644d02fbf1e
6
+ metadata.gz: bfc23dfba08023225f17e44860caf95dc2fe041bcf81a1d61651ee96cea59563aa2c97ee6a1bcdc2e38e2e56a0f9c7c5e144b64583286af70864ec4d636a5795
7
+ data.tar.gz: 2cc2bf17cc1a235264db9981e33e99b22c23468514bf136e3379523020ca1efd906b792b46c45b6893f0edb279d8845c79e2e14ee8821ee47db6fc87d4992692
@@ -27,34 +27,45 @@ module ForestLiana
27
27
  end
28
28
 
29
29
  def get_smart_action_load_ctx(fields)
30
- fields = fields.reduce({}) do |p, c|
31
- ForestLiana::WidgetsHelper.set_field_widget(c)
32
- p.update(c[:field] => c.merge!(value: nil))
30
+ fields = fields.map do |field|
31
+ ForestLiana::WidgetsHelper.set_field_widget(field)
32
+ field[:value] = nil unless field[:value]
33
+ field
33
34
  end
34
35
  {:record => get_record, :fields => fields}
35
36
  end
36
37
 
37
- def get_smart_action_change_ctx(fields)
38
- fields = fields.reduce({}) do |p, c|
39
- field = c.permit!.to_h.symbolize_keys
38
+ def get_smart_action_change_ctx(fields, field_changed)
39
+ found_field_changed = fields.find{|field| field[:field] == field_changed}
40
+ fields = fields.map do |field|
41
+ field = field.permit!.to_h.symbolize_keys
40
42
  ForestLiana::WidgetsHelper.set_field_widget(field)
41
- p.update(c[:field] => field)
43
+ field
42
44
  end
43
- {:record => get_record, :fields => fields}
45
+ {:record => get_record, :field_changed => found_field_changed, :fields => fields}
44
46
  end
45
47
 
46
- def handle_result(result, formatted_fields, action)
47
- if result.nil? || !result.is_a?(Hash)
48
- return render status: 500, json: { error: 'Error in smart action load hook: hook must return an object' }
48
+ def handle_result(result, action)
49
+ if result.nil? || !result.is_a?(Array)
50
+ return render status: 500, json: { error: 'Error in smart action load hook: hook must return an array of fields' }
49
51
  end
50
- is_same_data_structure = ForestLiana::IsSameDataStructureHelper::Analyser.new(formatted_fields, result, 1)
51
- unless is_same_data_structure.perform
52
- return render status: 500, json: { error: 'Error in smart action hook: fields must be unchanged (no addition nor deletion allowed)' }
52
+
53
+ # Validate that the fields are well formed.
54
+ begin
55
+ # action.hooks[:change] is a hashmap here
56
+ # to do the validation, only the hook names are require
57
+ change_hooks_name = action.hooks[:change].nil? ? nil : action.hooks[:change].keys
58
+ ForestLiana::SmartActionFieldValidator.validate_smart_action_fields(result, action.name, change_hooks_name)
59
+ rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
60
+ FOREST_LOGGER.warn invalid_field_error.message
61
+ rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
62
+ FOREST_LOGGER.error invalid_hook_error.message
63
+ return render status: 500, json: { error: invalid_hook_error.message }
53
64
  end
54
65
 
55
66
  # Apply result on fields (transform the object back to an array), preserve order.
56
- fields = action.fields.map do |field|
57
- updated_field = result[field[:field]]
67
+ fields = result.map do |field|
68
+ updated_field = result.find{|f| f[:field] == field[:field]}
58
69
 
59
70
  # Reset `value` when not present in `enums` (which means `enums` has changed).
60
71
  if updated_field[:enums].is_a?(Array)
@@ -72,7 +83,7 @@ module ForestLiana
72
83
  updated_field
73
84
  end
74
85
 
75
- render serializer: nil, json: { fields: fields}, status: :ok
86
+ render serializer: nil, json: { fields: fields }, status: :ok
76
87
  end
77
88
 
78
89
  def load
@@ -81,14 +92,13 @@ module ForestLiana
81
92
  if !action
82
93
  render status: 500, json: {error: 'Error in smart action load hook: cannot retrieve action from collection'}
83
94
  else
84
- # Transform fields from array to an object to ease usage in hook, adds null value.
95
+ # Get the smart action hook load context
85
96
  context = get_smart_action_load_ctx(action.fields)
86
- formatted_fields = context[:fields].clone # clone for following test on is_same_data_structure
87
97
 
88
98
  # Call the user-defined load hook.
89
99
  result = action.hooks[:load].(context)
90
100
 
91
- handle_result(result, formatted_fields, action)
101
+ handle_result(result, action)
92
102
  end
93
103
  end
94
104
 
@@ -96,17 +106,22 @@ module ForestLiana
96
106
  action = get_action(params[:collectionName])
97
107
 
98
108
  if !action
99
- render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
100
- else
101
- # Transform fields from array to an object to ease usage in hook.
102
- context = get_smart_action_change_ctx(params[:fields])
103
- formatted_fields = context[:fields].clone # clone for following test on is_same_data_structure
109
+ return render status: 500, json: {error: 'Error in smart action change hook: cannot retrieve action from collection'}
110
+ elsif params[:fields].nil?
111
+ return render status: 500, json: {error: 'Error in smart action change hook: fields params is mandatory'}
112
+ elsif !params[:fields].is_a?(Array)
113
+ return render status: 500, json: {error: 'Error in smart action change hook: fields params must be an array'}
114
+ end
104
115
 
105
- # Call the user-defined change hook.
106
- result = action.hooks[:change][params[:changedField]].(context)
116
+ # Get the smart action hook change context
117
+ context = get_smart_action_change_ctx(params[:fields], params[:changedField])
107
118
 
108
- handle_result(result, formatted_fields, action)
109
- end
119
+ field_changed_hook = context[:field_changed][:hook]
120
+
121
+ # Call the user-defined change hook.
122
+ result = action.hooks[:change][field_changed_hook].(context)
123
+
124
+ handle_result(result, action)
110
125
  end
111
126
  end
112
127
  end
@@ -34,14 +34,14 @@ module ForestLiana
34
34
 
35
35
  def serialize_model(record, options = {})
36
36
  options[:is_collection] = false
37
- json = JSONAPI::Serializer.serialize(record, options)
37
+ json = ForestAdmin::JSONAPI::Serializer.serialize(record, options)
38
38
 
39
39
  force_utf8_encoding(json)
40
40
  end
41
41
 
42
42
  def serialize_models(records, options = {}, fields_searched = [])
43
43
  options[:is_collection] = true
44
- json = JSONAPI::Serializer.serialize(records, options)
44
+ json = ForestAdmin::JSONAPI::Serializer.serialize(records, options)
45
45
 
46
46
  if options[:params] && options[:params][:search]
47
47
  # NOTICE: Add the Smart Fields with a 'String' type.
@@ -41,7 +41,7 @@ module ForestLiana
41
41
  updater.perform
42
42
 
43
43
  if updater.errors
44
- render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
44
+ render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
45
45
  updater.errors), status: 422
46
46
  else
47
47
  head :no_content
@@ -24,7 +24,7 @@ module ForestLiana
24
24
  end
25
25
  end
26
26
  rescue ForestLiana::Errors::ExpectedError => exception
27
- error_data = JSONAPI::Serializer.serialize_errors([{
27
+ error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
28
28
  status: exception.error_code,
29
29
  detail: exception.message
30
30
  }])
@@ -41,7 +41,7 @@ module ForestLiana
41
41
  status: :unprocessable_entity, serializer: nil
42
42
  rescue ForestLiana::Errors::ExpectedError => error
43
43
  error.display_error
44
- error_data = JSONAPI::Serializer.serialize_errors([{
44
+ error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
45
45
  status: error.error_code,
46
46
  detail: error.message
47
47
  }])
@@ -73,7 +73,7 @@ module ForestLiana
73
73
  status: :unprocessable_entity, serializer: nil
74
74
  rescue ForestLiana::Errors::ExpectedError => error
75
75
  error.display_error
76
- error_data = JSONAPI::Serializer.serialize_errors([{
76
+ error_data = ForestAdmin::JSONAPI::Serializer.serialize_errors([{
77
77
  status: error.error_code,
78
78
  detail: error.message
79
79
  }])
@@ -108,12 +108,12 @@ module ForestLiana
108
108
  creator.perform
109
109
 
110
110
  if creator.errors
111
- render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
111
+ render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
112
112
  creator.errors), status: 400
113
113
  elsif creator.record.valid?
114
114
  render serializer: nil, json: render_record_jsonapi(creator.record)
115
115
  else
116
- render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
116
+ render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
117
117
  creator.record.errors), status: 400
118
118
  end
119
119
  rescue => error
@@ -131,12 +131,12 @@ module ForestLiana
131
131
  updater.perform
132
132
 
133
133
  if updater.errors
134
- render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
134
+ render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
135
135
  updater.errors), status: 400
136
136
  elsif updater.record.valid?
137
137
  render serializer: nil, json: render_record_jsonapi(updater.record)
138
138
  else
139
- render serializer: nil, json: JSONAPI::Serializer.serialize_errors(
139
+ render serializer: nil, json: ForestAdmin::JSONAPI::Serializer.serialize_errors(
140
140
  updater.record.errors), status: 400
141
141
  end
142
142
  rescue => error
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class IntercomAttributeSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :session_count
6
6
  attribute :last_seen_ip
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class IntercomConversationSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :created_at
6
6
  attribute :updated_at
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class MixpanelEventSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :id
6
6
  attribute :event
@@ -53,7 +53,7 @@ module ForestLiana
53
53
 
54
54
  def serializer_for(active_record_class)
55
55
  serializer = Class.new {
56
- include JSONAPI::Serializer
56
+ include ForestAdmin::JSONAPI::Serializer
57
57
 
58
58
  def self_link
59
59
  "/forest#{super.underscore}"
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StatSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :value
6
6
 
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StripeBankAccountSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :account_holder_name
6
6
  attribute :account_holder_type
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StripeCardSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :last4
6
6
  attribute :brand
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StripeInvoiceSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :amount_due
6
6
  attribute :amount_paid
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StripePaymentSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :description
6
6
  attribute :refunded
@@ -1,6 +1,6 @@
1
1
  module ForestLiana
2
2
  class StripeSubscriptionSerializer
3
- include JSONAPI::Serializer
3
+ include ForestAdmin::JSONAPI::Serializer
4
4
 
5
5
  attribute :cancel_at_period_end
6
6
  attribute :canceled_at
@@ -52,6 +52,7 @@ module ForestLiana
52
52
  'description',
53
53
  'position',
54
54
  'widget',
55
+ 'hook',
55
56
  ]
56
57
  KEYS_SEGMENT = ['name']
57
58
 
@@ -0,0 +1,49 @@
1
+ module ForestLiana
2
+ class SmartActionFieldValidator
3
+
4
+ @@accepted_primitive_field_type = [
5
+ 'String',
6
+ 'Number',
7
+ 'Date',
8
+ 'Boolean',
9
+ 'File',
10
+ 'Enum',
11
+ 'Json',
12
+ 'Dateonly',
13
+ ]
14
+
15
+ @@accepted_array_field_type = [
16
+ 'String',
17
+ 'Number',
18
+ 'Date',
19
+ 'boolean',
20
+ 'File',
21
+ 'Enum',
22
+ ]
23
+
24
+ def self.validate_field(field, action_name)
25
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, nil, "The field attribute must be defined") if !field || field[:field].nil?
26
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, nil, "The field attribute must be a string.") if !field[:field].is_a?(String)
27
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, field[:field], "The description attribute must be a string.") if field[:description] && !field[:description].is_a?(String)
28
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, field[:field], "The enums attribute must be an array.") if field[:enums] && !field[:enums].is_a?(Array)
29
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, field[:field], "The reference attribute must be a string.") if field[:reference] && !field[:reference].is_a?(String)
30
+
31
+ is_type_valid = field[:type].is_a?(Array) ?
32
+ @@accepted_array_field_type.include?(field[:type][0]) :
33
+ @@accepted_primitive_field_type.include?(field[:type])
34
+
35
+ raise ForestLiana::Errors::SmartActionInvalidFieldError.new(action_name, field[:field], "The type attribute must be a valid type. See the documentation for more information. https://docs.forestadmin.com/documentation/reference-guide/fields/create-and-manage-smart-fields#available-field-options.") if !is_type_valid
36
+ end
37
+
38
+ def self.validate_field_change_hook(field, action_name, hooks)
39
+ raise ForestLiana::Errors::SmartActionInvalidFieldHookError.new(action_name, field[:field], field[:hook]) if field[:hook] && !hooks.find{|hook| hook == field[:hook]}
40
+ end
41
+
42
+ def self.validate_smart_action_fields(fields, action_name, change_hooks)
43
+ fields.map{|field|
44
+ self.validate_field(field.symbolize_keys, action_name)
45
+ self.validate_field_change_hook(field.symbolize_keys, action_name, change_hooks) if change_hooks
46
+ }
47
+ end
48
+ end
49
+ end
@@ -13,6 +13,23 @@ module ForestLiana
13
13
  end
14
14
  end
15
15
 
16
+ class SmartActionInvalidFieldError < StandardError
17
+ def initialize(action_name=nil, field_name=nil, message=nil)
18
+ error_message = ""
19
+ error_message << "Error while parsing action \"#{action_name}\"" if !action_name.nil?
20
+ error_message << " on field \"#{field_name}\"" if !field_name.nil?
21
+ error_message << ": " if !field_name.nil? || !action_name.nil?
22
+ error_message << message if !message.nil?
23
+ super(error_message)
24
+ end
25
+ end
26
+
27
+ class SmartActionInvalidFieldHookError < StandardError
28
+ def initialize(action_name=nil, field_name=nil, hook_name=nil)
29
+ super("The hook \"#{hook_name}\" of \"#{field_name}\" field on the smart action \"#{action_name}\" is not defined.")
30
+ end
31
+ end
32
+
16
33
  class ExpectedError < StandardError
17
34
  attr_reader :error_code, :status, :message, :name
18
35
 
@@ -3,21 +3,24 @@ module ForestLiana
3
3
  class Logger
4
4
  class << self
5
5
  def log
6
- logger = ::Logger.new(STDOUT)
7
- logger_colors = {
8
- DEBUG: 34,
9
- WARN: 33,
10
- ERROR: 31,
11
- INFO: 37
12
- }
6
+ if ForestLiana.logger != nil
7
+ logger = ForestLiana.logger
8
+ else
9
+ logger = ::Logger.new(STDOUT)
10
+ logger_colors = {
11
+ DEBUG: 34,
12
+ WARN: 33,
13
+ ERROR: 31,
14
+ INFO: 37
15
+ }
13
16
 
14
- logger.formatter = proc do |severity, datetime, progname, message|
15
- displayed_message = "[#{datetime.to_s(:db)}] Forest 🌳🌳🌳 " \
16
- "#{message}\n"
17
- "\e[#{logger_colors[severity.to_sym]}m#{displayed_message}\033[0m"
17
+ logger.formatter = proc do |severity, datetime, progname, message|
18
+ displayed_message = "[#{datetime.to_s(:db)}] Forest 🌳🌳🌳 " \
19
+ "#{message}\n"
20
+ "\e[#{logger_colors[severity.to_sym]}m#{displayed_message}\033[0m"
21
+ end
22
+ logger
18
23
  end
19
-
20
- logger
21
24
  end
22
25
  end
23
26
  end
data/lib/forest_liana.rb CHANGED
@@ -27,6 +27,7 @@ module ForestLiana
27
27
  mattr_accessor :user_class_name
28
28
  mattr_accessor :names_overriden
29
29
  mattr_accessor :meta
30
+ mattr_accessor :logger
30
31
  # TODO: Remove once lianas prior to 2.0.0 are not supported anymore.
31
32
  mattr_accessor :names_old_overriden
32
33
 
@@ -38,6 +39,7 @@ module ForestLiana
38
39
  self.user_class_name = nil
39
40
  self.names_overriden = {}
40
41
  self.meta = {}
42
+ self.logger = nil
41
43
 
42
44
  @config_dir = 'lib/forest_liana/**/*.rb'
43
45
 
@@ -60,7 +60,7 @@ module ForestLiana
60
60
  a = get_action(c, action['name'])
61
61
  load = !a.hooks.nil? && a.hooks.key?(:load) && a.hooks[:load].is_a?(Proc)
62
62
  change = !a.hooks.nil? && a.hooks.key?(:change) && a.hooks[:change].is_a?(Hash) ? a.hooks[:change].keys : []
63
- action['hooks'] = {:load => load, :change => change}
63
+ action['hooks'] = {'load' => load, 'change' => change}
64
64
  end
65
65
  end
66
66
  end
@@ -139,7 +139,7 @@ module ForestLiana
139
139
 
140
140
  # Monkey patch the find_serializer_class_name method to specify the
141
141
  # good serializer to use.
142
- ::JSONAPI::Serializer.class_eval do
142
+ ::ForestAdmin::JSONAPI::Serializer.class_eval do
143
143
  def self.find_serializer_class_name(record, options)
144
144
  if record.respond_to?(:jsonapi_serializer_class_name)
145
145
  record.jsonapi_serializer_class_name.to_s
@@ -63,6 +63,7 @@ module ForestLiana
63
63
  'description',
64
64
  'position',
65
65
  'widget',
66
+ 'hook',
66
67
  ]
67
68
  KEYS_SEGMENT = ['name']
68
69
 
@@ -96,6 +97,13 @@ module ForestLiana
96
97
  end
97
98
 
98
99
  collection['actions'] = collection['actions'].map do |action|
100
+ begin
101
+ SmartActionFieldValidator.validate_smart_action_fields(action['fields'], action['name'], action['hooks']['change'])
102
+ rescue ForestLiana::Errors::SmartActionInvalidFieldError => invalid_field_error
103
+ FOREST_LOGGER.warn invalid_field_error.message
104
+ rescue ForestLiana::Errors::SmartActionInvalidFieldHookError => invalid_hook_error
105
+ FOREST_LOGGER.error invalid_hook_error.message
106
+ end
99
107
  action['fields'] = action['fields'].map { |field| field.slice(*KEYS_ACTION_FIELD) }
100
108
  action.slice(*KEYS_ACTION)
101
109
  end
@@ -1,3 +1,3 @@
1
1
  module ForestLiana
2
- VERSION = "6.3.8"
2
+ VERSION = "7.0.0.beta.2"
3
3
  end
@@ -0,0 +1,30 @@
1
+ module ForestLiana
2
+ describe Logger do
3
+ describe 'self.log' do
4
+ describe 'with a logger overload' do
5
+ it 'should return the given logger' do
6
+ logger = ActiveSupport::Logger.new($stdout)
7
+ logger.formatter = proc do |severity, datetime, progname, msg|
8
+ {:message => msg}.to_json
9
+ end
10
+ ForestLiana.logger = logger
11
+
12
+ expect(Logger.log.is_a?(ActiveSupport::Logger)).to be_truthy
13
+ expect { Logger.log.error "[error] override logger" }.to output({:message => "[error] override logger"}.to_json).to_stdout_from_any_process
14
+ expect { Logger.log.info "[info] override logger" }.to output({:message => "[info] override logger"}.to_json).to_stdout_from_any_process
15
+ end
16
+ end
17
+
18
+ describe 'with no logger overload' do
19
+ it 'should return an instance of ::Logger' do
20
+ ForestLiana.logger = nil
21
+
22
+ expect(Logger.log.is_a?(::Logger)).to be_truthy
23
+ # RegExp is used to check for the forestadmin logger format
24
+ expect { Logger.log.error "[error] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] Forest .* \[error\]/).to_stdout_from_any_process
25
+ expect { Logger.log.info "[info] default logger" }.to output(/\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\] Forest .* \[info\]/).to_stdout_from_any_process
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,94 @@
1
+ module ForestLiana
2
+ describe SchemaFileUpdater do
3
+ describe "initialize" do
4
+ describe "without any collections nor meta" do
5
+ it "should set collections as an empty array and meta as an empty object" do
6
+ schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", [], {})
7
+ expect(schema_file_updater.instance_variable_get(:@collections)).to eq([])
8
+ expect(schema_file_updater.instance_variable_get(:@meta)).to eq({})
9
+ end
10
+ end
11
+
12
+ describe "with a given collection" do
13
+ describe "when the collection has a smart action action" do
14
+ it "should save the smart action" do
15
+ collections = [{
16
+ "fields" => [],
17
+ "actions" => [{
18
+ "fields" => [],
19
+ "name" => "test",
20
+ "hooks" => {
21
+ "change" => []
22
+ }
23
+ }],
24
+ "segments" => []
25
+ }]
26
+ schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", collections, {})
27
+ expect(schema_file_updater.instance_variable_get(:@collections))
28
+ .to eq(collections)
29
+ end
30
+
31
+ describe "when a smart action field is malformed" do
32
+ it "should display a warning message" do
33
+ collections = [{
34
+ "fields" => [],
35
+ "actions" => [{
36
+ "fields" => [{}],
37
+ "name" => "test",
38
+ "hooks" => {
39
+ "change" => []
40
+ }
41
+ }],
42
+ "segments" => []
43
+ }]
44
+ allow(FOREST_LOGGER).to receive(:warn)
45
+ schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", collections, {})
46
+ expect(FOREST_LOGGER).to have_received(:warn).with('Error while parsing action "test": The field attribute must be defined')
47
+ end
48
+ end
49
+
50
+ describe "when a smart action change field hook does not exist" do
51
+ it "should display an error message" do
52
+ collections = [{
53
+ "fields" => [],
54
+ "actions" => [{
55
+ "fields" => [{
56
+ "field" => "testField",
57
+ "hook" => "undefinedHook",
58
+ "type" => "String",
59
+ }],
60
+ "name" => "test",
61
+ "hooks" => {
62
+ "change" => []
63
+ }
64
+ }],
65
+ "segments" => []
66
+ }]
67
+
68
+ allow(FOREST_LOGGER).to receive(:error)
69
+ schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", collections, {})
70
+ expect(FOREST_LOGGER).to have_received(:error).with('The hook "undefinedHook" of "testField" field on the smart action "test" is not defined.')
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "perform" do
78
+ it "should call file puts with pretty printed data" do
79
+ file = instance_double(File, read: "stubbed read")
80
+
81
+ allow(File).to receive(:open).with("test.txt", "w") { |&block| block.call(file) }
82
+
83
+ schema_file_updater = ForestLiana::SchemaFileUpdater.new("test.txt", [], {})
84
+ expected_result = schema_file_updater.pretty_print({
85
+ "collections" => [],
86
+ "meta" => {}
87
+ })
88
+
89
+ expect(file).to receive(:puts).with(expected_result)
90
+ schema_file_updater.perform
91
+ end
92
+ end
93
+ end
94
+ end
@@ -30,6 +30,7 @@ describe 'Requesting Actions routes', :type => :request do
30
30
  reference: nil,
31
31
  description: nil,
32
32
  widget: nil,
33
+ hook: 'on_foo_changed'
33
34
  }
34
35
  enum = {
35
36
  field: 'enum',
@@ -50,10 +51,10 @@ describe 'Requesting Actions routes', :type => :request do
50
51
  context[:fields]
51
52
  },
52
53
  :change => {
53
- 'foo' => -> (context) {
54
- fields = context[:fields]
55
- fields['foo'][:value] = 'baz'
56
- return fields
54
+ 'on_foo_changed' => -> (context) {
55
+ foo = context[:fields].find{|field| field[:field] == 'foo'}
56
+ foo[:value] = 'baz'
57
+ context[:fields]
57
58
  }
58
59
  }
59
60
  }
@@ -66,7 +67,7 @@ describe 'Requesting Actions routes', :type => :request do
66
67
  1
67
68
  },
68
69
  :change => {
69
- 'foo' => -> (context) {
70
+ 'on_foo_changed' => -> (context) {
70
71
  1
71
72
  }
72
73
  }
@@ -77,11 +78,10 @@ describe 'Requesting Actions routes', :type => :request do
77
78
  fields: [foo],
78
79
  hooks: {
79
80
  :load => -> (context) {
80
- context[:fields]['baz'] = foo.clone.update({field: 'baz'})
81
- context[:fields]
81
+ {}
82
82
  },
83
83
  :change => {
84
- 'foo' => -> (context) {
84
+ 'on_foo_changed' => -> (context) {
85
85
  context[:fields]['baz'] = foo.clone.update({field: 'baz'})
86
86
  context[:fields]
87
87
  }
@@ -93,10 +93,11 @@ describe 'Requesting Actions routes', :type => :request do
93
93
  fields: [foo, enum],
94
94
  hooks: {
95
95
  :change => {
96
- 'foo' => -> (context) {
96
+ 'on_foo_changed' => -> (context) {
97
97
  fields = context[:fields]
98
- fields['enum'][:enums] = %w[c d e]
99
- return fields
98
+ enum_field = fields.find{|field| field[:field] == 'enum'}
99
+ enum_field[:enums] = %w[c d e]
100
+ fields
100
101
  }
101
102
  }
102
103
  }
@@ -107,10 +108,11 @@ describe 'Requesting Actions routes', :type => :request do
107
108
  fields: [foo, multiple_enum],
108
109
  hooks: {
109
110
  :change => {
110
- 'foo' => -> (context) {
111
+ 'on_foo_changed' => -> (context) {
111
112
  fields = context[:fields]
112
- fields['multipleEnum'][:enums] = %w[c d z]
113
- return fields
113
+ enum_field = fields.find{|field| field[:field] == 'multipleEnum'}
114
+ enum_field[:enums] = %w[c d z]
115
+ fields
114
116
  }
115
117
  }
116
118
  }
@@ -136,16 +138,19 @@ describe 'Requesting Actions routes', :type => :request do
136
138
  it 'should respond 500 with bad params' do
137
139
  post '/forest/actions/my_action/hooks/load', params: {}
138
140
  expect(response.status).to eq(500)
141
+ expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: cannot retrieve action from collection'})
139
142
  end
140
143
 
141
144
  it 'should respond 500 with bad hook result type' do
142
145
  post '/forest/actions/fail_action/hooks/load', params: JSON.dump(params), headers: { 'CONTENT_TYPE' => 'application/json' }
143
146
  expect(response.status).to eq(500)
147
+ expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
144
148
  end
145
149
 
146
150
  it 'should respond 500 with bad hook result data structure' do
147
151
  post '/forest/actions/cheat_action/hooks/load', params: JSON.dump(params), headers: { 'CONTENT_TYPE' => 'application/json' }
148
152
  expect(response.status).to eq(500)
153
+ expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
149
154
  end
150
155
  end
151
156
 
@@ -163,18 +168,15 @@ describe 'Requesting Actions routes', :type => :request do
163
168
  end
164
169
 
165
170
  it 'should respond 500 with bad params' do
166
- post '/forest/actions/my_action/hooks/change', params: {}
171
+ post '/forest/actions/my_action/hooks/change', params: JSON.dump({collectionName: 'Island'}), headers: { 'CONTENT_TYPE' => 'application/json' }
167
172
  expect(response.status).to eq(500)
173
+ expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action change hook: fields params is mandatory'})
168
174
  end
169
175
 
170
176
  it 'should respond 500 with bad hook result type' do
171
177
  post '/forest/actions/fail_action/hooks/change', params: JSON.dump(params), headers: { 'CONTENT_TYPE' => 'application/json' }
172
178
  expect(response.status).to eq(500)
173
- end
174
-
175
- it 'should respond 500 with bad hook result data structure' do
176
- post '/forest/actions/cheat_action/hooks/change', params: JSON.dump(params), headers: { 'CONTENT_TYPE' => 'application/json' }
177
- expect(response.status).to eq(500)
179
+ expect(JSON.parse(response.body)).to eq({'error' => 'Error in smart action load hook: hook must return an array of fields'})
178
180
  end
179
181
 
180
182
  it 'should reset value when enums has changed' do
@@ -0,0 +1,70 @@
1
+ module ForestLiana
2
+ describe SmartActionFieldValidator do
3
+ describe "self.validate_field" do
4
+ it "should raise an SmartActionInvalidFieldError with nil field" do
5
+ expect { SmartActionFieldValidator.validate_field(nil, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName": The field attribute must be defined')
6
+ end
7
+
8
+ it "should raise an SmartActionInvalidFieldError with a field that is not a string" do
9
+ expect { SmartActionFieldValidator.validate_field({
10
+ :field => 5
11
+ }, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName": The field attribute must be a string.')
12
+ end
13
+
14
+ it "should raise an SmartActionInvalidFieldError with description that is not a string" do
15
+ expect { SmartActionFieldValidator.validate_field({
16
+ :field => "field",
17
+ :description => 5
18
+ }, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName" on field "field": The description attribute must be a string.')
19
+ end
20
+
21
+ it "should raise an SmartActionInvalidFieldError with an enums that is not an array" do
22
+ expect { SmartActionFieldValidator.validate_field({
23
+ :field => "field",
24
+ :enums => "NotAnArray"
25
+ }, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName" on field "field": The enums attribute must be an array.')
26
+ end
27
+
28
+ it "should raise an SmartActionInvalidFieldError with a reference that is not a string" do
29
+ expect { SmartActionFieldValidator.validate_field({
30
+ :field => "field",
31
+ :type => "String",
32
+ :reference => 5
33
+ }, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName" on field "field": The reference attribute must be a string.')
34
+ end
35
+
36
+ it "should raise an SmartActionInvalidFieldError with an invalid type" do
37
+ expect { SmartActionFieldValidator.validate_field({
38
+ :field => "field",
39
+ :type => "AbsolutelyNotAValidType"
40
+ }, "actionName") }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldError, 'Error while parsing action "actionName" on field "field": The type attribute must be a valid type. See the documentation for more information. https://docs.forestadmin.com/documentation/reference-guide/fields/create-and-manage-smart-fields#available-field-options.')
41
+ end
42
+
43
+ it "should not raise any error when everything is configured correctly" do
44
+ expect { SmartActionFieldValidator.validate_field({
45
+ :field => "field",
46
+ :type => "String",
47
+ :description => "field description"
48
+ }, "actionName") }.not_to raise_error
49
+ end
50
+ end
51
+
52
+ describe "self.validate_field_change_hook" do
53
+ it "should raise an SmartActionInvalidFieldHookError with an invalid type" do
54
+ expect { SmartActionFieldValidator.validate_field_change_hook({
55
+ :field => "field",
56
+ :type => "AbsolutelyNotAValidType",
57
+ :hook => "hookThatDoesNotExist"
58
+ }, "actionName", []) }.to raise_error(ForestLiana::Errors::SmartActionInvalidFieldHookError)
59
+ end
60
+
61
+ it "should not raise any error when everything is configured correctly" do
62
+ expect { SmartActionFieldValidator.validate_field_change_hook({
63
+ :field => "field",
64
+ :type => "AbsolutelyNotAValidType",
65
+ :hook => "on_field_changed"
66
+ }, "actionName", ["on_field_changed"]) }.not_to raise_error
67
+ end
68
+ end
69
+ end
70
+ 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: 6.3.8
4
+ version: 7.0.0.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Munda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2021-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: jsonapi-serializers
28
+ name: forestadmin-jsonapi-serializers
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -225,7 +225,6 @@ files:
225
225
  - app/helpers/forest_liana/adapter_helper.rb
226
226
  - app/helpers/forest_liana/application_helper.rb
227
227
  - app/helpers/forest_liana/decoration_helper.rb
228
- - app/helpers/forest_liana/is_same_data_structure_helper.rb
229
228
  - app/helpers/forest_liana/query_helper.rb
230
229
  - app/helpers/forest_liana/schema_helper.rb
231
230
  - app/helpers/forest_liana/widgets_helper.rb
@@ -283,6 +282,7 @@ files:
283
282
  - app/services/forest_liana/schema_utils.rb
284
283
  - app/services/forest_liana/scope_validator.rb
285
284
  - app/services/forest_liana/search_query_builder.rb
285
+ - app/services/forest_liana/smart_action_field_validator.rb
286
286
  - app/services/forest_liana/stat_getter.rb
287
287
  - app/services/forest_liana/stripe_base_getter.rb
288
288
  - app/services/forest_liana/stripe_invoice_getter.rb
@@ -316,6 +316,7 @@ files:
316
316
  - lib/generators/forest_liana/install_generator.rb
317
317
  - lib/tasks/display_apimap.rake
318
318
  - lib/tasks/send_apimap.rake
319
+ - spec/config/initializers/logger_spec.rb
319
320
  - spec/dummy/README.rdoc
320
321
  - spec/dummy/Rakefile
321
322
  - spec/dummy/app/assets/config/manifest.js
@@ -367,9 +368,9 @@ files:
367
368
  - spec/dummy/db/schema.rb
368
369
  - spec/dummy/lib/forest_liana/collections/location.rb
369
370
  - spec/dummy/lib/forest_liana/collections/user.rb
370
- - spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb
371
371
  - spec/helpers/forest_liana/query_helper_spec.rb
372
372
  - spec/helpers/forest_liana/schema_helper_spec.rb
373
+ - spec/lib/forest_liana/schema_file_updater_spec.rb
373
374
  - spec/rails_helper.rb
374
375
  - spec/requests/actions_controller_spec.rb
375
376
  - spec/requests/authentications_spec.rb
@@ -386,6 +387,7 @@ files:
386
387
  - spec/services/forest_liana/permissions_getter_spec.rb
387
388
  - spec/services/forest_liana/resources_getter_spec.rb
388
389
  - spec/services/forest_liana/schema_adapter_spec.rb
390
+ - spec/services/forest_liana/smart_action_field_validator_spec.rb
389
391
  - spec/spec_helper.rb
390
392
  - test/dummy/README.rdoc
391
393
  - test/dummy/Rakefile
@@ -494,9 +496,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
494
496
  version: '0'
495
497
  required_rubygems_version: !ruby/object:Gem::Requirement
496
498
  requirements:
497
- - - ">="
499
+ - - ">"
498
500
  - !ruby/object:Gem::Version
499
- version: '0'
501
+ version: 1.3.1
500
502
  requirements: []
501
503
  rubygems_version: 3.1.2
502
504
  signing_key:
@@ -602,12 +604,13 @@ test_files:
602
604
  - spec/services/forest_liana/permissions_checker_acl_enabled_spec.rb
603
605
  - spec/services/forest_liana/filters_parser_spec.rb
604
606
  - spec/services/forest_liana/schema_adapter_spec.rb
607
+ - spec/services/forest_liana/smart_action_field_validator_spec.rb
605
608
  - spec/services/forest_liana/line_stat_getter_spec.rb
606
609
  - spec/services/forest_liana/permissions_formatter_spec.rb
607
610
  - spec/services/forest_liana/permissions_checker_live_queries_spec.rb
608
611
  - spec/services/forest_liana/apimap_sorter_spec.rb
609
612
  - spec/services/forest_liana/permissions_checker_acl_disabled_spec.rb
610
- - spec/helpers/forest_liana/is_same_data_structure_helper_spec.rb
613
+ - spec/config/initializers/logger_spec.rb
611
614
  - spec/helpers/forest_liana/schema_helper_spec.rb
612
615
  - spec/helpers/forest_liana/query_helper_spec.rb
613
616
  - spec/dummy/db/schema.rb
@@ -661,6 +664,7 @@ test_files:
661
664
  - spec/dummy/app/models/location.rb
662
665
  - spec/dummy/app/models/island.rb
663
666
  - spec/dummy/app/models/reference.rb
667
+ - spec/lib/forest_liana/schema_file_updater_spec.rb
664
668
  - spec/rails_helper.rb
665
669
  - spec/requests/stats_spec.rb
666
670
  - spec/requests/resources_spec.rb
@@ -1,44 +0,0 @@
1
- require 'set'
2
-
3
- module ForestLiana
4
- module IsSameDataStructureHelper
5
- class Analyser
6
- def initialize(object, other, deep = 0)
7
- @object = object
8
- @other = other
9
- @deep = deep
10
- end
11
-
12
- def are_objects(object, other)
13
- object && other && object.is_a?(Hash) && other.is_a?(Hash)
14
- end
15
-
16
- def check_keys(object, other, step = 0)
17
- unless are_objects(object, other)
18
- return false
19
- end
20
-
21
- object_keys = object.keys
22
- other_keys = other.keys
23
-
24
- if object_keys.length != other_keys.length
25
- return false
26
- end
27
-
28
- object_keys_set = object_keys.to_set
29
- other_keys.each { |key|
30
- if !object_keys_set.member?(key) || (step + 1 <= @deep && !check_keys(object[key], other[key], step + 1))
31
- return false
32
- end
33
- }
34
-
35
- return true
36
- end
37
-
38
- def perform
39
- check_keys(@object, @other)
40
- end
41
- end
42
- end
43
- end
44
-
@@ -1,87 +0,0 @@
1
- module ForestLiana
2
- context 'IsSameDataStructure class' do
3
- it 'should: be valid with simple data' do
4
- object = {:a => 'a', :b => 'b'}
5
- other = {:a => 'a', :b => 'b'}
6
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
7
- expect(result).to be true
8
- end
9
-
10
- it 'should: be invalid with simple data' do
11
- object = {:a => 'a', :b => 'b'}
12
- other = {:a => 'a', :c => 'c'}
13
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
14
- expect(result).to be false
15
- end
16
-
17
- it 'should: be invalid with not same hash' do
18
- object = {:a => 'a', :b => 'b'}
19
- other = {:a => 'a', :b => 'b', :c => 'c'}
20
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
21
- expect(result).to be false
22
- end
23
-
24
- it 'should: be invalid with nil' do
25
- object = nil
26
- other = {:a => 'a', :b => 'b', :c => 'c'}
27
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
28
- expect(result).to be false
29
- end
30
-
31
- it 'should: be invalid with not hash' do
32
- object = nil
33
- other = {:a => 'a', :b => 'b', :c => 'c'}
34
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
35
- expect(result).to be false
36
- end
37
-
38
- it 'should: be invalid with integer' do
39
- object = 1
40
- other = {:a => 'a', :b => 'b', :c => 'c'}
41
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
42
- expect(result).to be false
43
- end
44
-
45
- it 'should: be invalid with string' do
46
- object = 'a'
47
- other = {:a => 'a', :b => 'b', :c => 'c'}
48
- result = IsSameDataStructureHelper::Analyser.new(object, other).perform
49
- expect(result).to be false
50
- end
51
-
52
- it 'should: be valid with depth 1' do
53
- object = {:a => {:c => 'c'}, :b => {:d => 'd'}}
54
- other = {:a => {:c => 'c'}, :b => {:d => 'd'}}
55
- result = IsSameDataStructureHelper::Analyser.new(object, other, 1).perform
56
- expect(result).to be true
57
- end
58
-
59
- it 'should: be invalid with depth 1' do
60
- object = {:a => {:c => 'c'}, :b => {:d => 'd'}}
61
- other = {:a => {:c => 'c'}, :b => {:e => 'e'}}
62
- result = IsSameDataStructureHelper::Analyser.new(object, other, 1).perform
63
- expect(result).to be false
64
- end
65
-
66
- it 'should: be invalid with depth 1 and nil' do
67
- object = {:a => {:c => 'c'}, :b => {:d => 'd'}}
68
- other = {:a => {:c => 'c'}, :b => nil}
69
- result = IsSameDataStructureHelper::Analyser.new(object, other, 1).perform
70
- expect(result).to be false
71
- end
72
-
73
- it 'should: be invalid with depth 1 and integer' do
74
- object = {:a => {:c => 'c'}, :b => {:d => 'd'}}
75
- other = {:a => {:c => 'c'}, :b => 1}
76
- result = IsSameDataStructureHelper::Analyser.new(object, other, 1).perform
77
- expect(result).to be false
78
- end
79
-
80
- it 'should: be invalid with depth 1 and string' do
81
- object = {:a => {:c => 'c'}, :b => {:d => 'd'}}
82
- other = {:a => {:c => 'c'}, :b => 'b'}
83
- result = IsSameDataStructureHelper::Analyser.new(object, other, 1).perform
84
- expect(result).to be false
85
- end
86
- end
87
- end