openapi3_parser 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +8 -0
  4. data/README.md +1 -1
  5. data/TODO.md +4 -4
  6. data/lib/openapi3_parser/array_sentence.rb +12 -0
  7. data/lib/openapi3_parser/cautious_dig.rb +39 -0
  8. data/lib/openapi3_parser/context.rb +53 -1
  9. data/lib/openapi3_parser/context/location.rb +1 -0
  10. data/lib/openapi3_parser/context/pointer.rb +67 -5
  11. data/lib/openapi3_parser/document.rb +45 -4
  12. data/lib/openapi3_parser/error.rb +9 -0
  13. data/lib/openapi3_parser/node/array.rb +14 -4
  14. data/lib/openapi3_parser/node/map.rb +45 -3
  15. data/lib/openapi3_parser/node/object.rb +25 -5
  16. data/lib/openapi3_parser/node_factory.rb +0 -150
  17. data/lib/openapi3_parser/node_factory/array.rb +198 -0
  18. data/lib/openapi3_parser/node_factory/callback.rb +24 -0
  19. data/lib/openapi3_parser/{node_factories → node_factory}/components.rb +24 -25
  20. data/lib/openapi3_parser/{node_factories → node_factory}/contact.rb +5 -6
  21. data/lib/openapi3_parser/{node_factories → node_factory}/discriminator.rb +6 -7
  22. data/lib/openapi3_parser/{node_factories → node_factory}/encoding.rb +6 -8
  23. data/lib/openapi3_parser/{node_factories → node_factory}/example.rb +4 -5
  24. data/lib/openapi3_parser/{node_factories → node_factory}/external_documentation.rb +4 -5
  25. data/lib/openapi3_parser/node_factory/field.rb +129 -0
  26. data/lib/openapi3_parser/node_factory/fields/reference.rb +54 -18
  27. data/lib/openapi3_parser/{node_factories → node_factory}/header.rb +4 -5
  28. data/lib/openapi3_parser/{node_factories → node_factory}/info.rb +6 -7
  29. data/lib/openapi3_parser/{node_factories → node_factory}/license.rb +4 -5
  30. data/lib/openapi3_parser/{node_factories → node_factory}/link.rb +6 -8
  31. data/lib/openapi3_parser/node_factory/map.rb +206 -21
  32. data/lib/openapi3_parser/{node_factories → node_factory}/media_type.rb +17 -16
  33. data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flow.rb +2 -4
  34. data/lib/openapi3_parser/{node_factories → node_factory}/oauth_flows.rb +4 -6
  35. data/lib/openapi3_parser/node_factory/object.rb +66 -63
  36. data/lib/openapi3_parser/node_factory/object_factory/dsl.rb +50 -0
  37. data/lib/openapi3_parser/node_factory/object_factory/field_config.rb +88 -0
  38. data/lib/openapi3_parser/node_factory/object_factory/node_builder.rb +96 -0
  39. data/lib/openapi3_parser/node_factory/object_factory/validator.rb +172 -0
  40. data/lib/openapi3_parser/node_factory/openapi.rb +65 -0
  41. data/lib/openapi3_parser/node_factory/operation.rb +87 -0
  42. data/lib/openapi3_parser/node_factory/optional_reference.rb +7 -3
  43. data/lib/openapi3_parser/{node_factories → node_factory}/parameter.rb +16 -22
  44. data/lib/openapi3_parser/node_factory/parameter_like.rb +42 -0
  45. data/lib/openapi3_parser/{node_factories → node_factory}/path_item.rb +27 -29
  46. data/lib/openapi3_parser/{node_factories → node_factory}/paths.rb +21 -27
  47. data/lib/openapi3_parser/node_factory/recursive_pointer.rb +17 -0
  48. data/lib/openapi3_parser/node_factory/reference.rb +48 -0
  49. data/lib/openapi3_parser/{node_factories → node_factory}/request_body.rb +15 -19
  50. data/lib/openapi3_parser/{node_factories → node_factory}/response.rb +18 -21
  51. data/lib/openapi3_parser/node_factory/responses.rb +51 -0
  52. data/lib/openapi3_parser/{node_factories → node_factory}/schema.rb +33 -33
  53. data/lib/openapi3_parser/node_factory/security_requirement.rb +21 -0
  54. data/lib/openapi3_parser/{node_factories → node_factory}/security_scheme.rb +4 -6
  55. data/lib/openapi3_parser/{node_factories → node_factory}/server.rb +6 -8
  56. data/lib/openapi3_parser/{node_factories → node_factory}/server_variable.rb +10 -12
  57. data/lib/openapi3_parser/{node_factories → node_factory}/tag.rb +4 -6
  58. data/lib/openapi3_parser/node_factory/type_checker.rb +103 -0
  59. data/lib/openapi3_parser/{node_factories → node_factory}/xml.rb +4 -5
  60. data/lib/openapi3_parser/source/reference_resolver.rb +3 -3
  61. data/lib/openapi3_parser/validation/error.rb +9 -0
  62. data/lib/openapi3_parser/validation/input_validator.rb +18 -0
  63. data/lib/openapi3_parser/validation/validatable.rb +44 -0
  64. data/lib/openapi3_parser/validators/mutually_exclusive_fields.rb +121 -0
  65. data/lib/openapi3_parser/validators/required_fields.rb +37 -0
  66. data/lib/openapi3_parser/validators/unexpected_fields.rb +52 -0
  67. data/lib/openapi3_parser/version.rb +1 -1
  68. metadata +48 -38
  69. data/lib/openapi3_parser/node_factories/array.rb +0 -114
  70. data/lib/openapi3_parser/node_factories/callback.rb +0 -27
  71. data/lib/openapi3_parser/node_factories/map.rb +0 -120
  72. data/lib/openapi3_parser/node_factories/openapi.rb +0 -62
  73. data/lib/openapi3_parser/node_factories/operation.rb +0 -84
  74. data/lib/openapi3_parser/node_factories/parameter/parameter_like.rb +0 -41
  75. data/lib/openapi3_parser/node_factories/reference.rb +0 -35
  76. data/lib/openapi3_parser/node_factories/responses.rb +0 -60
  77. data/lib/openapi3_parser/node_factories/security_requirement.rb +0 -26
  78. data/lib/openapi3_parser/node_factory/field_config.rb +0 -88
  79. data/lib/openapi3_parser/node_factory/object/node_builder.rb +0 -97
  80. data/lib/openapi3_parser/node_factory/object/validator.rb +0 -176
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/openapi"
4
+ require "openapi3_parser/node_factory/object"
5
+ require "openapi3_parser/node_factory/info"
6
+ require "openapi3_parser/node_factory/array"
7
+ require "openapi3_parser/node_factory/server"
8
+ require "openapi3_parser/node_factory/paths"
9
+ require "openapi3_parser/node_factory/components"
10
+ require "openapi3_parser/node_factory/security_requirement"
11
+ require "openapi3_parser/node_factory/tag"
12
+ require "openapi3_parser/node_factory/external_documentation"
13
+
14
+ module Openapi3Parser
15
+ module NodeFactory
16
+ class Openapi < NodeFactory::Object
17
+ allow_extensions
18
+
19
+ field "openapi", input_type: String, required: true
20
+ field "info", factory: NodeFactory::Info, required: true
21
+ field "servers", factory: :servers_factory
22
+ field "paths", factory: NodeFactory::Paths, required: true
23
+ field "components", factory: NodeFactory::Components
24
+ field "security", factory: :security_factory
25
+ field "tags", factory: :tags_factory
26
+ field "externalDocs", factory: NodeFactory::ExternalDocumentation
27
+
28
+ def can_use_default?
29
+ false
30
+ end
31
+
32
+ private
33
+
34
+ def build_object(data, context)
35
+ Node::Openapi.new(data, context)
36
+ end
37
+
38
+ def servers_factory(context)
39
+ NodeFactory::Array.new(context,
40
+ value_factory: NodeFactory::Server)
41
+ end
42
+
43
+ def security_factory(context)
44
+ NodeFactory::Array.new(context,
45
+ value_factory: NodeFactory::SecurityRequirement)
46
+ end
47
+
48
+ def tags_factory(context)
49
+ validate_unique_tags = lambda do |validatable|
50
+ names = validatable.factory.context.input.map { |i| i["name"] }
51
+ return if names.uniq.count == names.count
52
+
53
+ dupes = names.find_all { |name| names.count(name) > 1 }
54
+ validatable.add_error(
55
+ "Duplicate tag names: #{dupes.uniq.join(', ')}"
56
+ )
57
+ end
58
+
59
+ NodeFactory::Array.new(context,
60
+ value_factory: NodeFactory::Tag,
61
+ validate: validate_unique_tags)
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openapi3_parser/node/operation"
4
+ require "openapi3_parser/node_factory/map"
5
+ require "openapi3_parser/node_factory/object"
6
+ require "openapi3_parser/node_factory/optional_reference"
7
+ require "openapi3_parser/node_factory/array"
8
+ require "openapi3_parser/node_factory/external_documentation"
9
+ require "openapi3_parser/node_factory/parameter"
10
+ require "openapi3_parser/node_factory/request_body"
11
+ require "openapi3_parser/node_factory/responses"
12
+ require "openapi3_parser/node_factory/callback"
13
+ require "openapi3_parser/node_factory/server"
14
+ require "openapi3_parser/node_factory/security_requirement"
15
+ require "openapi3_parser/validators/duplicate_parameters"
16
+
17
+ module Openapi3Parser
18
+ module NodeFactory
19
+ class Operation < NodeFactory::Object
20
+ allow_extensions
21
+ field "tags", factory: :tags_factory
22
+ field "summary", input_type: String
23
+ field "description", input_type: String
24
+ field "externalDocs", factory: NodeFactory::ExternalDocumentation
25
+ field "operationId", input_type: String
26
+ field "parameters", factory: :parameters_factory
27
+ field "requestBody", factory: :request_body_factory
28
+ field "responses", factory: NodeFactory::Responses,
29
+ required: true
30
+ field "callbacks", factory: :callbacks_factory
31
+ field "deprecated", input_type: :boolean, default: false
32
+ field "security", factory: :security_factory
33
+ field "servers", factory: :servers_factory
34
+
35
+ private
36
+
37
+ def build_object(data, context)
38
+ Node::Operation.new(data, context)
39
+ end
40
+
41
+ def tags_factory(context)
42
+ NodeFactory::Array.new(context, value_input_type: String)
43
+ end
44
+
45
+ def parameters_factory(context)
46
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Parameter)
47
+
48
+ validate_parameters = lambda do |validatable|
49
+ validatable.add_error(
50
+ Validators::DuplicateParameters.call(
51
+ validatable.factory.resolved_input
52
+ )
53
+ )
54
+ end
55
+
56
+ NodeFactory::Array.new(context,
57
+ value_factory: factory,
58
+ validate: validate_parameters)
59
+ end
60
+
61
+ def request_body_factory(context)
62
+ factory = NodeFactory::RequestBody
63
+ NodeFactory::OptionalReference.new(factory).call(context)
64
+ end
65
+
66
+ def callbacks_factory(context)
67
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Callback)
68
+ NodeFactory::Map.new(context, value_factory: factory)
69
+ end
70
+
71
+ def responses_factory(context)
72
+ factory = NodeFactory::RequestBody
73
+ NodeFactory::OptionalReference.new(factory).call(context)
74
+ end
75
+
76
+ def security_factory(context)
77
+ NodeFactory::Array.new(context,
78
+ value_factory: NodeFactory::SecurityRequirement)
79
+ end
80
+
81
+ def servers_factory(context)
82
+ NodeFactory::Array.new(context,
83
+ value_factory: NodeFactory::Server)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "openapi3_parser/node_factories/reference"
3
+ require "openapi3_parser/node_factory/reference"
4
4
 
