forest_admin_datasource_toolkit 1.0.0.pre.beta.28 → 1.0.0.pre.beta.29
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/lib/forest_admin_datasource_toolkit/components/query/projection.rb +22 -0
 - data/lib/forest_admin_datasource_toolkit/decorators/collection_decorator.rb +3 -20
 - data/lib/forest_admin_datasource_toolkit/exceptions/validation_error.rb +9 -0
 - data/lib/forest_admin_datasource_toolkit/schema/concerns/primitive_types.rb +19 -0
 - data/lib/forest_admin_datasource_toolkit/validations/field_validator.rb +80 -0
 - data/lib/forest_admin_datasource_toolkit/validations/rules.rb +144 -0
 - data/lib/forest_admin_datasource_toolkit/validations/type_getter.rb +97 -0
 - data/lib/forest_admin_datasource_toolkit/version.rb +1 -1
 - metadata +7 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: eb59a941fffbdba03d6ce1966b69d7ce5415507d120591e7b76f3d642b6d83ec
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d5519a29087ec84a16dc0d4ccc433168f31e7d68a7e1906ff4a9e4fc882cc35c
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e30897a61d2675a5f63bc0cce508f3779b3c7e7c56c1b5b67051c4217216d47e3aa1c820c7e863c864864fc7357d0be14a0b6b1f422e82815ec7ac1cfbdb0531
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: efdbf2b8d9f8603a803c6c5cbe5c8092a2cdd5da4dd4c4f9258311653c9278b889d1e2304e6c9a9e87781cb607a583a86e41a3713dc5f68c79bb96a06711ac08
         
     | 
| 
         @@ -36,6 +36,28 @@ module ForestAdminDatasourceToolkit 
     | 
|
| 
       36 
36 
     | 
    
         
             
                    def nest(prefix: nil)
         
     | 
| 
       37 
37 
     | 
    
         
             
                      prefix ? Projection.new(map { |path| "#{prefix}:#{path}" }) : self
         
     | 
| 
       38 
38 
     | 
    
         
             
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    def unnest
         
     | 
| 
      
 41 
     | 
    
         
            +
                      prefix = first.split(':')[0]
         
     | 
| 
      
 42 
     | 
    
         
            +
                      raise 'Cannot unnest projection.' unless all? { |path| path.start_with?(prefix) }
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                      Projection.new(map { |path| path[prefix.length + 1, path.length - prefix.length - 1] })
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    def replace(...)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      Projection.new(
         
     | 
| 
      
 49 
     | 
    
         
            +
                        map(...)
         
     | 
| 
      
 50 
     | 
    
         
            +
                        .reduce(Projection.new) do |memo, path|
         
     | 
| 
      
 51 
     | 
    
         
            +
                          return memo.union([path]) if path.is_a?(String)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                          memo.union(path)
         
     | 
| 
      
 54 
     | 
    
         
            +
                        end
         
     | 
| 
      
 55 
     | 
    
         
            +
                      )
         
     | 
| 
      
 56 
     | 
    
         
            +
                    end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    def equals(other)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      length == other.length && all? { |field| other.include?(field) }
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
       39 
61 
     | 
    
         
             
                  end
         
     | 
| 
       40 
62 
     | 
    
         
             
                end
         
     | 
| 
       41 
63 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2,24 +2,14 @@ module ForestAdminDatasourceToolkit 
     | 
|
| 
       2 
2 
     | 
    
         
             
              module Decorators
         
     | 
| 
       3 
3 
     | 
    
         
             
                class CollectionDecorator < Collection
         
     | 
| 
       4 
4 
     | 
    
         
             
                  attr_reader :datasource, :child_collection, :last_schema
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_writer :parent
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
                  def initialize(child_collection, datasource)
         
     | 
| 
       7 
8 
     | 
    
         
             
                    super
         
     | 
| 
       8 
9 
     | 
    
         
             
                    @child_collection = child_collection
         
     | 
