forest_admin_datasource_toolkit 1.0.0.pre.beta.27 → 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
|