5
5
  module Openapi3Parser
6
6
  module NodeFactory
@@ -11,8 +11,12 @@ module Openapi3Parser
11
11
 
12
12
  def call(context)
13
13
  reference = context.input.is_a?(Hash) && context.input["$ref"]
14
- return NodeFactories::Reference.new(context, self) if reference
15
- factory.new(context)
14
+
15
+ if reference
16
+ NodeFactory::Reference.new(context, self)
17
+ else
18
+ factory.new(context)
19
+ end
16
20
  end
17
21
 
18
22
  private
@@ -2,15 +2,14 @@
2
2
 
3
3
  require "openapi3_parser/context"
4
4
  require "openapi3_parser/node/parameter"
5
- require "openapi3_parser/node_factories/parameter/parameter_like"
5
+ require "openapi3_parser/node_factory/parameter_like"
6
6
  require "openapi3_parser/node_factory/object"
7
7
  require "openapi3_parser/validation/error"
8
8
 
9
9
  module Openapi3Parser
10
- module NodeFactories
11
- class Parameter
12
- include NodeFactory::Object
13
- include Parameter::ParameterLike
10
+ module NodeFactory
11
+ class Parameter < NodeFactory::Object
12
+ include ParameterLike
14
13
 
15
14
  allow_extensions
16
15
 