| 
       9 
10 
     | 
    
         
             
                    @datasource = datasource
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                     
     | 
| 
       12 
     | 
    
         
            -
                    # This is done like this, and not in the markSchemaAsDirty method, because we don't have
         
     | 
| 
       13 
     | 
    
         
            -
                    # a reference to parent collections from children.
         
     | 
| 
       14 
     | 
    
         
            -
                    return unless child_collection.is_a?(CollectionDecorator)
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                    child_collection.define_singleton_method(:mark_schema_as_dirty) do
         
     | 
| 
       17 
     | 
    
         
            -
                      # Call the original method (the child)
         
     | 
| 
       18 
     | 
    
         
            -
                      original_child_mark_schema_as_dirty.call(child_collection)
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
                      # Invalidate our schema (the parent)
         
     | 
| 
       21 
     | 
    
         
            -
                      mark_schema_as_dirty
         
     | 
| 
       22 
     | 
    
         
            -
                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
                    child_collection.parent = self if child_collection.is_a?(CollectionDecorator)
         
     | 
| 
       23 
13 
     | 
    
         
             
                  end
         
     | 
| 
       24 
14 
     | 
    
         | 
| 
       25 
15 
     | 
    
         
             
                  def native_driver
         
     | 
| 
         @@ -87,6 +77,7 @@ module ForestAdminDatasourceToolkit 
     | 
|
| 
       87 
77 
     | 
    
         | 
| 
       88 
78 
     | 
    
         
             
                  def mark_schema_as_dirty
         
     | 
| 
       89 
79 
     | 
    
         
             
                    @last_schema = nil
         
     | 
| 
      
 80 
     | 
    
         
            +
                    @parent&.mark_schema_as_dirty
         
     | 
| 
       90 
81 
     | 
    
         
             
                  end
         
     | 
| 
       91 
82 
     | 
    
         | 
| 
       92 
83 
     | 
    
         
             
                  def refine_filter(_caller, filter = nil)
         
     | 
| 
         @@ -96,14 +87,6 @@ module ForestAdminDatasourceToolkit 
     | 
|
| 
       96 
87 
     | 
    
         
             
                  def refine_schema(sub_schema)
         
     | 
| 
       97 
88 
     | 
    
         
             
                    sub_schema
         
     | 
| 
       98 
89 
     | 
    
         
             
                  end
         
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
                  private
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                  def push_customization(customization)
         
     | 
| 
       103 
     | 
    
         
            -
                    @stack.queue_customization(customization)
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
                    self
         
     | 
| 
       106 
     | 
    
         
            -
                  end
         
     | 
| 
       107 
90 
     | 
    
         
             
                end
         
     | 
| 
       108 
91 
     | 
    
         
             
              end
         
     | 
| 
       109 
92 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ForestAdminDatasourceToolkit
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Schema
         
     | 
| 
      
 3 
     | 
    
         
            +
                module Concerns
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class PrimitiveTypes
         
     | 
| 
      
 5 
     | 
    
         
            +
                    BINARY = 'Binary'.freeze
         
     | 
| 
      
 6 
     | 
    
         
            +
                    BOOLEAN = 'Boolean'.freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                    DATE = 'Date'.freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
                    DATE_ONLY = 'Dateonly'.freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
                    ENUM = 'Enum'.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                    JSON = 'Json'.freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
                    NUMBER = 'Number'.freeze
         
     | 
| 
      
 12 
     | 
    
         
            +
                    POINT = 'Point'.freeze
         
     | 
| 
      
 13 
     | 
    
         
            +
                    STRING = 'String'.freeze
         
     | 
| 
      
 14 
     | 
    
         
            +
                    TIME_ONLY = 'Timeonly'.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
                    UUID = 'Uuid'.freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ForestAdminDatasourceToolkit
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validations
         
     | 
