openapi3_parser 0.4.0 → 0.5.0
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/.rubocop.yml +2 -0
- data/CHANGELOG.md +8 -0
- data/README.md +1 -1
- data/TODO.md +4 -4
- data/lib/openapi3_parser/array_sentence.rb +12 -0
- data/lib/openapi3_parser/cautious_dig.rb +39 -0
- data/lib/openapi3_parser/context.rb +53 -1
- data/lib/openapi3_parser/context/location.rb +1 -0
- data/lib/openapi3_parser/context/pointer.rb +67 -5
- data/lib/openapi3_parser/document.rb +45 -4
- data/lib/openapi3_parser/error.rb +9 -0
- data/lib/openapi3_parser/node/array.rb +14 -4
- data/lib/openapi3_parser/node/map.rb +45 -3
- data/lib/openapi3_parser/node/object.rb +25 -5
- data/lib/openapi3_parser/node_factory.rb +0 -150
- data/lib/openapi3_parser/node_factory/array.rb +198 -0
- data/lib/openapi3_parser/node_factory/callback.rb +24 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/components.rb +24 -25
- data/lib/openapi3_parser/{node_factories → node_factory}/contact.rb +5 -6
- data/lib/openapi3_parser/{node_factories → node_factory}/discriminator.rb +6 -7
- data/lib/openapi3_parser/{node_factories → node_factory}/encoding.rb +6 -8
- data/lib/openapi3_parser/{node_factories → node_factory}/example.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/external_documentation.rb +4 -5
- data/lib/openapi3_parser/node_factory/field.rb +129 -0
- data/lib/openapi3_parser/node_factory/fields/reference.rb +54 -18
- data/lib/openapi3_parser/{node_factories → node_factory}/header.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/info.rb +6 -7
- data/lib/openapi3_parser/{node_factories → node_factory}/license.rb +4 -5
- data/lib/openapi3_parser/{node_factories → node_factory}/link.rb +6 -8
- data/lib/openapi3_parser/node_factory/map.rb +206 -21
- data/lib/openapi3_parser/{node_factories → node_factory}/media_type.rb +17 -16
- data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flow.rb +2 -4
- data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flows.rb +4 -6
- data/lib/openapi3_parser/node_factory/object.rb +66 -63
- data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +50 -0
- data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +88 -0
- data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +96 -0
- data/lib/openapi3_parser/node_factory/object_factory/validator.rb +172 -0
- data/lib/openapi3_parser/node_factory/openapi.rb +65 -0
- data/lib/openapi3_parser/node_factory/operation.rb +87 -0
- data/lib/openapi3_parser/node_factory/optional_reference.rb +7 -3
- data/lib/openapi3_parser/{node_factories → node_factory}/parameter.rb +16 -22
- data/lib/openapi3_parser/node_factory/parameter_like.rb +42 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/path_item.rb +27 -29
- data/lib/openapi3_parser/{node_factories → node_factory}/paths.rb +21 -27
- data/lib/openapi3_parser/node_factory/recursive_pointer.rb +17 -0
- data/lib/openapi3_parser/node_factory/reference.rb +48 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/request_body.rb +15 -19
- data/lib/openapi3_parser/{node_factories → node_factory}/response.rb +18 -21
- data/lib/openapi3_parser/node_factory/responses.rb +51 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/schema.rb +33 -33
- data/lib/openapi3_parser/node_factory/security_requirement.rb +21 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/security_scheme.rb +4 -6
- data/lib/openapi3_parser/{node_factories → node_factory}/server.rb +6 -8
- data/lib/openapi3_parser/{node_factories → node_factory}/server_variable.rb +10 -12
- data/lib/openapi3_parser/{node_factories → node_factory}/tag.rb +4 -6
- data/lib/openapi3_parser/node_factory/type_checker.rb +103 -0
- data/lib/openapi3_parser/{node_factories → node_factory}/xml.rb +4 -5
- data/lib/openapi3_parser/source/reference_resolver.rb +3 -3
- data/lib/openapi3_parser/validation/error.rb +9 -0
- data/lib/openapi3_parser/validation/input_validator.rb +18 -0
- data/lib/openapi3_parser/validation/validatable.rb +44 -0
- data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +121 -0
- data/lib/openapi3_parser/validators/required_fields.rb +37 -0
- data/lib/openapi3_parser/validators/unexpected_fields.rb +52 -0
- data/lib/openapi3_parser/version.rb +1 -1
- metadata +48 -38
- data/lib/openapi3_parser/node_factories/array.rb +0 -114
- data/lib/openapi3_parser/node_factories/callback.rb +0 -27
- data/lib/openapi3_parser/node_factories/map.rb +0 -120
- data/lib/openapi3_parser/node_factories/openapi.rb +0 -62
- data/lib/openapi3_parser/node_factories/operation.rb +0 -84
- data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +0 -41
- data/lib/openapi3_parser/node_factories/reference.rb +0 -35
- data/lib/openapi3_parser/node_factories/responses.rb +0 -60
- data/lib/openapi3_parser/node_factories/security_requirement.rb +0 -26
- data/lib/openapi3_parser/node_factory/field_config.rb +0 -88
- data/lib/openapi3_parser/node_factory/object/node_builder.rb +0 -97
- data/lib/openapi3_parser/node_factory/object/validator.rb +0 -176
@@ -1,77 +1,87 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "forwardable"
|
4
4
|
|
5
5
|
require "openapi3_parser/context"
|
6
|
-
require "openapi3_parser/node_factory"
|
7
|
-
require "openapi3_parser/node_factory/
|
8
|
-
require "openapi3_parser/node_factory/object/node_builder"
|
9
|
-
require "openapi3_parser/node_factory/object/validator"
|
10
|
-
require "openapi3_parser/validation/error_collection"
|
6
|
+
require "openapi3_parser/node_factory/object_factory/dsl"
|
7
|
+
require "openapi3_parser/node_factory/object_factory/node_builder"
|
11
8
|
|
12
9
|
module Openapi3Parser
|
13
10
|
module NodeFactory
|
14
|
-
|
15
|
-
|
11
|
+
class Object
|
12
|
+
extend Forwardable
|
13
|
+
extend ObjectFactory::Dsl
|
14
|
+
|
15
|
+
def_delegators "self.class",
|
16
|
+
:field_configs,
|
17
|
+
:allowed_extensions?,
|
18
|
+
:mutually_exclusive_fields,
|
19
|
+
:allowed_default?,
|
20
|
+
:validations
|
21
|
+
|
22
|
+
attr_reader :context, :data
|
23
|
+
|
24
|
+
def initialize(context)
|
25
|
+
@context = context
|
26
|
+
@data = build_data(context.input)
|
27
|
+
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@field_configs[name] = FieldConfig.new(options)
|
21
|
-
end
|
29
|
+
def resolved_input
|
30
|
+
@resolved_input ||= build_resolved_input
|
31
|
+
end
|
22
32
|
|
23
|
-
|
24
|
-
|
25
|
-
|
33
|
+
def raw_input
|
34
|
+
context.input
|
35
|
+
end
|
26
36
|
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
def nil_input?
|
38
|
+
context.input.nil?
|
39
|
+
end
|
30
40
|
|
31
|
-
|
32
|
-
|
33
|
-
|
41
|
+
def valid?
|
42
|
+
errors.empty?
|
43
|
+
end
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
def errors
|
46
|
+
@errors ||= ObjectFactory::NodeBuilder.errors(self)
|
47
|
+
end
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
fields: fields, required: required
|
43
|
-
)
|
44
|
-
end
|
49
|
+
def node
|
50
|
+
@node ||= build_node
|
51
|
+
end
|
45
52
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
53
|
+
def can_use_default?
|
54
|
+
true
|
49
55
|
end
|
50
56
|
|
51
|
-
def
|
52
|
-
|
53
|
-
base.extend(ClassMethods)
|
54
|
-
base.class_eval do
|
55
|
-
input_type Hash
|
56
|
-
end
|
57
|
+
def default
|
58
|
+
nil
|
57
59
|
end
|
58
60
|
|
59
|
-
def
|
60
|
-
|
61
|
+
def allowed_fields
|
62
|
+
field_configs.keys
|
61
63
|
end
|
62
64
|
|
63
|
-
def
|
64
|
-
|
65
|
+
def required_fields
|
66
|
+
field_configs.each_with_object([]) do |(key, config), memo|
|
67
|
+
memo << key if config.required?
|
68
|
+
end
|
65
69
|
end
|
66
70
|
|
67
|
-
def
|
68
|
-
self.class.
|
71
|
+
def inspect
|
72
|
+
%{#{self.class.name}(#{context.source_location.inspect})}
|
69
73
|
end
|
70
74
|
|
71
75
|
private
|
72
76
|
|
73
|
-
def
|
74
|
-
|
77
|
+
def build_data(raw_input)
|
78
|
+
use_default = nil_input? || !raw_input.is_a?(::Hash)
|
79
|
+
return if use_default && default.nil?
|
80
|
+
process_data(use_default ? default : raw_input)
|
81
|
+
end
|
82
|
+
|
83
|
+
def process_data(raw_data)
|
84
|
+
field_configs.each_with_object(raw_data.dup) do |(field, config), memo|
|
75
85
|
memo[field] = nil unless memo[field]
|
76
86
|
next unless config.factory?
|
77
87
|
next_context = Context.next_field(context, field)
|
@@ -79,22 +89,10 @@ module Openapi3Parser
|
|
79
89
|
end
|
80
90
|
end
|
81
91
|
|
82
|
-
def validate_input
|
83
|
-
validator = Validator.new(processed_input, self)
|
84
|
-
Validation::ErrorCollection.combine(super, validator.errors)
|
85
|
-
end
|
86
|
-
|
87
|
-
def build_node(input)
|
88
|
-
data = NodeBuilder.new(input, self).data
|
89
|
-
build_object(data, context)
|
90
|
-
end
|
91
|
-
|
92
|
-
def build_object(data, _context)
|
93
|
-
data
|
94
|
-
end
|
95
|
-
|
96
92
|
def build_resolved_input
|
97
|
-
|
93
|
+
return unless data
|
94
|
+
|
95
|
+
data.each_with_object({}) do |(key, value), memo|
|
98
96
|
next if value.respond_to?(:nil_input?) && value.nil_input?
|
99
97
|
memo[key] = if value.respond_to?(:resolved_input)
|
100
98
|
value.resolved_input
|
@@ -103,6 +101,11 @@ module Openapi3Parser
|
|
103
101
|
end
|
104
102
|
end
|
105
103
|
end
|
104
|
+
|
105
|
+
def build_node
|
106
|
+
data = ObjectFactory::NodeBuilder.node_data(self)
|
107
|
+
build_object(data, context) if data
|
108
|
+
end
|
106
109
|
end
|
107
110
|
end
|
108
111
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
require "openapi3_parser/node_factory/object_factory/field_config"
|
6
|
+
|
7
|
+
module Openapi3Parser
|
8
|
+
module NodeFactory
|
9
|
+
module ObjectFactory
|
10
|
+
module Dsl
|
11
|
+
def field(name, **options)
|
12
|
+
@field_configs ||= {}
|
13
|
+
@field_configs[name] = FieldConfig.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def field_configs
|
17
|
+
@field_configs || {}
|
18
|
+
end
|
19
|
+
|
20
|
+
def allow_extensions
|
21
|
+
@allow_extensions = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def allowed_extensions?
|
25
|
+
@allow_extensions == true
|
26
|
+
end
|
27
|
+
|
28
|
+
def mutually_exclusive(*fields, required: false)
|
29
|
+
@mutually_exclusive ||= []
|
30
|
+
@mutually_exclusive << OpenStruct.new(
|
31
|
+
fields: fields, required: required
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def mutually_exclusive_fields
|
36
|
+
@mutually_exclusive || []
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate(*items, &block)
|
40
|
+
@validations = (@validations || []).concat(items)
|
41
|
+
@validations << block if block
|
42
|
+
end
|
43
|
+
|
44
|
+
def validations
|
45
|
+
@validations || []
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/error"
|
4
|
+
require "openapi3_parser/node_factory/type_checker"
|
5
|
+
|
6
|
+
module Openapi3Parser
|
7
|
+
module NodeFactory
|
8
|
+
module ObjectFactory
|
9
|
+
class FieldConfig
|
10
|
+
def initialize(
|
11
|
+
input_type: nil,
|
12
|
+
factory: nil,
|
13
|
+
required: false,
|
14
|
+
default: nil,
|
15
|
+
validate: nil
|
16
|
+
)
|
17
|
+
@given_input_type = input_type
|
18
|
+
@given_factory = factory
|
19
|
+
@given_required = required
|
20
|
+
@given_default = default
|
21
|
+
@given_validate = validate
|
22
|
+
end
|
23
|
+
|
24
|
+
def factory?
|
25
|
+
!given_factory.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize_factory(context, parent_factory)
|
29
|
+
if given_factory.is_a?(Class)
|
30
|
+
given_factory.new(context)
|
31
|
+
elsif given_factory.is_a?(Symbol)
|
32
|
+
parent_factory.send(given_factory, context)
|
33
|
+
else
|
34
|
+
given_factory.call(context)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def required?
|
39
|
+
given_required
|
40
|
+
end
|
41
|
+
|
42
|
+
def check_input_type(validatable, building_node)
|
43
|
+
return true if !given_input_type || validatable.input.nil?
|
44
|
+
|
45
|
+
if building_node
|
46
|
+
TypeChecker.raise_on_invalid_type(validatable.context,
|
47
|
+
type: given_input_type)
|
48
|
+
else
|
49
|
+
TypeChecker.validate_type(validatable, type: given_input_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_field(validatable, building_node)
|
54
|
+
return true if !given_validate || validatable.input.nil?
|
55
|
+
|
56
|
+
run_validation(validatable)
|
57
|
+
|
58
|
+
return validatable.errors.empty? unless building_node
|
59
|
+
return true if validatable.errors.empty?
|
60
|
+
|
61
|
+
error = validatable.errors.first
|
62
|
+
location_summary = error.context.location_summary
|
63
|
+
raise Error::InvalidData,
|
64
|
+
"Invalid data for #{location_summary}: #{error.message}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def default(factory)
|
68
|
+
return given_default.call if given_default.is_a?(Proc)
|
69
|
+
return factory.send(given_default) if given_default.is_a?(Symbol)
|
70
|
+
given_default
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
attr_reader :given_input_type, :given_factory, :given_required,
|
76
|
+
:given_default, :given_validate
|
77
|
+
|
78
|
+
def run_validation(validatable)
|
79
|
+
if given_validate.is_a?(Symbol)
|
80
|
+
validatable.factory.send(given_validate, validatable)
|
81
|
+
else
|
82
|
+
given_validate.call(validatable)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/node_factory/type_checker"
|
4
|
+
require "openapi3_parser/node_factory/object_factory/validator"
|
5
|
+
require "openapi3_parser/node_factory/recursive_pointer"
|
6
|
+
|
7
|
+
module Openapi3Parser
|
8
|
+
module NodeFactory
|
9
|
+
module ObjectFactory
|
10
|
+
class NodeBuilder
|
11
|
+
def self.errors(factory)
|
12
|
+
new(factory).errors
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.node_data(factory)
|
16
|
+
new(factory).node_data
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(factory)
|
20
|
+
@factory = factory
|
21
|
+
@validatable = Validation::Validatable.new(factory)
|
22
|
+
end
|
23
|
+
|
24
|
+
def errors
|
25
|
+
return validatable.collection if empty_and_allowed_to_be?
|
26
|
+
|
27
|
+
TypeChecker.validate_type(validatable, type: ::Hash)
|
28
|
+
|
29
|
+
if validatable.errors.empty?
|
30
|
+
validatable.add_errors(validate(raise_on_invalid: false))
|
31
|
+
end
|
32
|
+
|
33
|
+
validatable.collection
|
34
|
+
end
|
35
|
+
|
36
|
+
def node_data
|
37
|
+
return build_node_data if empty_and_allowed_to_be?
|
38
|
+
TypeChecker.raise_on_invalid_type(factory.context, type: ::Hash)
|
39
|
+
validate(raise_on_invalid: true)
|
40
|
+
build_node_data
|
41
|
+
end
|
42
|
+
|
43
|
+
private_class_method :new
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :factory, :validatable
|
48
|
+
|
49
|
+
def empty_and_allowed_to_be?
|
50
|
+
factory.nil_input? && factory.can_use_default?
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate(raise_on_invalid:)
|
54
|
+
Validator.call(factory, raise_on_invalid)
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_node_data
|
58
|
+
return if factory.nil_input? && factory.data.nil?
|
59
|
+
|
60
|
+
factory.data.each_with_object(NodeData.new) do |(key, value), memo|
|
61
|
+
memo[key] = if node_is_recursive_pointer?(value)
|
62
|
+
value.recursive_pointer
|
63
|
+
else
|
64
|
+
resolve_value(key, value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def resolve_value(key, value)
|
70
|
+
config = factory.field_configs[key]
|
71
|
+
resolved_value = value.respond_to?(:node) ? value.node : value
|
72
|
+
|
73
|
+
# let a field config default take precedence if value is a nil_input?
|
74
|
+
if (value.respond_to?(:nil_input?) && value.nil_input?) || value.nil?
|
75
|
+
default = config&.default(factory)
|
76
|
+
default.nil? ? resolved_value : default
|
77
|
+
else
|
78
|
+
resolved_value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def node_is_recursive_pointer?(value_factory)
|
83
|
+
return false unless value_factory.respond_to?(:in_recursive_loop?)
|
84
|
+
value_factory.in_recursive_loop?
|
85
|
+
end
|
86
|
+
|
87
|
+
class NodeData < ::Hash
|
88
|
+
def [](key)
|
89
|
+
item = super(key)
|
90
|
+
item.is_a?(NodeFactory::RecursivePointer) ? item.node : item
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
require "openapi3_parser/array_sentence"
|
6
|
+
require "openapi3_parser/error"
|
7
|
+
require "openapi3_parser/validation/validatable"
|
8
|
+
require "openapi3_parser/validators/mutually_exclusive_fields"
|
9
|
+
require "openapi3_parser/validators/required_fields"
|
10
|
+
require "openapi3_parser/validators/unexpected_fields"
|
11
|
+
|
12
|
+
module Openapi3Parser
|
13
|
+
module NodeFactory
|
14
|
+
module ObjectFactory
|
15
|
+
class Validator
|
16
|
+
private_class_method :new
|
17
|
+
attr_reader :factory, :validatable, :building_node
|
18
|
+
|
19
|
+
def self.call(*args)
|
20
|
+
new(*args).call
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(factory, building_node)
|
24
|
+
@factory = factory
|
25
|
+
@building_node = building_node
|
26
|
+
@validatable = Validation::Validatable.new(factory)
|
27
|
+
end
|
28
|
+
|
29
|
+
def call
|
30
|
+
check_required_fields
|
31
|
+
check_unexpected_fields
|
32
|
+
check_mutually_exclusive_fields
|
33
|
+
check_invalid_fields
|
34
|
+
check_factory_validations
|
35
|
+
validatable.collection
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def check_required_fields
|
41
|
+
Validators::RequiredFields.call(
|
42
|
+
validatable,
|
43
|
+
required_fields: factory.required_fields,
|
44
|
+
raise_on_invalid: building_node
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_unexpected_fields
|
49
|
+
Validators::UnexpectedFields.call(
|
50
|
+
validatable,
|
51
|
+
allow_extensions: factory.allowed_extensions?,
|
52
|
+
allowed_fields: factory.allowed_fields,
|
53
|
+
raise_on_invalid: building_node
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def check_mutually_exclusive_fields
|
58
|
+
Validators::MutuallyExclusiveFields.call(
|
59
|
+
validatable,
|
60
|
+
mutually_exclusive_fields: factory.mutually_exclusive_fields,
|
61
|
+
raise_on_invalid: building_node
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def check_invalid_fields
|
66
|
+
CheckInvalidFields.call(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
def check_factory_validations
|
70
|
+
CheckFactoryValidations.call(self)
|
71
|
+
end
|
72
|
+
|
73
|
+
class CheckInvalidFields
|
74
|
+
extend Forwardable
|
75
|
+
attr_reader :validator
|
76
|
+
def_delegators :validator, :factory, :building_node, :validatable
|
77
|
+
private_class_method :new
|
78
|
+
|
79
|
+
def self.call(validator)
|
80
|
+
new(validator).call
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(validator)
|
84
|
+
@validator = validator
|
85
|
+
end
|
86
|
+
|
87
|
+
def call
|
88
|
+
factory.data.each do |name, field|
|
89
|
+
# references can reference themselves and become in a loop
|
90
|
+
next if in_recursive_loop?(field)
|
91
|
+
has_factory_errors = handle_factory_checks(name)
|
92
|
+
|
93
|
+
next if has_factory_errors || !field.respond_to?(:errors)
|
94
|
+
|
95
|
+
# We don't add errors when we're building a node as they will
|
96
|
+
# be raised when that child node is built
|
97
|
+
validatable.add_errors(field.errors) unless building_node
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def handle_factory_checks(name)
|
104
|
+
field_errors = if factory.field_configs[name]
|
105
|
+
check_field(name, factory.field_configs[name])
|
106
|
+
end
|
107
|
+
|
108
|
+
(field_errors || []).any?
|
109
|
+
end
|
110
|
+
|
111
|
+
def in_recursive_loop?(field)
|
112
|
+
field.respond_to?(:in_recursive_loop?) && field.in_recursive_loop?
|
113
|
+
end
|
114
|
+
|
115
|
+
def check_field(name, field_config)
|
116
|
+
return if factory.raw_input[name].nil?
|
117
|
+
|
118
|
+
field_validatable = Validation::Validatable.new(
|
119
|
+
factory,
|
120
|
+
context: Context.next_field(factory.context, name)
|
121
|
+
)
|
122
|
+
|
123
|
+
valid_input_type = field_config.check_input_type(field_validatable,
|
124
|
+
building_node)
|
125
|
+
|
126
|
+
if valid_input_type
|
127
|
+
field_config.validate_field(field_validatable, building_node)
|
128
|
+
end
|
129
|
+
|
130
|
+
validatable.add_errors(field_validatable.errors)
|
131
|
+
field_validatable.errors
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class CheckFactoryValidations
|
136
|
+
private_class_method :new
|
137
|
+
|
138
|
+
def self.call(validator)
|
139
|
+
new.call(validator)
|
140
|
+
end
|
141
|
+
|
142
|
+
def call(validator)
|
143
|
+
run_validations(validator)
|
144
|
+
|
145
|
+
errors = validator.validatable.errors
|
146
|
+
|
147
|
+
return if errors.empty? || !validator.building_node
|
148
|
+
|
149
|
+
location_summary = errors.first.context.location_summary
|
150
|
+
raise Error::InvalidData,
|
151
|
+
"Invalid data for #{location_summary}: "\
|
152
|
+
"#{errors.first.message}"
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def run_validations(validator)
|
158
|
+
validator.factory.validations.each do |validation|
|
159
|
+
if validation.respond_to?(:call)
|
160
|
+
validation.call(validator.validatable)
|
161
|
+
elsif validation.is_a?(Symbol)
|
162
|
+
validator.factory.send(validation, validator.validatable)
|
163
|
+
else
|
164
|
+
raise Error::NotCallable, "expected a symbol or a callable"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|