@@ -34,24 +33,19 @@ module Openapi3Parser
34
33
 
35
34
  mutually_exclusive "example", "examples"
36
35
 
37
- private
38
-
39
- def build_object(data, context)
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(
36
+ validate do |validatable|
37
+ if validatable.input["in"] == "path" && !validatable.input["required"]
38
+ validatable.add_error(
48
39
  "Must be included and true for a path parameter",
49
- Context.next_field(context, "required"),
50
- self.class
40
+ Context.next_field(validatable.context, "required")
51
41
  )
52
42
  end
43
+ end
44
+
45
+ private
53
46
 
54
- errors
47
+ def build_object(data, context)
48
+ Node::Parameter.new(data, context)
55
49
  end
56
50
 
57
51
  def default_style
@@ -59,9 +53,9 @@ module Openapi3Parser
59
53
  "form"
60
54
  end
61
55
 
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
56
+ def validate_in(validatable)
57
+ return if %w[header query cookie path].include?(validatable.input)
58
+ validatable.add_error("in can only be header, query, cookie, or path")
65
59
  end
66
60
  end
67
61
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Openapi3Parser
4
+ module NodeFactory
5
+ module ParameterLike
6
+ def default_explode
7
+ context.input["style"] == "form"
8
+ end
9
+
10
+ def schema_factory(context)
11
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Schema)
12
+ factory.call(context)
13
+ end
14
+
15
+ def examples_factory(context)
16
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Schema)
17
+ NodeFactory::Map.new(context,
18
+ default: nil,
19
+ value_factory: factory)
20
+ end
21
+
22
+ def content_factory(context)
23
+ NodeFactory::Map.new(context,
24
+ default: nil,
25
+ value_factory: NodeFactory::MediaType,
26
+ validate: method(:validate_content))
27
+ end
28
+
29
+ def validate_content(validatable)
30
+ return if validatable.input.size == 1
31
+ validatable.add_error("Must only have one item")
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ # These are in the footer as a cyclic dependency can stop this module loading
38
+ require "openapi3_parser/node_factory/optional_reference"
39
+ require "openapi3_parser/node_factory/map"
40
+ require "openapi3_parser/node_factory/schema"
41
+ require "openapi3_parser/node_factory/example"
42
+ require "openapi3_parser/node_factory/media_type"
@@ -3,19 +3,17 @@
3
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
- require "openapi3_parser/node_factory/object/node_builder"
6
+ require "openapi3_parser/node_factory/object_factory/node_builder"
7
7
  require "openapi3_parser/node_factory/optional_reference"