| 
      
 3 
     | 
    
         
            +
                class FieldValidator
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include ForestAdminDatasourceToolkit::Schema
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def self.validate(collection, field, values = nil)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    dot_index = field.index(':')
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    if dot_index.nil?
         
     | 
| 
      
 10 
     | 
    
         
            +
                      schema = collection.schema[:fields][field]
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                      raise Exceptions::ValidationError, "Column not found: '#{collection.name}.#{field}'" if schema.nil?
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                      if schema.type != 'Column'
         
     | 
| 
      
 15 
     | 
    
         
            +
                        raise Exceptions::ValidationError,
         
     | 
| 
      
 16 
     | 
    
         
            +
                              "Unexpected field type: '#{collection.name}.#{field}' (found '#{schema.type}' expected 'Column')"
         
     | 
| 
      
 17 
     | 
    
         
            +
                      end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                      values&.each do |value|
         
     | 
| 
      
 20 
     | 
    
         
            +
                        validate_value(field, schema, value)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      end
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                      prefix = field[0, dot_index]
         
     | 
| 
      
 24 
     | 
    
         
            +
                      schema = collection.schema[:fields][prefix]
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                      raise Exceptions::ValidationError, "Relation not found: '#{collection.name}.#{prefix}'" if schema.nil?
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                      if schema.type != 'ManyToOne' && schema.type != 'OneToOne'
         
     | 
| 
      
 29 
     | 
    
         
            +
                        raise Exceptions::ValidationError,
         
     | 
| 
      
 30 
     | 
    
         
            +
                              "Unexpected field type: '#{collection.name}.#{prefix}' (found '#{schema.type}' expected 'ManyToOne' or 'OneToOne')"
         
     | 
| 
      
 31 
     | 
    
         
            +
                      end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                      suffix = field[dot_index + 1, field.length - dot_index - 1]
         
     | 
| 
      
 34 
     | 
    
         
            +
                      association = collection.datasource.get_collection(schema.foreign_collection)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      validate(association, suffix, values)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def self.validate_value_for_id(field, schema, value)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    validate_value(field, schema, value, [schema.column_type])
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def self.validate_value(field, schema, value, allowed_types = nil)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    allowed_types ||= Rules.get_allowed_types_for_column_type(schema.column_type)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    # TODO: FIXME: handle complex type from ColumnType
         
     | 
| 
      
 47 
     | 
    
         
            +
                    # if schema.column_type != PrimitiveType::STRING
         
     | 
| 
      
 48 
     | 
    
         
            +
                    # end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    type = TypeGetter.get(value, schema.column_type)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    unless allowed_types.include?(type)
         
     | 
| 
      
 53 
     | 
    
         
            +
                      raise Exceptions::ValidationError,
         
     | 
| 
      
 54 
     | 
    
         
            +
                            "The given value has a wrong type for '#{field}': #{value}.\n Expects #{allowed_types}"
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    return unless value && schema.column_type == PrimitiveType::ENUM
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    check_enum_value(schema, value)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def self.validate_name(collection_name, name)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    return unless name.include?(' ')
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    sanitized_name = name.gsub(/ (.)/, &:upcase)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    raise Exceptions::ValidationError,
         
     | 
| 
      
 67 
     | 
    
         
            +
                          "The name of field '#{name}' you configured on '#{collection_name}' must not contain space. Something like '#{sanitized_name}' should work has expected."
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def self.check_enum_value(column_schema, enum_value)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    is_enum_allowed = column_schema.enum_values.include?(enum_value)
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    return if is_enum_allowed
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    raise Exceptions::ValidationError,
         
     | 
| 
      
 76 
     | 
    
         
            +
                          "The given enum value(s) #{enum_value} is not listed in #{column_schema.enum_values}"
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              end
         
     | 
| 
      
 80 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,144 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ForestAdminDatasourceToolkit
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validations
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Rules
         
     | 
| 
      
 4 
     | 
    
         
            +
                  include ForestAdminDatasourceToolkit::Schema
         
     | 
