openapi3_parser 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +9 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +5 -0
- data/LICENCE +19 -0
- data/README.md +32 -0
- data/Rakefile +10 -0
- data/TODO.md +30 -0
- data/lib/openapi3_parser.rb +39 -0
- data/lib/openapi3_parser/context.rb +54 -0
- data/lib/openapi3_parser/document.rb +48 -0
- data/lib/openapi3_parser/error.rb +5 -0
- data/lib/openapi3_parser/fields/map.rb +83 -0
- data/lib/openapi3_parser/node.rb +115 -0
- data/lib/openapi3_parser/node/map.rb +24 -0
- data/lib/openapi3_parser/node/object.rb +28 -0
- data/lib/openapi3_parser/node_factories/array.rb +105 -0
- data/lib/openapi3_parser/node_factories/callback.rb +26 -0
- data/lib/openapi3_parser/node_factories/components.rb +83 -0
- data/lib/openapi3_parser/node_factories/contact.rb +24 -0
- data/lib/openapi3_parser/node_factories/discriminator.rb +31 -0
- data/lib/openapi3_parser/node_factories/encoding.rb +34 -0
- data/lib/openapi3_parser/node_factories/example.rb +25 -0
- data/lib/openapi3_parser/node_factories/external_documentation.rb +23 -0
- data/lib/openapi3_parser/node_factories/header.rb +36 -0
- data/lib/openapi3_parser/node_factories/info.rb +28 -0
- data/lib/openapi3_parser/node_factories/license.rb +22 -0
- data/lib/openapi3_parser/node_factories/link.rb +40 -0
- data/lib/openapi3_parser/node_factories/map.rb +110 -0
- data/lib/openapi3_parser/node_factories/media_type.rb +44 -0
- data/lib/openapi3_parser/node_factories/oauth_flow.rb +24 -0
- data/lib/openapi3_parser/node_factories/oauth_flows.rb +31 -0
- data/lib/openapi3_parser/node_factories/openapi.rb +50 -0
- data/lib/openapi3_parser/node_factories/operation.rb +76 -0
- data/lib/openapi3_parser/node_factories/parameter.rb +43 -0
- data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +37 -0
- data/lib/openapi3_parser/node_factories/path_item.rb +75 -0
- data/lib/openapi3_parser/node_factories/paths.rb +32 -0
- data/lib/openapi3_parser/node_factories/reference.rb +33 -0
- data/lib/openapi3_parser/node_factories/request_body.rb +31 -0
- data/lib/openapi3_parser/node_factories/response.rb +45 -0
- data/lib/openapi3_parser/node_factories/responses.rb +32 -0
- data/lib/openapi3_parser/node_factories/schema.rb +106 -0
- data/lib/openapi3_parser/node_factories/security_requirement.rb +25 -0
- data/lib/openapi3_parser/node_factories/security_scheme.rb +35 -0
- data/lib/openapi3_parser/node_factories/server.rb +32 -0
- data/lib/openapi3_parser/node_factories/server_variable.rb +28 -0
- data/lib/openapi3_parser/node_factories/tag.rb +24 -0
- data/lib/openapi3_parser/node_factories/xml.rb +25 -0
- data/lib/openapi3_parser/node_factory.rb +126 -0
- data/lib/openapi3_parser/node_factory/field_config.rb +88 -0
- data/lib/openapi3_parser/node_factory/map.rb +39 -0
- data/lib/openapi3_parser/node_factory/object.rb +80 -0
- data/lib/openapi3_parser/node_factory/object/node_builder.rb +85 -0
- data/lib/openapi3_parser/node_factory/object/validator.rb +102 -0
- data/lib/openapi3_parser/node_factory/optional_reference.rb +23 -0
- data/lib/openapi3_parser/nodes/array.rb +26 -0
- data/lib/openapi3_parser/nodes/callback.rb +11 -0
- data/lib/openapi3_parser/nodes/components.rb +47 -0
- data/lib/openapi3_parser/nodes/contact.rb +23 -0
- data/lib/openapi3_parser/nodes/discriminator.rb +19 -0
- data/lib/openapi3_parser/nodes/encoding.rb +31 -0
- data/lib/openapi3_parser/nodes/example.rb +27 -0
- data/lib/openapi3_parser/nodes/external_documentation.rb +19 -0
- data/lib/openapi3_parser/nodes/header.rb +13 -0
- data/lib/openapi3_parser/nodes/info.rb +35 -0
- data/lib/openapi3_parser/nodes/license.rb +19 -0
- data/lib/openapi3_parser/nodes/link.rb +35 -0
- data/lib/openapi3_parser/nodes/map.rb +11 -0
- data/lib/openapi3_parser/nodes/media_type.rb +27 -0
- data/lib/openapi3_parser/nodes/oauth_flow.rb +27 -0
- data/lib/openapi3_parser/nodes/oauth_flows.rb +27 -0
- data/lib/openapi3_parser/nodes/openapi.rb +44 -0
- data/lib/openapi3_parser/nodes/operation.rb +59 -0
- data/lib/openapi3_parser/nodes/parameter.rb +21 -0
- data/lib/openapi3_parser/nodes/parameter/parameter_like.rb +53 -0
- data/lib/openapi3_parser/nodes/path_item.rb +59 -0
- data/lib/openapi3_parser/nodes/paths.rb +11 -0
- data/lib/openapi3_parser/nodes/request_body.rb +23 -0
- data/lib/openapi3_parser/nodes/response.rb +27 -0
- data/lib/openapi3_parser/nodes/responses.rb +15 -0
- data/lib/openapi3_parser/nodes/schema.rb +159 -0
- data/lib/openapi3_parser/nodes/security_requirement.rb +11 -0
- data/lib/openapi3_parser/nodes/security_scheme.rb +44 -0
- data/lib/openapi3_parser/nodes/server.rb +25 -0
- data/lib/openapi3_parser/nodes/server_variable.rb +23 -0
- data/lib/openapi3_parser/nodes/tag.rb +23 -0
- data/lib/openapi3_parser/nodes/xml.rb +31 -0
- data/lib/openapi3_parser/validation/error.rb +14 -0
- data/lib/openapi3_parser/validation/error_collection.rb +36 -0
- data/lib/openapi3_parser/version.rb +5 -0
- data/openapi3_parser.gemspec +28 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3c7e2585cb3bbdbda459519eb7e264dfd89f5745
|
4
|
+
data.tar.gz: 4fdbb4b547e626e1b0ba5f502b93107638af3655
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 498d2043a338970c66ef9d14590adb34ec2f7b65e751985f8d12a8c9506d63e09a9840e7521b00a953000af1e286917147fb4507be29f68a5c047aa9f124f7d8
|
7
|
+
data.tar.gz: 7fb4d6b168b514fa01056fa5be0026551ad02e0e47506e99516b14b290e450c0940031e82fc38bdc0f4c5d137d98d3a621f28d42bd6e5923f335a7c31255796e
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/Gemfile.lock
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENCE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2017 Kevin Dew
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# OpenAPI 3 Parser
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/kevindew/openapi_parser.svg?branch=master)](https://travis-ci.org/kevindew/openapi_parser)
|
4
|
+
|
5
|
+
|
6
|
+
This is a parser/validator for [Open API 3][openapi-3] built in Ruby.
|
7
|
+
|
8
|
+
Example usage:
|
9
|
+
|
10
|
+
```
|
11
|
+
require "openapi3_parser"
|
12
|
+
|
13
|
+
document = Openapi3Parser.load_file("path/to/example.yaml")
|
14
|
+
|
15
|
+
# check whether document is valid
|
16
|
+
document.valid?
|
17
|
+
|
18
|
+
# traverse document
|
19
|
+
document.paths["/"]
|
20
|
+
```
|
21
|
+
|
22
|
+
[openapi-3]: https://github.com/OAI/OpenAPI-Specification
|
23
|
+
|
24
|
+
## Status
|
25
|
+
|
26
|
+
This is currently a work in progress and will remain so until it reaches 1.0.
|
27
|
+
|
28
|
+
See [TODO](TODO.md) for details of the roadmap there.
|
29
|
+
|
30
|
+
## Licence
|
31
|
+
|
32
|
+
[MIT License](LICENCE)
|
data/Rakefile
ADDED
data/TODO.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Todo
|
2
|
+
|
3
|
+
These are the steps defined to reach 1.0. Assistance is very welcome.
|
4
|
+
|
5
|
+
- [ ] Handle mutually exclusive fields
|
6
|
+
- [ ] Refactor the various NodeFactory modules to be a less confusing
|
7
|
+
hierachical structure. Consider having factories subclass instead of use
|
8
|
+
mixin
|
9
|
+
- [ ] Decouple Document class for the source file. Consider a source file class
|
10
|
+
instead
|
11
|
+
- [ ] Validate that a reference creates the type of node that is expected in
|
12
|
+
a context
|
13
|
+
- [ ] Allow opening of references from additional files
|
14
|
+
- [ ] Allow opening of openapi documents by URL
|
15
|
+
- [ ] Support references by URL, consider option to limit behaviour
|
16
|
+
- [ ] Support converting CommonMark to HTML
|
17
|
+
- [ ] Reach parity with OpenAPI specification for validation
|
18
|
+
- [ ] Consider a lenient mode for a document to only have to comply with type
|
19
|
+
based validation
|
20
|
+
- [ ] Improve test coverage
|
21
|
+
- [ ] Publish documentation of the interface through the structure
|
22
|
+
- [ ] Consider a resolved context class for representing context with a node
|
23
|
+
that can handle scenarios where a node is represented by both a reference
|
24
|
+
and resolved context
|
25
|
+
- [ ] Create error classes for various scenarios
|
26
|
+
- [ ] Associate/resolve operation id / operation references
|
27
|
+
- [ ] Do something to model expressions
|
28
|
+
- [ ] Improve the modelling of namespace
|
29
|
+
- [ ] Set up nicer string representations of key classes to help them be
|
30
|
+
debugged
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/error"
|
4
|
+
require "openapi3_parser/document"
|
5
|
+
|
6
|
+
require "yaml"
|
7
|
+
require "json"
|
8
|
+
|
9
|
+
module Openapi3Parser
|
10
|
+
def self.load(input)
|
11
|
+
# working_directory ||= if input.respond_to?(:read)
|
12
|
+
# File.dirname(input)
|
13
|
+
# else
|
14
|
+
# Dir.pwd
|
15
|
+
# end
|
16
|
+
|
17
|
+
Document.new(parse_input(input))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.load_file(path)
|
21
|
+
file = File.open(path)
|
22
|
+
load(file)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_input(input)
|
26
|
+
return input if input.respond_to?(:keys)
|
27
|
+
|
28
|
+
extension = input.respond_to?(:extname) ? input.extname : nil
|
29
|
+
contents = input.respond_to?(:read) ? input.read : input
|
30
|
+
|
31
|
+
if extension == ".json" || contents.strip[0] == "{"
|
32
|
+
JSON.parse(contents)
|
33
|
+
else
|
34
|
+
YAML.safe_load(contents, [], [], true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private_class_method :parse_input
|
39
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Openapi3Parser
|
4
|
+
class Context
|
5
|
+
attr_reader :input, :namespace, :document, :parent
|
6
|
+
|
7
|
+
def initialize(input:, namespace: [], document:, parent: nil)
|
8
|
+
@input = input
|
9
|
+
@namespace = namespace.freeze
|
10
|
+
@document = document
|
11
|
+
@parent = parent
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.root(input, document)
|
15
|
+
new(input: input, document: document)
|
16
|
+
end
|
17
|
+
|
18
|
+
def stringify_namespace
|
19
|
+
return "root" if namespace.empty?
|
20
|
+
namespace
|
21
|
+
.map { |i| i.include?("/") ? %("#{i}") : i }
|
22
|
+
.join("/")
|
23
|
+
end
|
24
|
+
|
25
|
+
def next_namespace(segment, next_input = nil)
|
26
|
+
next_input ||= input[segment]
|
27
|
+
self.class.new(
|
28
|
+
input: next_input,
|
29
|
+
namespace: namespace + [segment],
|
30
|
+
document: document,
|
31
|
+
parent: self
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolve_reference
|
36
|
+
document.resolve_reference(input["$ref"]) do |resolved_input, namespace|
|
37
|
+
# @TODO track reference for cyclic depenendies
|
38
|
+
next_context = resolved_reference(resolved_input, namespace)
|
39
|
+
yield(next_context)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def resolved_reference(input, namespace)
|
46
|
+
self.class.new(
|
47
|
+
input: input,
|
48
|
+
namespace: namespace,
|
49
|
+
document: document,
|
50
|
+
parent: parent
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/context"
|
4
|
+
require "openapi3_parser/error"
|
5
|
+
require "openapi3_parser/node_factories/openapi"
|
6
|
+
|
7
|
+
require "forwardable"
|
8
|
+
|
9
|
+
module Openapi3Parser
|
10
|
+
class Document
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :input
|
14
|
+
|
15
|
+
def_delegators :factory, :valid?, :errors
|
16
|
+
def_delegators :root, :openapi, :info, :servers, :paths, :components,
|
17
|
+
:security, :tags, :external_docs, :extension, :[], :each
|
18
|
+
|
19
|
+
def initialize(input)
|
20
|
+
@input = input
|
21
|
+
end
|
22
|
+
|
23
|
+
def root
|
24
|
+
factory.node
|
25
|
+
end
|
26
|
+
|
27
|
+
def resolve_reference(reference)
|
28
|
+
if reference[0..1] != "#/"
|
29
|
+
raise Error, "Only anchor references are currently supported"
|
30
|
+
end
|
31
|
+
|
32
|
+
parts = reference.split("/").drop(1).map do |field|
|
33
|
+
CGI.unescape(field.gsub("+", "%20"))
|
34
|
+
end
|
35
|
+
|
36
|
+
result = input.dig(*parts)
|
37
|
+
raise Error, "Could not resolve reference #{reference}" unless result
|
38
|
+
|
39
|
+
yield(result, parts)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def factory
|
45
|
+
@factory ||= NodeFactories::Openapi.new(Context.root(input, self))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/error"
|
4
|
+
|
5
|
+
module Openapi3Parser
|
6
|
+
module Fields
|
7
|
+
class Map
|
8
|
+
private_class_method :new
|
9
|
+
|
10
|
+
def initialize(input, context, value_type, key_format)
|
11
|
+
@input = input
|
12
|
+
@context = context
|
13
|
+
@value_type = value_type
|
14
|
+
@key_format = key_format
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.call(
|
18
|
+
input,
|
19
|
+
context,
|
20
|
+
value_type: Hash,
|
21
|
+
key_format: nil,
|
22
|
+
&block
|
23
|
+
)
|
24
|
+
new(input, context, value_type, key_format).call(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.reference_input(
|
28
|
+
input,
|
29
|
+
context,
|
30
|
+
value_type: Hash,
|
31
|
+
key_format: nil,
|
32
|
+
&block
|
33
|
+
)
|
34
|
+
call(
|
35
|
+
input, context, value_type: value_type, key_format: key_format
|
36
|
+
) do |field_input, field_context|
|
37
|
+
field_context.possible_reference(field_input, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def call(&block)
|
42
|
+
validate_keys
|
43
|
+
validate_values
|
44
|
+
|
45
|
+
input.each_with_object({}) do |(key, value), memo|
|
46
|
+
memo[key] = if block
|
47
|
+
yield(value, context.next_namespace(key), key)
|
48
|
+
else
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
attr_reader :input, :context, :value_type, :key_format
|
57
|
+
|
58
|
+
def validate_keys
|
59
|
+
return unless key_format
|
60
|
+
invalid_keys = input.keys.reject { |key| key =~ key_format }
|
61
|
+
return if invalid_keys.empty?
|
62
|
+
|
63
|
+
raise Openapi3Parser::Error, "Invalid field names for "\
|
64
|
+
"#{context.stringify_namespace}: #{invalid_keys.join(', ')}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def validate_values
|
68
|
+
return unless value_type
|
69
|
+
invalid = input.reject do |key, value|
|
70
|
+
if value_type.is_a?(Proc)
|
71
|
+
value.call(value, key)
|
72
|
+
else
|
73
|
+
value.is_a?(value_type)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return if invalid.empty?
|
77
|
+
|
78
|
+
raise Openapi3Parser::Error, "Unexpected type for "\
|
79
|
+
"#{context.stringify_namespace}: #{invalid.keys.join(', ')}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "openapi3_parser/node/field_config"
|
4
|
+
|
5
|
+
module Openapi3Parser
|
6
|
+
module Node
|
7
|
+
module ClassMethods
|
8
|
+
def field(name, **options)
|
9
|
+
@field_configs ||= {}
|
10
|
+
@field_configs[name] = FieldConfig.new(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def field_configs
|
14
|
+
@field_configs || {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def allow_extensions
|
18
|
+
@allow_extensions = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def disallow_extensions
|
22
|
+
@allow_extensions = false
|
23
|
+
end
|
24
|
+
|
25
|
+
def allowed_extensions?
|
26
|
+
@allow_extensions == true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.included(base)
|
31
|
+
base.extend(ClassMethods)
|
32
|
+
end
|
33
|
+
|
34
|
+
EXTENSION_REGEX = /^x-(.*)/
|
35
|
+
|
36
|
+
attr_reader :input, :context, :fields
|
37
|
+
|
38
|
+
def initialize(input, context)
|
39
|
+
@input = input
|
40
|
+
@context = context
|
41
|
+
@fields = build_fields(input)
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](value)
|
45
|
+
fields[value]
|
46
|
+
end
|
47
|
+
|
48
|
+
def extension(value)
|
49
|
+
fields["x-#{value}"]
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def build_fields(input)
|
55
|
+
check_for_unexpected_fields(input)
|
56
|
+
create_fields(input)
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_for_unexpected_fields(input)
|
60
|
+
allowed_fields = field_configs.keys
|
61
|
+
remaining_fields = input.keys - allowed_fields
|
62
|
+
return if remaining_fields.empty?
|
63
|
+
|
64
|
+
if allowed_extensions?
|
65
|
+
remaining_fields.reject! { |key| key =~ EXTENSION_REGEX }
|
66
|
+
end
|
67
|
+
|
68
|
+
return if remaining_fields.empty?
|
69
|
+
raise Error,
|
70
|
+
"Unexpected attributes for #{context.stringify_namespace}: "\
|
71
|
+
"#{remaining_fields.join(', ')}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_fields(input)
|
75
|
+
check_required(input)
|
76
|
+
check_types(input)
|
77
|
+
fields = field_configs.each_with_object({}) do |(field, config), memo|
|
78
|
+
next_context = context.next_namespace(field)
|
79
|
+
memo[field] = config.build(input[field], self, next_context)
|
80
|
+
end
|
81
|
+
extensions = input.select { |(k, _)| k =~ EXTENSION_REGEX }
|
82
|
+
fields.merge(extensions)
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_required(input)
|
86
|
+
missing = field_configs.reject do |field, config|
|
87
|
+
config.valid_presence?(input[field])
|
88
|
+
end
|
89
|
+
|
90
|
+
return if missing.empty?
|
91
|
+
raise Error,
|
92
|
+
"Missing required fields for #{context.stringify_namespace}: "\
|
93
|
+
"#{missing.keys}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_types(input)
|
97
|
+
invalid = field_configs.reject do |field, config|
|
98
|
+
config.valid_input_type?(input[field], self)
|
99
|
+
end
|
100
|
+
|
101
|
+
return if invalid.empty?
|
102
|
+
raise Error,
|
103
|
+
"Invalid fields for #{context.stringify_namespace}: "\
|
104
|
+
"#{invalid.keys}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def allowed_extensions?
|
108
|
+
self.class.allowed_extensions?
|
109
|
+
end
|
110
|
+
|
111
|
+
def field_configs
|
112
|
+
self.class.field_configs || {}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|