openapi3_parser 0.1.0 → 0.2.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +5 -1
  5. data/TODO.md +1 -0
  6. data/lib/openapi3_parser.rb +13 -0
  7. data/lib/openapi3_parser/context.rb +2 -2
  8. data/lib/openapi3_parser/node_factories/array.rb +4 -2
  9. data/lib/openapi3_parser/node_factories/components.rb +4 -0
  10. data/lib/openapi3_parser/node_factories/schema.rb +15 -2
  11. data/lib/openapi3_parser/node_factories/server_variable.rb +10 -2
  12. data/lib/openapi3_parser/node_factory.rb +17 -1
  13. data/lib/openapi3_parser/node_factory/map.rb +4 -0
  14. data/lib/openapi3_parser/node_factory/object.rb +3 -5
  15. data/lib/openapi3_parser/node_factory/object/node_builder.rb +19 -8
  16. data/lib/openapi3_parser/nodes/array.rb +8 -0
  17. data/lib/openapi3_parser/nodes/callback.rb +1 -0
  18. data/lib/openapi3_parser/nodes/components.rb +12 -0
  19. data/lib/openapi3_parser/nodes/contact.rb +4 -0
  20. data/lib/openapi3_parser/nodes/discriminator.rb +3 -0
  21. data/lib/openapi3_parser/nodes/encoding.rb +6 -0
  22. data/lib/openapi3_parser/nodes/example.rb +5 -0
  23. data/lib/openapi3_parser/nodes/external_documentation.rb +3 -0
  24. data/lib/openapi3_parser/nodes/header.rb +1 -0
  25. data/lib/openapi3_parser/nodes/info.rb +7 -0
  26. data/lib/openapi3_parser/nodes/license.rb +3 -0
  27. data/lib/openapi3_parser/nodes/link.rb +7 -0
  28. data/lib/openapi3_parser/nodes/map.rb +6 -0
  29. data/lib/openapi3_parser/nodes/media_type.rb +5 -0
  30. data/lib/openapi3_parser/nodes/oauth_flow.rb +5 -0
  31. data/lib/openapi3_parser/nodes/oauth_flows.rb +5 -0
  32. data/lib/openapi3_parser/nodes/openapi.rb +11 -0
  33. data/lib/openapi3_parser/nodes/operation.rb +16 -0
  34. data/lib/openapi3_parser/nodes/parameter.rb +3 -0
  35. data/lib/openapi3_parser/nodes/parameter/parameter_like.rb +14 -0
  36. data/lib/openapi3_parser/nodes/path_item.rb +14 -0
  37. data/lib/openapi3_parser/nodes/paths.rb +1 -0
  38. data/lib/openapi3_parser/nodes/request_body.rb +4 -0
  39. data/lib/openapi3_parser/nodes/response.rb +5 -0
  40. data/lib/openapi3_parser/nodes/responses.rb +2 -0
  41. data/lib/openapi3_parser/nodes/schema.rb +38 -0
  42. data/lib/openapi3_parser/nodes/security_requirement.rb +1 -0
  43. data/lib/openapi3_parser/nodes/security_scheme.rb +9 -0
  44. data/lib/openapi3_parser/nodes/server.rb +5 -2
  45. data/lib/openapi3_parser/nodes/server_variable.rb +4 -0
  46. data/lib/openapi3_parser/nodes/tag.rb +4 -0
  47. data/lib/openapi3_parser/nodes/xml.rb +8 -2
  48. data/lib/openapi3_parser/version.rb +1 -1
  49. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3c7e2585cb3bbdbda459519eb7e264dfd89f5745
4
- data.tar.gz: 4fdbb4b547e626e1b0ba5f502b93107638af3655
3
+ metadata.gz: f60ae34f39675f7ce3616bd96dc3e383275d117d
4
+ data.tar.gz: 4ecf6bffd69c875778c4d096f29cf7b5deadbf7e
5
5
  SHA512:
6
- metadata.gz: 498d2043a338970c66ef9d14590adb34ec2f7b65e751985f8d12a8c9506d63e09a9840e7521b00a953000af1e286917147fb4507be29f68a5c047aa9f124f7d8
7
- data.tar.gz: 7fb4d6b168b514fa01056fa5be0026551ad02e0e47506e99516b14b290e450c0940031e82fc38bdc0f4c5d137d98d3a621f28d42bd6e5923f335a7c31255796e
6
+ metadata.gz: f5a96679f760117e345c9e992161444c06df7d667953fad6badf547dd83dee449b672cc9f265a26a7166e2cdec887d5205bf0dcda6abdbece2a2ce74aeb61153
7
+ data.tar.gz: 087aea7ab5f4abceb69b9acce6854f70dac44fca1620bde695d7dd1a7813ca1a468728e3cc5a08ab5ae31d5320c75459f9537ce584c6ffbff082ca796d9a3602
data/.gitignore CHANGED
@@ -1 +1,4 @@
1
1
  /Gemfile.lock
2
+ /pkg
3
+ /.yardoc
4
+ /doc
@@ -0,0 +1,6 @@
1
+ # 0.2.0
2
+
3
+ - Allow defaulting to empty arrays and maps
4
+ - Configure rubydoc
5
+ - Types returned documented for the nodes
6
+
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # OpenAPI 3 Parser
2
2
 