| 
      
 5 
     | 
    
         
            +
                  include ForestAdminDatasourceToolkit::Components::Query::ConditionTree
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  BASE_OPERATORS = [Operators::BLANK, Operators::EQUAL, Operators::MISSING, Operators::NOT_EQUAL,
         
     | 
| 
      
 8 
     | 
    
         
            +
                                    Operators::PRESENT].freeze
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  ARRAY_OPERATORS = [Operators::IN, Operators::NOT_IN, Operators::INCLUDES_ALL].freeze
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  BASE_DATEONLY_OPERATORS = [
         
     | 
| 
      
 13 
     | 
    
         
            +
                    Operators::TODAY,
         
     | 
| 
      
 14 
     | 
    
         
            +
                    Operators::YESTERDAY,
         
     | 
| 
      
 15 
     | 
    
         
            +
                    Operators::PREVIOUS_X_DAYS,
         
     | 
| 
      
 16 
     | 
    
         
            +
                    Operators::PREVIOUS_WEEK,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    Operators::PREVIOUS_MONTH,
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Operators::PREVIOUS_QUARTER,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    Operators::PREVIOUS_YEAR,
         
     | 
| 
      
 20 
     | 
    
         
            +
                    Operators::PREVIOUS_X_DAYS_TO_DATE,
         
     | 
| 
      
 21 
     | 
    
         
            +
                    Operators::PREVIOUS_WEEK_TO_DATE,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    Operators::PREVIOUS_MONTH_TO_DATE,
         
     | 
| 
      
 23 
     | 
    
         
            +
                    Operators::PREVIOUS_QUARTER_TO_DATE,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    Operators::PREVIOUS_YEAR_TO_DATE,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    Operators::PAST,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    Operators::FUTURE,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    Operators::BEFORE,
         
     | 
| 
      
 28 
     | 
    
         
            +
                    Operators::AFTER
         
     | 
| 
      
 29 
     | 
    
         
            +
                  ].freeze
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def self.get_allowed_operators_for_column_type(primitive_type = nil)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    allowed_operators = {
         
     | 
| 
      
 33 
     | 
    
         
            +
                      PrimitiveType::STRING => [
         
     | 
| 
      
 34 
     | 
    
         
            +
                        *Rules::BASE_OPERATORS,
         
     | 
| 
      
 35 
     | 
    
         
            +
                        *Rules::ARRAY_OPERATORS,
         
     | 
| 
      
 36 
     | 
    
         
            +
                        Operators::CONTAINS,
         
     | 
| 
      
 37 
     | 
    
         
            +
                        Operators::NOT_CONTAINS,
         
     | 
| 
      
 38 
     | 
    
         
            +
                        Operators::ENDS_WITH,
         
     | 
| 
      
 39 
     | 
    
         
            +
                        Operators::STARTS_WITH,
         
     | 
| 
      
 40 
     | 
    
         
            +
                        Operators::LONGER_THAN,
         
     | 
| 
      
 41 
     | 
    
         
            +
                        Operators::SHORTER_THAN,
         
     | 
| 
      
 42 
     | 
    
         
            +
                        Operators::LIKE,
         
     | 
| 
      
 43 
     | 
    
         
            +
                        Operators::I_LIKE,
         
     | 
| 
      
 44 
     | 
    
         
            +
                        Operators::I_CONTAINS,
         
     | 
| 
      
 45 
     | 
    
         
            +
                        Operators::I_ENDS_WITH,
         
     | 
| 
      
 46 
     | 
    
         
            +
                        Operators::I_STARTS_WITH
         
     | 
| 
      
 47 
     | 
    
         
            +
                      ],
         
     | 
| 
      
 48 
     | 
    
         
            +
                      PrimitiveType::NUMBER => [
         
     | 
| 
      
 49 
     | 
    
         
            +
                        *Rules::BASE_OPERATORS,
         
     | 
| 
      
 50 
     | 
    
         
            +
                        *Rules::ARRAY_OPERATORS,
         
     | 
| 
      
 51 
     | 
    
         
            +
                        Operators::GREATER_THAN,
         
     | 
| 
      
 52 
     | 
    
         
            +
                        Operators::LESS_THAN
         
     | 
| 
      
 53 
     | 
    
         
            +
                      ],
         
     | 
| 
      
 54 
     | 
    
         
            +
                      PrimitiveType::DATE => [
         
     | 
| 
      
 55 
     | 
    
         
            +
                        *Rules::BASE_OPERATORS,
         
     | 
| 
      
 56 
     | 
    
         
            +
                        *Rules::BASE_DATEONLY_OPERATORS,
         
     | 
| 
      
 57 
     | 
    
         
            +
                        Operators::BEFORE_X_HOURS_AGO,
         
     | 
| 
      
 58 
     | 
    
         
            +
                        Operators::AFTER_X_HOURS_AGO
         
     | 
| 
      
 59 
     | 
    
         
            +
                      ],
         
     | 
| 
      
 60 
     | 
    
         
            +
                      PrimitiveType::TIMEONLY => [
         
     | 
| 
      
 61 
     | 
    
         
            +
                        *Rules::BASE_OPERATORS,
         
     | 
| 
      
 62 
     | 
    
         
            +
                        Operators::LESS_THAN,
         
     | 
| 
      
 63 
     | 
    
         
            +
                        Operators::GREATER_THAN
         
     | 
| 
      
 64 
     | 
    
         
            +
                      ],
         
     | 
| 
      
 65 
     | 
    
         
            +
                      PrimitiveType::JSON => [
         
     | 
| 
      
 66 
     | 
    
         
            +
                        Operators::BLANK,
         
     | 
| 
      
 67 
     | 
    
         
            +
                        Operators::MISSING,
         
     | 
| 
      
 68 
     | 
    
         
            +
                        Operators::PRESENT
         
     | 
| 
      
 69 
     | 
    
         
            +
                      ],
         
     | 
| 
      
 70 
     | 
    
         
            +
                      PrimitiveType::DATEONLY => [*Rules::BASE_OPERATORS, *Rules::BASE_DATEONLY_OPERATORS],
         
     | 
| 
      
 71 
     | 
    
         
            +
                      PrimitiveType::ENUM => [*Rules::BASE_OPERATORS, *Rules::ARRAY_OPERATORS],
         
     | 
| 
      
 72 
     | 
    
         
            +
                      PrimitiveType::UUID => [*Rules::BASE_OPERATORS, *Rules::ARRAY_OPERATORS],
         
     | 
| 
      
 73 
     | 
    
         
            +
                      PrimitiveType::BOOLEAN => Rules::BASE_OPERATORS,
         
     | 
| 
      
 74 
     | 
    
         
            +
                      PrimitiveType::POINT => Rules::BASE_OPERATORS
         
     | 
| 
      
 75 
     | 
    
         
            +
                    }
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    primitive_type ? allowed_operators[primitive_type] : allowed_operators
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  def self.get_allowed_types_for_column_type(primitive_type = nil)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    allowed_types = {
         
     | 
| 
      
 82 
     | 
    
         
            +
                      PrimitiveType::STRING => [PrimitiveType::STRING, nil],
         
     | 
| 
      
 83 
     | 
    
         
            +
                      PrimitiveType::NUMBER => [PrimitiveType::NUMBER, nil],
         
     | 
| 
      
 84 
     | 
    
         
            +
                      PrimitiveType::DATEONLY => [PrimitiveType::DATEONLY, nil],
         
     | 
| 
      
 85 
     | 
    
         
            +
                      PrimitiveType::DATE => [PrimitiveType::DATE, nil],
         
     | 
| 
      
 86 
     | 
    
         
            +
                      PrimitiveType::TIMEONLY => [PrimitiveType::TIMEONLY, nil],
         
     | 
| 
      
 87 
     | 
    
         
            +
                      PrimitiveType::ENUM => [PrimitiveType::ENUM, nil],
         
     | 
| 
      
 88 
     | 
    
         
            +
                      PrimitiveType::UUID => [PrimitiveType::UUID, nil],
         
     | 
| 
      
 89 
     | 
    
         
            +
                      PrimitiveType::JSON => [PrimitiveType::JSON, nil],
         
     | 
| 
      
 90 
     | 
    
         
            +
                      PrimitiveType::BOOLEAN => [PrimitiveType::BOOLEAN, nil],
         
     | 
| 
      
 91 
     | 
    
         
            +
                      PrimitiveType::POINT => [PrimitiveType::POINT, nil]
         
     | 
| 
      
 92 
     | 
    
         
            +
                    }
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                    primitive_type ? allowed_types[primitive_type] : allowed_types
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def self.compute_allowed_types_for_operators
         
     | 