8
- require "openapi3_parser/node_factories/array"
9
- require "openapi3_parser/node_factories/server"
10
- require "openapi3_parser/node_factories/operation"
11
- require "openapi3_parser/node_factories/parameter"
8
+ require "openapi3_parser/node_factory/array"
9
+ require "openapi3_parser/node_factory/server"
10
+ require "openapi3_parser/node_factory/operation"
11
+ require "openapi3_parser/node_factory/parameter"
12
12
  require "openapi3_parser/validators/duplicate_parameters"
13
13
 
14
14
  module Openapi3Parser
15
- module NodeFactories
16
- class PathItem
17
- include NodeFactory::Object
18
-
15
+ module NodeFactory
16
+ class PathItem < NodeFactory::Object
19
17
  allow_extensions
20
18
  field "$ref", input_type: String, factory: :ref_factory
21
19
  field "summary", input_type: String
@@ -31,10 +29,6 @@ module Openapi3Parser
31
29
  field "servers", factory: :servers_factory
32
30
  field "parameters", factory: :parameters_factory
33
31
 
34
- def node_data
35
- NodeBuilder.new(processed_input, self).data
36
- end
37
-
38
32
  private
39
33
 
40
34
  def build_object(data, context)
@@ -45,44 +39,48 @@ module Openapi3Parser
45
39
  end
46
40
 
47
41
  def ref_factory(context)
48
- Fields::Reference.new(context, self.class)
42
+ NodeFactory::Fields::Reference.new(context, self.class)
49
43
  end
50
44
 
51
45
  def operation_factory(context)
52
- NodeFactories::Operation.new(context)
46
+ NodeFactory::Operation.new(context)
53
47
  end
54
48
 
55
49
  def servers_factory(context)