3
- [![Build Status](https://travis-ci.org/kevindew/openapi_parser.svg?branch=master)](https://travis-ci.org/kevindew/openapi_parser)
3
+ [![Build Status](https://travis-ci.org/kevindew/openapi3_parser.svg?branch=master)](https://travis-ci.org/kevindew/openapi3_parser)
4
4
 
5
5
 
6
6
  This is a parser/validator for [Open API 3][openapi-3] built in Ruby.
@@ -19,7 +19,11 @@ document.valid?
19
19
  document.paths["/"]
20
20
  ```
21
21
 
22
+ Documentation for the API to navigate the OpenAPI nodes is available on
23
+ [rubydoc.info][docs].
24
+
22
25
  [openapi-3]: https://github.com/OAI/OpenAPI-Specification
26
+ [docs]: http://www.rubydoc.info/github/kevindew/openapi3_parser/Openapi3Parser/Nodes/Openapi
23
27
 
24
28
  ## Status
25
29
 
data/TODO.md CHANGED
@@ -28,3 +28,4 @@ These are the steps defined to reach 1.0. Assistance is very welcome.
28
28
  - [ ] Improve the modelling of namespace
29
29
  - [ ] Set up nicer string representations of key classes to help them be
30
30
  debugged
31
+ - [ ] Ensure Array and Map nodes return empty ones by default rather than nil
@@ -7,6 +7,12 @@ require "yaml"
7
7
  require "json"
8
8
 
9
9
  module Openapi3Parser
10
+ # For a variety of inputs this will construct an OpenAPI document. For a
11
+ # String/File input it will try to determine if the input is JSON or YAML.
12
+ #
13
+ # @param [String, Hash, File] input Source for the OpenAPI document
14
+ #
15
+ # @return [Document]
10
16
  def self.load(input)
11
17
  # working_directory ||= if input.respond_to?(:read)
12
18
  # File.dirname(input)
@@ -17,6 +23,13 @@ module Openapi3Parser
17
23
  Document.new(parse_input(input))
18
24
  end
19
25
 
26
+ # For a given string filename this will read the file and parse it as an
27
+ # OpenAPI document. It will try detect automatically whether the contents
28
+ # are JSON or YAML.
29
+ #
30
+ # @param [String] path Filename of the OpenAPI document
31
+ #
32
+ # @return [Document]
20
33
  def self.load_file(path)
21
34
  file = File.open(path)
22
35
  load(file)
@@ -18,12 +18,12 @@ module Openapi3Parser
18
18
  def stringify_namespace
19
19
  return "root" if namespace.empty?
20
20
  namespace
21
- .map { |i| i.include?("/") ? %("#{i}") : i }
21
+ .map { |i| i.to_s.include?("/") ? %("#{i}") : i }
22
22
  .join("/")
23
23
  end
24
24
 
25
25
  def next_namespace(segment, next_input = nil)
26
- next_input ||= input[segment]
26
+ next_input ||= input.nil? ? nil : input[segment]
27
27
  self.class.new(
28
28
  input: next_input,
29
29
  namespace: namespace + [segment],
@@ -12,11 +12,13 @@ module Openapi3Parser
12
12
 
13
13
  def initialize(
14
14
  context,
15
+ default: [],
15
16
  value_input_type: nil,
16
17
  value_factory: nil,
17
18
  validate: nil
18
19
  )
19
20
  super(context)
21
+ @default = default
20
22
  @given_value_input_type = value_input_type
21
23
  @given_value_factory = value_factory
22
24
  @given_validate = validate
@@ -24,8 +26,8 @@ module Openapi3Parser
24
26
 
25
27
  private
26
28
 
27
- attr_reader :given_value_input_type, :given_value_factory,
28
- :given_validate
29
+ attr_reader :default, :given_value_input_type,
30
+ :given_value_factory, :given_validate
29
31
 
30
32
  def process_input(input)
31
33
  input.each_with_index.map do |value, i|
@@ -78,6 +78,10 @@ module Openapi3Parser
78
78
  value_factory: NodeFactory::OptionalReference.new(factory)
79
79
  )
80
80
  end
81
+
82
+ def default
83
+ {}
84
+ end
81
85
  end
82
86
  end
83
87
  end
@@ -29,8 +29,8 @@ module Openapi3Parser
29
29
  field "uniqueItems", input_type: :boolean, default: false
30
30
  field "maxProperties", input_type: Integer
31
31
  field "minProperties", input_type: Integer, default: 0
32
- field "required", input_type: ::Array
33
- field "enum", input_type: ::Array
32
+ field "required", factory: :required_factory
33
+ field "enum", factory: :enum_factory
34
34
 
35
35
  field "type", input_type: String
36
36
  field "allOf", factory: :referenceable_schema_array
@@ -62,6 +62,18 @@ module Openapi3Parser
62
62
  Nodes::Schema.new(data, context)
63
63
  end
64
64
 
65
+ def required_factory(context)
66
+ NodeFactories::Array.new(
67
+ context,
68
+ default: nil,
69
+ value_input_type: String
70
+ )
71
+ end
72
+
73
+ def enum_factory(context)
74
+ NodeFactories::Array.new(context, default: nil)
75
+ end
76
+
65
77
  def disciminator_factory(context)
66
78
  NodeFactories::Discriminator.new(context)
67
79
  end
@@ -88,6 +100,7 @@ module Openapi3Parser
88
100
  def referenceable_schema_array(context)
89
101
  NodeFactories::Array.new(
90
102
  context,
103
+ default: nil,
91
104
  value_factory: NodeFactory::OptionalReference.new(self.class)
92
105
  )
93
106
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "openapi3_parser/nodes/server_variable"
4
4
  require "openapi3_parser/node_factory/object"
5
+ require "openapi3_parser/node_factories/array"
5
6
 
6
7
  module Openapi3Parser
7
8
  module NodeFactories
@@ -9,15 +10,22 @@ module Openapi3Parser
9
10
  include NodeFactory::Object
10
11
 
11
12
  allow_extensions
12
- field "enum", input_type: ::Array, validate: :validate_enum
13
+ field "enum", factory: :enum_factory, validate: :validate_enum
13
14
  field "default", input_type: String, required: true
14
15
  field "description", input_type: String
15
16
 
16
17
  private
17
18
 
19
+ def enum_factory(context)
20
+ NodeFactories::Array.new(
21
+ context,
22
+ default: nil,
23
+ value_input_type: String
24
+ )
25
+ end
26
+
18
27
  def validate_enum(input)
19
28
  return "Expected atleast one value" if input.empty?
20
- "Expected String values" unless input.map(&:class).uniq == [String]
21
29
  end
22
30
 
23
31
  def build_object(data, context)
@@ -45,6 +45,10 @@ module Openapi3Parser
45
45
  @node ||= build_valid_node
46
46
  end
47
47
 
48
+ def nil_input?
49
+ context.input.nil?
50
+ end
51
+
48
52
  private
49
53
 
50
54
  def validate(_input, _context); end
@@ -69,6 +73,7 @@ module Openapi3Parser
69
73
 
70
74
  def build_errors
71
75
  error_collection = Validation::ErrorCollection.new
76
+ return error_collection if nil_input?
72
77
  unless valid_type?
73
78
  error = Validation::Error.new(
74
79
  context.namespace, "Invalid type. #{validate_type}"
@@ -80,6 +85,10 @@ module Openapi3Parser
80
85
  end
81
86
 
82
87
  def build_valid_node
88
+ if nil_input?
89
+ return default.nil? ? nil : build_node(processed_input)
90
+ end
91
+
83
92
  unless valid_type?
84
93
  raise Openapi3Parser::Error,
85
94
  "Invalid type for #{context.stringify_namespace}. "\
@@ -108,7 +117,10 @@ module Openapi3Parser
108
117
  end
109
118
 
110
119
  def processed_input
111
- @processed_input ||= process_input(context.input)
120
+ @processed_input ||= begin
121
+ input = nil_input? ? default : context.input
122
+ process_input(input)
123
+ end
112
124
  end
113
125
 
114
126
  def process_input(input)
@@ -119,6 +131,10 @@ module Openapi3Parser
119
131
  input
120
132
  end
121
133
 
134
+ def default
135
+ nil
136
+ end
137
+
122
138
  def extension?(key)
123
139
  key.match(EXTENSION_REGEX)
124
140
  end
@@ -34,6 +34,10 @@ module Openapi3Parser
34
34
  def build_map(data, _)
35
35
  data
36
36
  end
37
+
38
+ def default
39
+ {}
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -53,17 +53,15 @@ module Openapi3Parser
53
53
 
54
54
  def process_input(input)
55
55
  field_configs.each_with_object(input.dup) do |(field, config), memo|
56
- next if !config.factory? || !memo[field]
56
+ next unless config.factory?
57
57
  next_context = context.next_namespace(field)
58
- memo[field] = config.initialize_factory(
59
- next_context, self
60
- )
58
+ memo[field] = config.initialize_factory(next_context, self)
61
59
  end
62
60
  end
63
61
 
64
62
  def validate_input(error_collection)
65
63
  super(error_collection)
66
- validator = Validator.new(processed_input, self)
64
+ validator = Validator.new(context.input, self)
67
65
  error_collection.tap { |ec| ec.append(*validator.errors) }
68
66
  end
69
67
 
@@ -16,10 +16,9 @@ module Openapi3Parser
16
16
  check_required_fields
17
17
  check_unexpected_fields
18
18
  check_fields_valid
19
- data = input.each_with_object({}) do |(key, value), memo|
20
- memo[key] = value.respond_to?(:node) ? value.node : value
19
+ input.each_with_object({}) do |(key, value), memo|
20
+ memo[key] = resolve_value(key, value)
21
21
  end
22
- apply_defaults(data)
23
22
  end
24
23
 
25
24
  private
@@ -55,7 +54,8 @@ module Openapi3Parser
55
54
 
56
55
  def check_type_error(name, field_config)
57
56
  field_context = context.next_namespace(name)
58
- error = field_config.input_type_error(input[name], factory)
57
+ input = context.input.nil? ? nil : context.input[name]
58
+ error = field_config.input_type_error(input, factory)
59
59
 
60
60
  return unless error
61
61
  raise Openapi3Parser::Error,
@@ -65,7 +65,7 @@ module Openapi3Parser
65
65
 
66
66
  def check_validation_errors(name, field_config)
67
67
  field_context = context.next_namespace(name)
68
- errors = field_config.validation_errors(input[name], factory)
68
+ errors = field_config.validation_errors(field_context.input, factory)
69
69
 
70
70
  return unless errors.any?
71
71
  raise Openapi3Parser::Error,
@@ -73,10 +73,21 @@ module Openapi3Parser
73
73
  "#{errors.join(', ')}"
74
74
  end
75
75
 
76
- def apply_defaults(data)
76
+ def resolve_value(key, value)
77
77
  configs = factory.field_configs
78
- configs.each_with_object(data) do |(name, field_config), _memo|
79
- data[name] = field_config.default(factory) if data[name].nil?
78
+ return configs[key]&.default(factory) if value.nil?
79
+ default_value(configs[key], value)
80
+ end
81
+
82
+ def default_value(config, value)
83
+ resolved_value = value.respond_to?(:node) ? value.node : value
84
+
85
+ # let a field config default take precedence if value is a nil_input?
86
+ if value.respond_to?(:nil_input?) && value.nil_input?
87
+ default = config&.default(factory)
88
+ default.nil? ? resolved_value : default
89
+ else
90
+ resolved_value
80
91
  end
81
92
  end
82
93
  end
@@ -4,11 +4,19 @@ require "openapi3_parser/node/map"
4
4
 
5
5
  module Openapi3Parser
6
6
  module Nodes
7
+ # An array within a OpenAPI document.
8
+ # Very similar to a normal Ruby array, however this is read only and knows
9
+ # the context of where it sits in an OpenAPI document
10
+ #
11
+ # The contents of the data will be dependent on where this document is in
12
+ # the document hierachy.
7
13
  class Array
8
14
  include Enumerable
9
15
 
10
16
  attr_reader :node_data, :node_context
11
17
 
18
+ # @param [::Array] data data used to populate this node
19
+ # @param [Context] context The context of this node in the document
12
20
  def initialize(data, context)
13
21
  @node_data = data
14
22
  @node_context = context
@@ -4,6 +4,7 @@ require "openapi3_parser/node/map"
4
4
 
5
5
  module Openapi3Parser
6
6
  module Nodes
7
+ # @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#callbackObject
7
8
  class Callback
8
9
  include Node::Map
9
10
  end
@@ -4,41 +4,53 @@ require "openapi3_parser/node/object"
4
4
 
5
5
  module Openapi3Parser
6
6
  module Nodes
7
+ # @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#componentsObject
7
8
  class Components
8
9
  include Node::Object
9
10
 
11
+ # @return [Map] a map of String: {Schema}[../Schema.html] objects
10
12
  def schemas
11
13
  node_data["schemas"]
12
14
  end
13
15
 
16
+ # @return [Map] a map of String: {Response}[./Response.html] objects
14
17
  def responses
15
18
  node_data["responses"]
16
19
  end
17
20
 
21
+ # @return [Map] a map of String: {Parameter}[./Parameter.html] objects
18
22
  def parameters
19
23
  node_data["parameters"]
20
24
  end
21
25
 
26
+ # @return [Map] a map of String: {Example}[../Example.html] objects
22
27
  def examples
23
28
  node_data["examples"]
24
29
  end
25
30
 
31
+ # @return [Map] a map of String: {RequestBody}[./RequestBody.html]
32
+ # objects
26
33
  def request_bodies
27
34
  node_data["requestBodies"]
28
35
  end
29
36
 
37
+ # @return [Map] a map of String: {Header}[./Header.html] objects
30
38
  def headers
31
39
  node_data["headers"]
32
40
  end
33
41
 
42
+ # @return [Map] a map of String: {SecurityScheme}[./SecurityScheme.html]
43
+ # objects
34
44
  def security_schemes
35
45
  node_data["securitySchemes"]
36
46
  end
37
47
 
48
+ # @return [Map] a map of String: {Link}[./Link.html] objects
38
49
  def links
39
50
  node_data["links"]
40
51
  end
41
52
 
53
+ # @return [Map] a map of String: {Callback}[./Callback.html] objects
42
54
  def callbacks
43
55
  node_data["callbacks"]
44
56
  end
@@ -4,17 +4,21 @@ require "openapi3_parser/node/object"
4
4
 
5
5
  module Openapi3Parser
6
6
  module Nodes
7
+ # @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#contactObject
7
8
  class Contact
8
9
  include Node::Object
9
10
 
11
+ # @return [String, nil]
10
12
  def name
11
13
  data["name"]
12
14
  end
13
15
 
16
+ # @return [String, nil]
14
17
  def url
15
18
  data["url"]
16
19
  end
17
20
 
21
+ # @return [String, nil]
18
22
  def email
19
23
  data["email"]
20
24
  end