| 
      
 98 
     | 
    
         
            +
                    get_allowed_operators_for_column_type.keys.reduce do |result, type|
         
     | 
| 
      
 99 
     | 
    
         
            +
                      allowed_operators = get_allowed_operators_for_column_type(type)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      allowed_operators.each do |operator|
         
     | 
| 
      
 101 
     | 
    
         
            +
                        if result[operator]
         
     | 
| 
      
 102 
     | 
    
         
            +
                          result[operator] << type
         
     | 
| 
      
 103 
     | 
    
         
            +
                        else
         
     | 
| 
      
 104 
     | 
    
         
            +
                          result[operator] = [type]
         
     | 
| 
      
 105 
     | 
    
         
            +
                        end
         
     | 
| 
      
 106 
     | 
    
         
            +
                      end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                      result
         
     | 
| 
      
 109 
     | 
    
         
            +
                    end
         
     | 
| 
      
 110 
     | 
    
         
            +
                  end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  def self.get_allowed_types_for_operator(operator = nil)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    no_type_allowed = [nil]
         
     | 
| 
      
 114 
     | 
    
         
            +
                    allowed_types = compute_allowed_types_for_operators
         
     | 
| 
      
 115 
     | 
    
         
            +
                    merged = allowed_types.merge(
         
     | 
| 
      
 116 
     | 
    
         
            +
                      Operators::IN => allowed_types[Operators::IN] + [nil],
         
     | 
| 
      
 117 
     | 
    
         
            +
                      Operators::NOT_IN => allowed_types[Operators::NOT_IN] + [nil],
         
     | 
| 
      
 118 
     | 
    
         
            +
                      Operators::INCLUDES_ALL => allowed_types[Operators::INCLUDES_ALL] + [nil],
         
     | 
| 
      
 119 
     | 
    
         
            +
                      Operators::BLANK => no_type_allowed,
         
     | 
| 
      
 120 
     | 
    
         
            +
                      Operators::MISSING => no_type_allowed,
         
     | 
| 
      
 121 
     | 
    
         
            +
                      Operators::PRESENT => no_type_allowed,
         
     | 
| 
      
 122 
     | 
    
         
            +
                      Operators::YESTERDAY => no_type_allowed,
         
     | 
| 
      
 123 
     | 
    
         
            +
                      Operators::TODAY => no_type_allowed,
         
     | 
| 
      
 124 
     | 
    
         
            +
                      Operators::PREVIOUS_QUARTER => no_type_allowed,
         
     | 
| 
      
 125 
     | 
    
         
            +
                      Operators::PREVIOUS_YEAR => no_type_allowed,
         
     | 
| 
      
 126 
     | 
    
         
            +
                      Operators::PREVIOUS_MONTH => no_type_allowed,
         
     | 
| 
      
 127 
     | 
    
         
            +
                      Operators::PREVIOUS_WEEK => no_type_allowed,
         
     | 
| 
      
 128 
     | 
    
         
            +
                      Operators::PAST => no_type_allowed,
         
     | 
| 
      
 129 
     | 
    
         
            +
                      Operators::FUTURE => no_type_allowed,
         
     | 
| 
      
 130 
     | 
    
         
            +
                      Operators::PREVIOUS_WEEK_TO_DATE => no_type_allowed,
         
     | 
| 
      
 131 
     | 
    
         
            +
                      Operators::PREVIOUS_MONTH_TO_DATE => no_type_allowed,
         
     | 
| 
      
 132 
     | 
    
         
            +
                      Operators::PREVIOUS_QUARTER_TO_DATE => no_type_allowed,
         
     | 
| 
      
 133 
     | 
    
         
            +
                      Operators::PREVIOUS_YEAR_TO_DATE => no_type_allowed,
         
     | 
| 
      
 134 
     | 
    
         
            +
                      Operators::PREVIOUS_X_DAYS_TO_DATE => ['Number'],
         
     | 
| 
      
 135 
     | 
    
         
            +
                      Operators::PREVIOUS_X_DAYS => ['Number'],
         
     | 
| 
      
 136 
     | 
    
         
            +
                      Operators::BEFORE_X_HOURS_AGO => ['Number'],
         
     | 
| 
      
 137 
     | 
    
         
            +
                      Operators::AFTER_X_HOURS_AGO => ['Number']
         
     | 
| 
      
 138 
     | 
    
         
            +
                    )
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                    operator ? merged[operator] : merged
         
     | 