56
- NodeFactories::Array.new(
50
+ NodeFactory::Array.new(
57
51
  context,
58
- value_factory: NodeFactories::Server
52
+ value_factory: NodeFactory::Server
59
53
  )
60
54
  end
61
55
 
62
56
  def build_resolved_input
63
- ref = processed_input["$ref"]
64
- data = super.tap { |d| d.delete("$ref") }
65
- return data unless ref
57
+ ref = data["$ref"]
58
+ data_without_ref = super.tap { |d| d.delete("$ref") }
59
+ return data_without_ref unless ref
66
60
 
67
- merge_data(ref.data || {}, data)
61
+ merge_data(ref.resolved_input || {}, data_without_ref)
68
62
  end
69
63
 
70
64
  def merge_data(base, priority)
71
- base.merge(priority) do |_, new, old|
65
+ base.merge(priority) do |_, old, new|
72
66
  new.nil? ? old : new
73
67
  end
74
68
  end
75
69
 
76
70
  def parameters_factory(context)
77
- factory = NodeFactory::OptionalReference.new(NodeFactories::Parameter)
78
-
79
- validate = lambda do |_input, array_factory|
80
- Validators::DuplicateParameters.call(array_factory.resolved_input)
71
+ factory = NodeFactory::OptionalReference.new(NodeFactory::Parameter)
72
+
73
+ validate_parameters = lambda do |validatable|
74
+ validatable.add_error(
75
+ Validators::DuplicateParameters.call(
76
+ validatable.factory.resolved_input
77
+ )
78
+ )
81
79
  end
82
80
 
83
- NodeFactories::Array.new(context,
84
- value_factory: factory,
85
- validate: validate)
81
+ NodeFactory::Array.new(context,
82
+ value_factory: factory,
83
+ validate: validate_parameters)
86
84
  end
87
85
  end
88
86
  end
@@ -2,13 +2,12 @@
2
2
 
3
3
  require "openapi3_parser/node_factory/map"
4
4
  require "openapi3_parser/node_factory/optional_reference"
5
- require "openapi3_parser/node_factories/path_item"
5
+ require "openapi3_parser/node_factory/path_item"
6
6
  require "openapi3_parser/node/paths"
7
7
 
8
8
  module Openapi3Parser
9
- module NodeFactories
10
- class Paths
11
- include NodeFactory::Map
9
+ module NodeFactory
10
+ class Paths < NodeFactory::Map
12
11
  PATH_REGEX = %r{
13
12
  \A
14
13
  # required prefix slash
@@ -24,46 +23,41 @@ module Openapi3Parser
24
23
  \Z
25
24
  }x
26
25
 
27
- private
26
+ def initialize(context)
27
+ factory = NodeFactory::OptionalReference.new(NodeFactory::PathItem)
28
28
 
29
- def process_input(input)
30
- input.each_with_object({}) do |(key, value), memo|
31
- memo[key] = value if extension?(key)
32
- next_context = Context.next_field(context, key)
33
- memo[key] = child_factory(next_context)
34
- end
29
+ super(context,
30
+ allow_extensions: true,
31
+ value_factory: factory,
32
+ validate: :validate)
35
33
  end
36
34
 
37
- def child_factory(child_context)
38
- NodeFactory::OptionalReference.new(NodeFactories::PathItem)
39
- .call(child_context)
40
- end
35
+ private
41
36
 
42
- def build_map(data, context)
37
+ def build_node(data)
43
38
  Node::Paths.new(data, context)
44
39
  end
45
40
 
46
- def validate(input, _context)
47
- paths = input.keys.reject { |key| extension?(key) }
48
- validate_paths(paths)
41
+ def validate(validatable)
42
+ paths = validatable.input.keys.reject do |key|
43
+ NodeFactory::EXTENSION_REGEX =~ key
44
+ end
45
+ validate_paths(validatable, paths)
49
46
  end
50
47
 
51
- def validate_paths(paths)
48
+ def validate_paths(validatable, paths)
52
49
  invalid_paths = paths.reject { |p| PATH_REGEX.match(p) }
53
- errors = []
54
50
  unless invalid_paths.empty?
55
51
  joined = invalid_paths.map { |p| "'#{p}'" }.join(", ")
56
- errors << %(There are invalid paths: #{joined})
52
+ validatable.add_error("There are invalid paths: #{joined}")
57
53
  end
58
54
 
59
55
  conflicts = conflicting_paths(paths)
60
56
 
61
- unless conflicts.empty?
62
- joined = conflicts.map { |p| "'#{p}'" }.join(", ")
63
- errors << %(There are paths that conflict: #{joined})
64
- end
57
+ return if conflicts.empty?
65
58
 
66
- errors
59
+ joined = conflicts.map { |p| "'#{p}'" }.join(", ")
60
+ validatable.add_error("There are paths that conflict: #{joined}")
67
61
  end
68
62
 
69
63
  def conflicting_paths(paths)