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.
- checksums.yaml +4 -4
- data/app/controllers/forest_liana/actions_controller.rb +44 -29
- data/app/controllers/forest_liana/application_controller.rb +2 -2
- data/app/controllers/forest_liana/associations_controller.rb +1 -1
- data/app/controllers/forest_liana/base_controller.rb +1 -1
- data/app/controllers/forest_liana/resources_controller.rb +6 -6
- data/app/serializers/forest_liana/intercom_attribute_serializer.rb +1 -1
- data/app/serializers/forest_liana/intercom_conversation_serializer.rb +1 -1
- data/app/serializers/forest_liana/mixpanel_event_serializer.rb +1 -1
- data/app/serializers/forest_liana/serializer_factory.rb +1 -1
- data/app/serializers/forest_liana/stat_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_bank_account_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_card_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_invoice_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_payment_serializer.rb +1 -1
- data/app/serializers/forest_liana/stripe_subscription_serializer.rb +1 -1
- data/app/services/forest_liana/apimap_sorter.rb +1 -0
- data/app/services/forest_liana/smart_action_field_validator.rb +49 -0
- data/config/initializers/errors.rb +17 -0
- data/config/initializers/logger.rb +16 -13
- data/lib/forest_liana.rb +2 -0
- data/lib/forest_liana/bootstrapper.rb +2 -2
- data/lib/forest_liana/schema_file_updater.rb +8 -0
- data/lib/forest_liana/version.rb +1 -1
- data/spec/config/initializers/logger_spec.rb +30 -0
- data/spec/lib/forest_liana/schema_file_updater_spec.rb +94 -0
- data/spec/requests/actions_controller_spec.rb +22 -20
- data/spec/services/forest_liana/smart_action_field_validator_spec.rb +70 -0
- metadata +12 -8
- data/app/helpers/forest_liana/is_same_data_structure_helper.rb +0 -44
- 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:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6be4888e921b11142c5bc7774e6a0c9ce9a91b7e0b8ceb8a812cff8e297b540a
         | 
| 4 | 
            +
              data.tar.gz: '043584e75c96d49b34a4ab6ec0e0f0315700eb86a7d061fb03de66c095840b1c'
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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. | 
| 31 | 
            -
                    ForestLiana::WidgetsHelper.set_field_widget( | 
| 32 | 
            -
                     | 
| 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 | 
            -
                   | 
| 39 | 
            -
             | 
| 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 | 
            -
                     | 
| 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,  | 
| 47 | 
            -
                  if result.nil? || !result.is_a?( | 
| 48 | 
            -
                    return render status: 500, json: { error: 'Error in smart action load hook: hook must return an  | 
| 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 | 
            -
             | 
| 51 | 
            -
                   | 
| 52 | 
            -
             | 
| 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 =  | 
| 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 | 
            -
                    #  | 
| 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,  | 
| 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 | 
            -
                   | 
| 101 | 
            -
                     | 
| 102 | 
            -
             | 
| 103 | 
            -
                     | 
| 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 | 
            -
             | 
| 106 | 
            -
             | 
| 116 | 
            +
                  # Get the smart action hook change context
         | 
| 117 | 
            +
                  context = get_smart_action_change_ctx(params[:fields], params[:changedField])
         | 
| 107 118 |  | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 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
         | 
| @@ -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  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
                       | 
| 10 | 
            -
                       | 
| 11 | 
            -
             | 
| 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 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 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'] = { | 
| 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
         | 
    
        data/lib/forest_liana/version.rb
    CHANGED
    
    
| @@ -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 | 
            -
                          ' | 
| 54 | 
            -
                             | 
| 55 | 
            -
                             | 
| 56 | 
            -
                             | 
| 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 | 
            -
                            ' | 
| 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 | 
            -
                           | 
| 81 | 
            -
                          context[:fields]
         | 
| 81 | 
            +
                          {}
         | 
| 82 82 | 
             
                        },
         | 
| 83 83 | 
             
                        :change => {
         | 
| 84 | 
            -
                            ' | 
| 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 | 
            -
                      ' | 
| 96 | 
            +
                      'on_foo_changed' => -> (context) {
         | 
| 97 97 | 
             
                        fields = context[:fields]
         | 
| 98 | 
            -
                        fields[ | 
| 99 | 
            -
                         | 
| 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 | 
            -
                            ' | 
| 111 | 
            +
                            'on_foo_changed' => -> (context) {
         | 
| 111 112 | 
             
                              fields = context[:fields]
         | 
| 112 | 
            -
                              fields[ | 
| 113 | 
            -
                               | 
| 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 | 
            -
             | 
| 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:  | 
| 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- | 
| 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:  | 
| 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/ | 
| 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
         |