| 
      
 141 
     | 
    
         
            +
                  end
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
              end
         
     | 
| 
      
 144 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,97 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'date'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'time'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'openssl'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            module ForestAdminDatasourceToolkit
         
     | 
| 
      
 6 
     | 
    
         
            +
              module Validations
         
     | 
| 
      
 7 
     | 
    
         
            +
                class TypeGetter
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include ForestAdminDatasourceToolkit::Schema::Concerns
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.get(value, type_context)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    return PrimitiveTypes::JSON if type_context == PrimitiveTypes::JSON
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    return get_type_from_string(value, type_context) if value.is_a?(String)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    return PrimitiveTypes::NUMBER if value.is_a?(Numeric)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    return PrimitiveTypes::DATE if value.is_a?(Date)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    return PrimitiveTypes::BOOLEAN if value.is_a?(TrueClass) || value.is_a?(FalseClass)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    return PrimitiveTypes::BINARY if value.is_a?(buffer)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 26 
     | 
    
         
            +
                    include ForestAdminDatasourceToolkit::Schema::Concerns
         
     | 
| 
      
 27 
     | 
    
         
            +
                    def get_date_type(value)
         
     | 
| 
      
 28 
     | 
    
         
            +
                      return PrimitiveTypes::DATE_ONLY if date?(value) && Date.parse(value).iso8601 == value
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      if time?(value) && (Time.parse(value).strftime('%H:%M:%S.%L') == value ||
         
     | 
| 
      
 31 
     | 
    
         
            +
                        Time.parse(value).strftime('%H:%M:%S') == value)
         
     | 
