openapi3_parser 0.3.0 → 0.4.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 +4 -0
- data/.travis.yml +3 -5
- data/CHANGELOG.md +7 -0
- data/README.md +2 -2
- data/TODO.md +9 -2
- data/lib/openapi3_parser/context/pointer.rb +2 -0
- data/lib/openapi3_parser/document.rb +54 -18
- data/lib/openapi3_parser/markdown.rb +15 -0
- data/lib/openapi3_parser/{nodes → node}/array.rb +1 -1
- data/lib/openapi3_parser/{nodes → node}/callback.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/components.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/contact.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/discriminator.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/encoding.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/example.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/external_documentation.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/header.rb +4 -5
- data/lib/openapi3_parser/{nodes → node}/info.rb +9 -6
- data/lib/openapi3_parser/{nodes → node}/license.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/link.rb +7 -4
- data/lib/openapi3_parser/node/map.rb +1 -1
- data/lib/openapi3_parser/{nodes → node}/media_type.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/oauth_flow.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/oauth_flows.rb +2 -4
- data/lib/openapi3_parser/node/object.rb +11 -1
- data/lib/openapi3_parser/{nodes → node}/openapi.rb +6 -8
- data/lib/openapi3_parser/{nodes → node}/operation.rb +11 -8
- data/lib/openapi3_parser/{nodes → node}/parameter.rb +3 -4
- data/lib/openapi3_parser/node/parameter_like.rb +70 -0
- data/lib/openapi3_parser/{nodes → node}/path_item.rb +9 -6
- data/lib/openapi3_parser/{nodes → node}/paths.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/request_body.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/response.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/responses.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/schema.rb +12 -9
- data/lib/openapi3_parser/{nodes → node}/security_requirement.rb +2 -4
- data/lib/openapi3_parser/{nodes → node}/security_scheme.rb +7 -5
- data/lib/openapi3_parser/{nodes → node}/server.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/server_variable.rb +8 -5
- data/lib/openapi3_parser/{nodes → node}/tag.rb +7 -4
- data/lib/openapi3_parser/{nodes → node}/xml.rb +2 -4
- data/lib/openapi3_parser/node_factories/array.rb +10 -4
- data/lib/openapi3_parser/node_factories/callback.rb +2 -2
- data/lib/openapi3_parser/node_factories/components.rb +5 -3
- data/lib/openapi3_parser/node_factories/contact.rb +10 -4
- data/lib/openapi3_parser/node_factories/discriminator.rb +2 -2
- data/lib/openapi3_parser/node_factories/encoding.rb +7 -3
- data/lib/openapi3_parser/node_factories/example.rb +8 -3
- data/lib/openapi3_parser/node_factories/external_documentation.rb +7 -3
- data/lib/openapi3_parser/node_factories/header.rb +2 -2
- data/lib/openapi3_parser/node_factories/info.rb +6 -3
- data/lib/openapi3_parser/node_factories/license.rb +6 -3
- data/lib/openapi3_parser/node_factories/link.rb +4 -2
- data/lib/openapi3_parser/node_factories/map.rb +12 -4
- data/lib/openapi3_parser/node_factories/media_type.rb +36 -5
- data/lib/openapi3_parser/node_factories/oauth_flow.rb +2 -2
- data/lib/openapi3_parser/node_factories/oauth_flows.rb +2 -2
- data/lib/openapi3_parser/node_factories/openapi.rb +13 -3
- data/lib/openapi3_parser/node_factories/operation.rb +11 -3
- data/lib/openapi3_parser/node_factories/parameter.rb +28 -3
- data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +9 -5
- data/lib/openapi3_parser/node_factories/path_item.rb +26 -7
- data/lib/openapi3_parser/node_factories/paths.rb +51 -2
- data/lib/openapi3_parser/node_factories/reference.rb +4 -0
- data/lib/openapi3_parser/node_factories/request_body.rb +32 -3
- data/lib/openapi3_parser/node_factories/response.rb +24 -4
- data/lib/openapi3_parser/node_factories/responses.rb +28 -2
- data/lib/openapi3_parser/node_factories/schema.rb +17 -3
- data/lib/openapi3_parser/node_factories/security_requirement.rb +2 -2
- data/lib/openapi3_parser/node_factories/security_scheme.rb +2 -2
- data/lib/openapi3_parser/node_factories/server.rb +2 -2
- data/lib/openapi3_parser/node_factories/server_variable.rb +2 -2
- data/lib/openapi3_parser/node_factories/tag.rb +2 -2
- data/lib/openapi3_parser/node_factories/xml.rb +6 -3
- data/lib/openapi3_parser/node_factory.rb +10 -4
- data/lib/openapi3_parser/node_factory/fields/reference.rb +4 -0
- data/lib/openapi3_parser/node_factory/map.rb +10 -0
- data/lib/openapi3_parser/node_factory/object.rb +29 -0
- data/lib/openapi3_parser/node_factory/object/validator.rb +69 -6
- data/lib/openapi3_parser/source/reference.rb +2 -0
- data/lib/openapi3_parser/source/reference_resolver.rb +5 -1
- data/lib/openapi3_parser/validators/absolute_uri.rb +14 -0
- data/lib/openapi3_parser/validators/component_keys.rb +17 -0
- data/lib/openapi3_parser/validators/duplicate_parameters.rb +30 -0
- data/lib/openapi3_parser/validators/email.rb +23 -0
- data/lib/openapi3_parser/validators/media_type.rb +20 -0
- data/lib/openapi3_parser/validators/url.rb +18 -0
- data/lib/openapi3_parser/version.rb +1 -1
- data/openapi3_parser.gemspec +5 -2
- metadata +70 -36
- data/lib/openapi3_parser/nodes/map.rb +0 -17
- data/lib/openapi3_parser/nodes/parameter/parameter_like.rb +0 -67
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require "openapi3_parser/context"
|
|
4
4
|
require "openapi3_parser/error"
|
|
5
5
|
require "openapi3_parser/node_factory/map"
|
|
6
|
-
require "openapi3_parser/
|
|
6
|
+
require "openapi3_parser/node/map"
|
|
7
7
|
require "openapi3_parser/validation/error"
|
|
8
8
|
require "openapi3_parser/validation/error_collection"
|
|
9
9
|
|
|
@@ -12,24 +12,28 @@ module Openapi3Parser
|
|
|
12
12
|
class Map
|
|
13
13
|
include NodeFactory::Map
|
|
14
14
|
|
|
15
|
+
# rubocop:disable Metrics/ParameterLists
|
|
15
16
|
def initialize(
|
|
16
17
|
context,
|
|
17
18
|
key_input_type: String,
|
|
18
19
|
value_input_type: nil,
|
|
19
20
|
value_factory: nil,
|
|
20
|
-
validate: nil
|
|
21
|
+
validate: nil,
|
|
22
|
+
default: {}
|
|
21
23
|
)
|
|
22
24
|
@given_key_input_type = key_input_type
|
|
23
25
|
@given_value_input_type = value_input_type
|
|
24
26
|
@given_value_factory = value_factory
|
|
25
27
|
@given_validate = validate
|
|
28
|
+
@given_default = default
|
|
26
29
|
super(context)
|
|
27
30
|
end
|
|
31
|
+
# rubocop:enable Metrics/ParameterLists
|
|
28
32
|
|
|
29
33
|
private
|
|
30
34
|
|
|
31
35
|
attr_reader :given_key_input_type, :given_value_input_type,
|
|
32
|
-
:given_value_factory, :given_validate
|
|
36
|
+
:given_value_factory, :given_validate, :given_default
|
|
33
37
|
|
|
34
38
|
def process_input(input)
|
|
35
39
|
input.each_with_object({}) do |(key, value), memo|
|
|
@@ -68,7 +72,7 @@ module Openapi3Parser
|
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
def build_map(data, context)
|
|
71
|
-
|
|
75
|
+
Node::Map.new(data, context)
|
|
72
76
|
end
|
|
73
77
|
|
|
74
78
|
def value_factory?
|
|
@@ -107,6 +111,10 @@ module Openapi3Parser
|
|
|
107
111
|
return unless type
|
|
108
112
|
"Expected #{type}" unless value.is_a?(type)
|
|
109
113
|
end
|
|
114
|
+
|
|
115
|
+
def default
|
|
116
|
+
given_default
|
|
117
|
+
end
|
|
110
118
|
end
|
|
111
119
|
end
|
|
112
120
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/media_type"
|
|
4
4
|
require "openapi3_parser/node_factory/object"
|
|
5
5
|
require "openapi3_parser/node_factory/optional_reference"
|
|
6
6
|
require "openapi3_parser/node_factories/map"
|
|
@@ -14,15 +14,18 @@ module Openapi3Parser
|
|
|
14
14
|
include NodeFactory::Object
|
|
15
15
|
|
|
16
16
|
allow_extensions
|
|
17
|
+
|
|
17
18
|
field "schema", factory: :schema_factory
|
|
18
19
|
field "example"
|
|
19
20
|
field "examples", factory: :examples_factory
|
|
20
21
|
field "encoding", factory: :encoding_factory
|
|
21
22
|
|
|
23
|
+
mutually_exclusive "example", "examples"
|
|
24
|
+
|
|
22
25
|
private
|
|
23
26
|
|
|
24
27
|
def build_object(data, context)
|
|
25
|
-
|
|
28
|
+
Node::MediaType.new(data, context)
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
def schema_factory(context)
|
|
@@ -32,12 +35,40 @@ module Openapi3Parser
|
|
|
32
35
|
|
|
33
36
|
def examples_factory(context)
|
|
34
37
|
factory = NodeFactory::OptionalReference.new(NodeFactories::Example)
|
|
35
|
-
NodeFactories::Map.new(context, value_factory: factory)
|
|
38
|
+
NodeFactories::Map.new(context, default: nil, value_factory: factory)
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def encoding_factory(context)
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
NodeFactories::Map.new(
|
|
43
|
+
context,
|
|
44
|
+
validate: EncodingValidator.new(self),
|
|
45
|
+
value_factory: NodeFactories::Encoding
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class EncodingValidator
|
|
50
|
+
def initialize(factory)
|
|
51
|
+
@factory = factory
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def call(input, _context)
|
|
55
|
+
missing_keys = input.keys - properties
|
|
56
|
+
error_message(missing_keys) unless missing_keys.empty?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
attr_reader :factory
|
|
62
|
+
|
|
63
|
+
def properties
|
|
64
|
+
properties = factory.resolved_input.dig("schema", "properties")
|
|
65
|
+
properties.respond_to?(:keys) ? properties.keys : []
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def error_message(missing_keys)
|
|
69
|
+
keys = missing_keys.join(", ")
|
|
70
|
+
"Keys are not defined as schema properties: #{keys}"
|
|
71
|
+
end
|
|
41
72
|
end
|
|
42
73
|
end
|
|
43
74
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/oauth_flow"
|
|
4
4
|
require "openapi3_parser/node_factory/object"
|
|
5
5
|
|
|
6
6
|
module Openapi3Parser
|
|
@@ -17,7 +17,7 @@ module Openapi3Parser
|
|
|
17
17
|
private
|
|
18
18
|
|
|
19
19
|
def build_object(data, context)
|
|
20
|
-
|
|
20
|
+
Node::OauthFlow.new(data, context)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/oauth_flows"
|
|
4
4
|
require "openapi3_parser/node_factories/oauth_flow"
|
|
5
5
|
require "openapi3_parser/node_factory/object"
|
|
6
6
|
require "openapi3_parser/node_factory/optional_reference"
|
|
@@ -24,7 +24,7 @@ module Openapi3Parser
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def build_object(data, context)
|
|
27
|
-
|
|
27
|
+
Node::OauthFlows.new(data, context)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/openapi"
|
|
4
4
|
require "openapi3_parser/node_factory/object"
|
|
5
5
|
require "openapi3_parser/node_factories/info"
|
|
6
6
|
require "openapi3_parser/node_factories/array"
|
|
@@ -31,7 +31,7 @@ module Openapi3Parser
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
33
|
def build_object(data, context)
|
|
34
|
-
|
|
34
|
+
Node::Openapi.new(data, context)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def servers_factory(context)
|
|
@@ -45,7 +45,17 @@ module Openapi3Parser
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def tags_factory(context)
|
|
48
|
-
|
|
48
|
+
validate_unique_tags = lambda do |input, _context|
|
|
49
|
+
names = input.map { |i| i["name"] }
|
|
50
|
+
return if names.uniq.count == names.count
|
|
51
|
+
|
|
52
|
+
dupes = names.find_all { |name| names.count(name) > 1 }
|
|
53
|
+
"Duplicate tag names: #{dupes.uniq.join(', ')}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
NodeFactories::Array.new(context,
|
|
57
|
+
value_factory: NodeFactories::Tag,
|
|
58
|
+
validate: validate_unique_tags)
|
|
49
59
|
end
|
|
50
60
|
end
|
|
51
61
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/operation"
|
|
4
4
|
require "openapi3_parser/node_factory/object"
|
|
5
5
|
require "openapi3_parser/node_factory/optional_reference"
|
|
6
6
|
require "openapi3_parser/node_factories/array"
|
|
@@ -11,6 +11,7 @@ require "openapi3_parser/node_factories/responses"
|
|
|
11
11
|
require "openapi3_parser/node_factories/callback"
|
|
12
12
|
require "openapi3_parser/node_factories/server"
|
|
13
13
|
require "openapi3_parser/node_factories/security_requirement"
|
|
14
|
+
require "openapi3_parser/validators/duplicate_parameters"
|
|
14
15
|
|
|
15
16
|
module Openapi3Parser
|
|
16
17
|
module NodeFactories
|
|
@@ -35,7 +36,7 @@ module Openapi3Parser
|
|
|
35
36
|
private
|
|
36
37
|
|
|
37
38
|
def build_object(data, context)
|
|
38
|
-
|
|
39
|
+
Node::Operation.new(data, context)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def tags_factory(context)
|
|
@@ -44,7 +45,14 @@ module Openapi3Parser
|
|
|
44
45
|
|
|
45
46
|
def parameters_factory(context)
|
|
46
47
|
factory = NodeFactory::OptionalReference.new(NodeFactories::Parameter)
|
|
47
|
-
|
|
48
|
+
|
|
49
|
+
validate = lambda do |_input, array_factory|
|
|
50
|
+
Validators::DuplicateParameters.call(array_factory.resolved_input)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
NodeFactories::Array.new(context,
|
|
54
|
+
value_factory: factory,
|
|
55
|
+
validate: validate)
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
def request_body_factory(context)
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/context"
|
|
4
|
+
require "openapi3_parser/node/parameter"
|
|
4
5
|
require "openapi3_parser/node_factories/parameter/parameter_like"
|
|
5
6
|
require "openapi3_parser/node_factory/object"
|
|
7
|
+
require "openapi3_parser/validation/error"
|
|
6
8
|
|
|
7
9
|
module Openapi3Parser
|
|
8
10
|
module NodeFactories
|
|
@@ -13,7 +15,9 @@ module Openapi3Parser
|
|
|
13
15
|
allow_extensions
|
|
14
16
|
|
|
15
17
|
field "name", input_type: String, required: true
|
|
16
|
-
field "in", input_type: String,
|
|
18
|
+
field "in", input_type: String,
|
|
19
|
+
required: true,
|
|
20
|
+
validate: :validate_in
|
|
17
21
|
field "description", input_type: String
|
|
18
22
|
field "required", input_type: :boolean, default: false
|
|
19
23
|
field "deprecated", input_type: :boolean, default: false
|
|
@@ -28,16 +32,37 @@ module Openapi3Parser
|
|
|
28
32
|
|
|
29
33
|
field "content", factory: :content_factory
|
|
30
34
|
|
|
35
|
+
mutually_exclusive "example", "examples"
|
|
36
|
+
|
|
31
37
|
private
|
|
32
38
|
|
|
33
39
|
def build_object(data, context)
|
|
34
|
-
|
|
40
|
+
Node::Parameter.new(data, context)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate(input, context)
|
|
44
|
+
errors = []
|
|
45
|
+
|
|
46
|
+
if input["in"] == "path" && !input["required"]
|
|
47
|
+
errors << Validation::Error.new(
|
|
48
|
+
"Must be included and true for a path parameter",
|
|
49
|
+
Context.next_field(context, "required"),
|
|
50
|
+
self.class
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
errors
|
|
35
55
|
end
|
|
36
56
|
|
|
37
57
|
def default_style
|
|
38
58
|
return "simple" if %w[path header].include?(context.input["in"])
|
|
39
59
|
"form"
|
|
40
60
|
end
|
|
61
|
+
|
|
62
|
+
def validate_in(input)
|
|
63
|
+
valid = %w[header query cookie path].include?(input)
|
|
64
|
+
"in can only be header, query, cookie, or path" unless valid
|
|
65
|
+
end
|
|
41
66
|
end
|
|
42
67
|
end
|
|
43
68
|
end
|
|
@@ -15,14 +15,18 @@ module Openapi3Parser
|
|
|
15
15
|
|
|
16
16
|
def examples_factory(context)
|
|
17
17
|
factory = NodeFactory::OptionalReference.new(NodeFactories::Schema)
|
|
18
|
-
NodeFactories::Map.new(context, value_factory: factory)
|
|
18
|
+
NodeFactories::Map.new(context, default: nil, value_factory: factory)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def content_factory(context)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
NodeFactories::Map.new(context,
|
|
23
|
+
default: nil,
|
|
24
|
+
value_factory: NodeFactories::MediaType,
|
|
25
|
+
validate: method(:validate_content).to_proc)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate_content(input, _context)
|
|
29
|
+
"Must only have one item" unless input.size == 1
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "openapi3_parser/
|
|
3
|
+
require "openapi3_parser/node/path_item"
|
|
4
4
|
require "openapi3_parser/node_factory/fields/reference"
|
|
5
5
|
require "openapi3_parser/node_factory/object"
|
|
6
6
|
require "openapi3_parser/node_factory/object/node_builder"
|
|
@@ -9,6 +9,7 @@ require "openapi3_parser/node_factories/array"
|
|
|
9
9
|
require "openapi3_parser/node_factories/server"
|
|
10
10
|
require "openapi3_parser/node_factories/operation"
|
|
11
11
|
require "openapi3_parser/node_factories/parameter"
|
|
12
|
+
require "openapi3_parser/validators/duplicate_parameters"
|
|
12
13
|
|
|
13
14
|
module Openapi3Parser
|
|
14
15
|
module NodeFactories
|
|
@@ -38,12 +39,9 @@ module Openapi3Parser
|
|
|
38
39
|
|
|
39
40
|
def build_object(data, context)
|
|
40
41
|
ref = data.delete("$ref")
|
|
41
|
-
return
|
|
42
|
+
return Node::PathItem.new(data, context) unless ref
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
new.nil? ? old : new
|
|
45
|
-
end
|
|
46
|
-
Nodes::PathItem.new(merged_data, ref.node_context)
|
|
44
|
+
Node::PathItem.new(merge_data(ref.node_data, data), ref.node_context)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
def ref_factory(context)
|
|
@@ -61,9 +59,30 @@ module Openapi3Parser
|
|
|
61
59
|
)
|
|
62
60
|
end
|
|
63
61
|
|
|
62
|
+
def build_resolved_input
|
|
63
|
+
ref = processed_input["$ref"]
|
|
64
|
+
data = super.tap { |d| d.delete("$ref") }
|
|
65
|
+
return data unless ref
|
|
66
|
+
|
|
67
|
+
merge_data(ref.data || {}, data)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def merge_data(base, priority)
|
|
71
|
+
base.merge(priority) do |_, new, old|
|
|
72
|
+
new.nil? ? old : new
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
64
76
|
def parameters_factory(context)
|
|
65
77
|
factory = NodeFactory::OptionalReference.new(NodeFactories::Parameter)
|
|
66
|
-
|
|
78
|
+
|
|
79
|
+
validate = lambda do |_input, array_factory|
|
|
80
|
+
Validators::DuplicateParameters.call(array_factory.resolved_input)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
NodeFactories::Array.new(context,
|
|
84
|
+
value_factory: factory,
|
|
85
|
+
validate: validate)
|
|
67
86
|
end
|
|
68
87
|
end
|
|
69
88
|
end
|
|
@@ -3,12 +3,26 @@
|
|
|
3
3
|
require "openapi3_parser/node_factory/map"
|
|
4
4
|
require "openapi3_parser/node_factory/optional_reference"
|
|
5
5
|
require "openapi3_parser/node_factories/path_item"
|
|
6
|
-
require "openapi3_parser/
|
|
6
|
+
require "openapi3_parser/node/paths"
|
|
7
7
|
|
|
8
8
|
module Openapi3Parser
|
|
9
9
|
module NodeFactories
|
|
10
10
|
class Paths
|
|
11
11
|
include NodeFactory::Map
|
|
12
|
+
PATH_REGEX = %r{
|
|
13
|
+
\A
|
|
14
|
+
# required prefix slash
|
|
15
|
+
/
|
|
16
|
+
(
|
|
17
|
+
# Match a path
|
|
18
|
+
([\-;_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*
|
|
19
|
+
# Match a path template parameter
|
|
20
|
+
({([\-;_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})+})*
|
|
21
|
+
# optional segment separating slash
|
|
22
|
+
/?
|
|
23
|
+
)*
|
|
24
|
+
\Z
|
|
25
|
+
}x
|
|
12
26
|
|
|
13
27
|
private
|
|
14
28
|
|
|
@@ -26,7 +40,42 @@ module Openapi3Parser
|
|
|
26
40
|
end
|
|
27
41
|
|
|
28
42
|
def build_map(data, context)
|
|
29
|
-
|
|
43
|
+
Node::Paths.new(data, context)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate(input, _context)
|
|
47
|
+
paths = input.keys.reject { |key| extension?(key) }
|
|
48
|
+
validate_paths(paths)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def validate_paths(paths)
|
|
52
|
+
invalid_paths = paths.reject { |p| PATH_REGEX.match(p) }
|
|
53
|
+
errors = []
|
|
54
|
+
unless invalid_paths.empty?
|
|
55
|
+
joined = invalid_paths.map { |p| "'#{p}'" }.join(", ")
|
|
56
|
+
errors << %(There are invalid paths: #{joined})
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
conflicts = conflicting_paths(paths)
|
|
60
|
+
|
|
61
|
+
unless conflicts.empty?
|
|
62
|
+
joined = conflicts.map { |p| "'#{p}'" }.join(", ")
|
|
63
|
+
errors << %(There are paths that conflict: #{joined})
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
errors
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def conflicting_paths(paths)
|
|
70
|
+
potential_conflicts = paths.each_with_object({}) do |path, memo|
|
|
71
|
+
without_params = path.gsub(/{.*?}/, "")
|
|
72
|
+
memo[path] = without_params if path != without_params
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
grouped_paths = potential_conflicts.group_by(&:last)
|
|
76
|
+
.map { |_k, v| v.map(&:first) }
|
|
77
|
+
|
|
78
|
+
grouped_paths.select { |group| group.size > 1 }.flatten
|
|
30
79
|
end
|
|
31
80
|
end
|
|
32
81
|
end
|