forest_admin_datasource_toolkit 1.0.0.pre.beta.27 → 1.0.0.pre.beta.29
Sign up to get free protection for your applications and to get access to all the features.
- 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
|