| 
      
 32 
     | 
    
         
            +
                        return PrimitiveTypes::TIME_ONLY
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                      PrimitiveTypes::DATE
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    def get_type_from_string(value, type_context)
         
     | 
| 
      
 39 
     | 
    
         
            +
                      return type_context if [PrimitiveTypes::ENUM, PrimitiveTypes::STRING].include?(type_context)
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                      return PrimitiveTypes::UUID if uuid?(value)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                      return get_date_type(value) if valid_date?(value) && [PrimitiveTypes::DATE, PrimitiveTypes::DATE_ONLY,
         
     | 
| 
      
 44 
     | 
    
         
            +
                                                                            PrimitiveTypes::TIME_ONLY].include?(type_context)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                      return PrimitiveTypes::POINT if point?(value, type_context)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      PrimitiveTypes::STRING
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    def valid_date?(value)
         
     | 
| 
      
 52 
     | 
    
         
            +
                      date?(value) || time?(value)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    def date?(value)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      true if Date.parse(value)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    rescue ArgumentError
         
     | 
| 
      
 58 
     | 
    
         
            +
                      false
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    def time?(value)
         
     | 
| 
      
 62 
     | 
    
         
            +
                      true if Time.parse(value)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    rescue ArgumentError
         
     | 
| 
      
 64 
     | 
    
         
            +
                      false
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    def number?(value)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      true if Float(value)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    rescue ArgumentError
         
     | 
| 
      
 70 
     | 
    
         
            +
                      false
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                    def point?(value, type_context)
         
     | 
| 
      
 74 
     | 
    
         
            +
                      potential_point = value.split(',')
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                      potential_point.length == 2 && type_context == PrimitiveTypes::POINT && potential_point.all? do |point|
         
     | 
| 
      
 77 
     | 
    
         
            +
                        number?(point) && get(point.to_i, PrimitiveTypes::NUMBER) == PrimitiveTypes::NUMBER
         
     | 
| 
      
 78 
     | 
    
         
            +
                      end
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    def uuid?(uuid)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      format = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                      true if format.match?(uuid.to_s.downcase)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    def buffer
         
     | 
| 
      
 88 
     | 
    
         
            +
                      if defined?(IO::Buffer)
         
     | 
| 
      
 89 
     | 
    
         
            +
                        IO::Buffer
         
     | 
| 
      
 90 
     | 
    
         
            +
                      else
         
     | 
| 
      
 91 
     | 
    
         
            +
                        OpenSSL::Buffering::Buffer
         
     | 
| 
      
 92 
     | 
    
         
            +
                      end
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
              end
         
     | 
| 
      
 97 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: forest_admin_datasource_toolkit
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.0.0.pre.beta. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.0.0.pre.beta.29
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Matthieu
         
     | 
| 
         @@ -9,7 +9,7 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2024-01- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2024-01-24 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: activesupport
         
     | 
| 
         @@ -85,7 +85,9 @@ files: 
     | 
|
| 
       85 
85 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/decorators/collection_decorator.rb
         
     | 
| 
       86 
86 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/decorators/datasource_decorator.rb
         
     | 
| 
       87 
87 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/exceptions/forest_exception.rb
         
     | 
| 
      
 88 
     | 
    
         
            +
            - lib/forest_admin_datasource_toolkit/exceptions/validation_error.rb
         
     | 
| 
       88 
89 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/schema/column_schema.rb
         
     | 
| 
      
 90 
     | 
    
         
            +
            - lib/forest_admin_datasource_toolkit/schema/concerns/primitive_types.rb
         
     | 
| 
       89 
91 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/schema/primitive_type.rb
         
     | 
| 
       90 
92 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/schema/relation_schema.rb
         
     | 
| 
       91 
93 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/schema/relations/many_to_many_schema.rb
         
     | 
| 
         @@ -96,6 +98,9 @@ files: 
     | 
|
| 
       96 
98 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/utils/record.rb
         
     | 
| 
       97 
99 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/utils/schema.rb
         
     | 
| 
       98 
100 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/validations/chart_validator.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - lib/forest_admin_datasource_toolkit/validations/field_validator.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
            - lib/forest_admin_datasource_toolkit/validations/rules.rb
         
     | 
| 
      
 103 
     | 
    
         
            +
            - lib/forest_admin_datasource_toolkit/validations/type_getter.rb
         
     | 
| 
       99 
104 
     | 
    
         
             
            - lib/forest_admin_datasource_toolkit/version.rb
         
     | 
| 
       100 
105 
     | 
    
         
             
            - sig/forest_admin_datasource_toolkit.rbs
         
     | 
| 
       101 
106 
     | 
    
         
             
            - sig/forest_admin_datasource_toolkit/collection.rbs
         
     |