openapi_parser 0.13.0 → 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yaml +17 -0
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +22 -0
  5. data/README.md +28 -3
  6. data/Rakefile +5 -1
  7. data/Steepfile +11 -0
  8. data/lib/openapi_parser/concerns/expandable.rb +19 -16
  9. data/lib/openapi_parser/concerns/findable.rb +2 -2
  10. data/lib/openapi_parser/concerns/parameter_validatable.rb +7 -1
  11. data/lib/openapi_parser/config.rb +14 -0
  12. data/lib/openapi_parser/errors.rb +17 -0
  13. data/lib/openapi_parser/reference_expander.rb +2 -2
  14. data/lib/openapi_parser/request_operation.rb +0 -1
  15. data/lib/openapi_parser/schema_validator.rb +6 -1
  16. data/lib/openapi_parser/schema_validators/all_of_validator.rb +7 -2
  17. data/lib/openapi_parser/schema_validators/base.rb +9 -10
  18. data/lib/openapi_parser/schema_validators/float_validator.rb +3 -3
  19. data/lib/openapi_parser/schema_validators/integer_validator.rb +3 -5
  20. data/lib/openapi_parser/schema_validators/object_validator.rb +11 -1
  21. data/lib/openapi_parser/schema_validators/string_validator.rb +15 -0
  22. data/lib/openapi_parser/schema_validators/unspecified_type_validator.rb +8 -0
  23. data/lib/openapi_parser/schemas/path_item.rb +4 -2
  24. data/lib/openapi_parser/version.rb +1 -1
  25. data/lib/openapi_parser.rb +11 -6
  26. data/openapi_parser.gemspec +2 -0
  27. data/sig/openapi_parser/config.rbs +17 -0
  28. data/sig/openapi_parser/schema_validator.rbs +46 -0
  29. data/sig/openapi_parser/schema_validators/base.rbs +17 -0
  30. data/sig/openapi_parser/schema_validators/options.rbs +17 -0
  31. data/sig/openapi_parser/schemas/base.rbs +17 -0
  32. data/sig/openapi_parser/version.rbs +3 -0
  33. data/sig/openapi_parser.rbs +19 -0
  34. data/sig/types.rbs +8 -0
  35. data/sig/wip_types.rbs +67 -0
  36. metadata +33 -7
  37. data/.travis.yml +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9afe7e0164ddcad6c8a8fbb6d57e0514e32df10d014bc642b88fa3473f1f8d74
4
- data.tar.gz: cefb18a632f4a16dd948fe2e82fadfc0236a159af2406fdcb52db50ef6dc980e
3
+ metadata.gz: e383e6ff9f0d2a6ac67fda1ac8663b82c249449a7c1cff616a72fb608344a816
4
+ data.tar.gz: 53c6a442a5f170f5190197aa17a805a707fd586d7b4d0fc4c259ed8af46dc2bd
5
5
  SHA512:
6
- metadata.gz: fb0397b9251edac7e3bfb15e30041d5945a466c21b6671183eaec8491a372730e2f76101af71efbc7c07c66345353c260983acfb69d80f2b066c35c71207a076
7
- data.tar.gz: 5a58f4b47946ca9c376dcce3c5be34de998a2bc3d59ee4321e2e7f0425c7cdf6b809ff9eb3a103fe099a849210def6c362431628610fdde137b3b79157a32849
6
+ metadata.gz: 3cd78f54ac0bb0bca68e9538f3f33968b7815b76d88c39908735da2f18936118ecc66ec57a97ac491f2b0fdc7fd4985e312b8b32a4051d22a2242ed8ea55608f
7
+ data.tar.gz: 22fcb549a4c134350274be73bc59114309d612f096b70dfd5633fe049b9b2111c176809575b2ce89fc30a2db3a8cda633c5ce18b82a58a418cd95d76ce23791a
@@ -0,0 +1,17 @@
1
+ name: ci
2
+ on: [push]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ os: [ubuntu-latest, macos-latest]
9
+ ruby: [2.6, 2.7, '3.0', head]
10
+ runs-on: ${{ matrix.os }}
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: ${{ matrix.ruby }}
16
+ bundler-cache: true
17
+ - run: bundle exec rake
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 1.0.0.beta1 (2021-12-15)
4
+ ### Added
5
+ * Add strict_reference_validation config and implementation to address/implement #29 #123
6
+
7
+ ## 0.15.0 (2021-09-27)
8
+ ### Added
9
+ * support: relative file path escape. #117
10
+
11
+ ## 0.14.1 (2021-07-9)
12
+ ### Fixed
13
+ * Fix bug for using path parameter and coerce option #115
14
+
15
+ ## 0.14.0 (2021-05-24)
16
+
17
+ ### Added
18
+ * Add basic polymorphism handling #103
19
+ * Support empty schema as any type #109
20
+ * Add date format validation for string #102
21
+
22
+ ### Fixed
23
+ * Fix anyOf coercion to float and integer when value is a non-string type #110
24
+
3
25
  ## 0.13.0 (2021-05-01)
4
26
  * Fix a problem with remote reference to path items which have path parameters #95
5
27
  * Support enum for booleans. #104
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  # OpenAPI Parser
2
- [![Build Status](https://travis-ci.org/ota42y/openapi_parser.svg?branch=master)](https://travis-ci.org/ota42y/openapi_parser)
2
+ [![ci](https://github.com/ota42y/openapi_parser/actions/workflows/ci.yaml/badge.svg)](https://github.com/ota42y/openapi_parser/actions/workflows/ci.yaml)
3
3
  [![Gem Version](https://badge.fury.io/rb/openapi_parser.svg)](https://badge.fury.io/rb/openapi_parser)
4
4
  [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/openapi_parser)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/62bad4bcb3f691d46487/maintainability)](https://codeclimate.com/github/ota42y/openapi_parser/maintainability)
6
- [![Test Coverage](https://api.codeclimate.com/v1/badges/62bad4bcb3f691d46487/test_coverage)](https://codeclimate.com/github/ota42y/openapi_parser/test_coverage)
7
5
  [![Inch CI](https://inch-ci.org/github/ota42y/openapi_parser.svg?branch=master)](https://inch-ci.org/github/ota42y/openapi_parser)
8
6
 
9
7
  This is OpenAPI3 parser and validator.
@@ -64,6 +62,33 @@ We support additional type validation.
64
62
  |---|---|---|
65
63
  |string|uuid|validate uuid string. But we don't check uuid layout|
66
64
 
65
+ ### Reference Validation on Schema Load
66
+ Invalid references (missing definitions, typos, etc.) can cause validation to fail in runtime,
67
+ and these errors can be difficult to debug (see: https://github.com/ota42y/openapi_parser/issues/29).
68
+
69
+ Pass the `strict_reference_validation: true` option to detect invalid references.
70
+ An `OpenAPIError::MissingReferenceError` exception will be raised when a reference cannot be resolved.
71
+
72
+ If the `expand_reference` configuration is explicitly `false` (default is `true`), then
73
+ this configuration has no effect.
74
+
75
+ DEPRECATION NOTICE: To maintain compatibility with the previous behavior, this version sets `false` as a default.
76
+ This behavior will be changed to `true` in a later version, so you should explicitly pass `strict_reference_validation: false`
77
+ if you wish to keep the old behavior (and please let the maintainers know your use-case for this configuration!).
78
+
79
+ ```ruby
80
+ yaml_file = YAML.load_file('open_api_3/schema_with_broken_references.yml')
81
+ options = {
82
+ coerce_value: true,
83
+ datetime_coerce_class: DateTime,
84
+ # This defaults to false (for now) - passing `true` provides load-time validation of refs
85
+ strict_reference_validation: true
86
+ }
87
+
88
+ # Will raise with OpenAPIParser::MissingReferenceError
89
+ OpenAPIParser.parse(yaml_file, options)
90
+ ```
91
+
67
92
  ## ToDo
68
93
  - correct schema checker
69
94
  - more detailed validator
data/Rakefile CHANGED
@@ -3,4 +3,8 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
- task :default => :spec
6
+ task :steep do
7
+ sh 'steep check'
8
+ end
9
+
10
+ task :default => [:steep, :spec]
data/Steepfile ADDED
@@ -0,0 +1,11 @@
1
+ target :lib do
2
+ signature "sig"
3
+ #check "lib"
4
+
5
+ check "lib/openapi_parser.rb"
6
+ check "lib/openapi_parser/config.rb"
7
+ check "lib/openapi_parser/schema_validators/options.rb"
8
+ check "lib/openapi_parser/schema_validators/base.rb"
9
+
10
+ library 'uri'
11
+ end
@@ -2,28 +2,28 @@ module OpenAPIParser::Expandable
2
2
  # expand refs
3
3
  # @param [OpenAPIParser::Schemas::Base] root
4
4
  # @return nil
5
- def expand_reference(root)
6
- expand_list_objects(root, self.class._openapi_attr_list_objects.keys)
7
- expand_objects(root, self.class._openapi_attr_objects.keys)
8
- expand_hash_objects(root, self.class._openapi_attr_hash_objects.keys)
9
- expand_hash_objects(root, self.class._openapi_attr_hash_body_objects.keys)
5
+ def expand_reference(root, validate_references)
6
+ expand_list_objects(root, self.class._openapi_attr_list_objects.keys, validate_references)
7
+ expand_objects(root, self.class._openapi_attr_objects.keys, validate_references)
8
+ expand_hash_objects(root, self.class._openapi_attr_hash_objects.keys, validate_references)
9
+ expand_hash_objects(root, self.class._openapi_attr_hash_body_objects.keys, validate_references)
10
10
  nil
11
11
  end
12
12
 
13
13
  private
14
14
 
15
- def expand_hash_objects(root, attribute_names)
15
+ def expand_hash_objects(root, attribute_names, validate_references)
16
16
  return unless attribute_names
17
17
 
18
- attribute_names.each { |name| expand_hash_attribute(root, name) }
18
+ attribute_names.each { |name| expand_hash_attribute(root, name, validate_references) }
19
19
  end
20
20
 
21
- def expand_hash_attribute(root, name)
21
+ def expand_hash_attribute(root, name, validate_references)
22
22
  h = send(name)
23
23
  return if h.nil?
24
24
 
25
25
  update_values = h.map do |k, v|
26
- new_object = expand_object(root, v)
26
+ new_object = expand_object(root, v, validate_references)
27
27
  new_object.nil? ? nil : [k, new_object]
28
28
  end
29
29
 
@@ -33,14 +33,14 @@ module OpenAPIParser::Expandable
33
33
  end
34
34
  end
35
35
 
36
- def expand_objects(root, attribute_names)
36
+ def expand_objects(root, attribute_names, validate_references)
37
37
  return unless attribute_names
38
38
 
39
39
  attribute_names.each do |name|
40
40
  v = send(name)
41
41
  next if v.nil?
42
42
 
43
- new_object = expand_object(root, v)
43
+ new_object = expand_object(root, v, validate_references)
44
44
  next if new_object.nil?
45
45
 
46
46
  _update_child_object(v, new_object)
@@ -48,7 +48,7 @@ module OpenAPIParser::Expandable
48
48
  end
49
49
  end
50
50
 
51
- def expand_list_objects(root, attribute_names)
51
+ def expand_list_objects(root, attribute_names, validate_references)
52
52
  return unless attribute_names
53
53
 
54
54
  attribute_names.each do |name|
@@ -56,7 +56,7 @@ module OpenAPIParser::Expandable
56
56
  next if l.nil?
57
57
 
58
58
  l.each_with_index do |v, idx|
59
- new_object = expand_object(root, v)
59
+ new_object = expand_object(root, v, validate_references)
60
60
  next if new_object.nil?
61
61
 
62
62
  _update_child_object(v, new_object)
@@ -65,12 +65,15 @@ module OpenAPIParser::Expandable
65
65
  end
66
66
  end
67
67
 
68
- def expand_object(root, object)
68
+ def expand_object(root, object, validate_references)
69
69
  if object.kind_of?(OpenAPIParser::Schemas::Reference)
70
- return referenced_object(root, object)
70
+ ref_object = referenced_object(root, object)
71
+ raise OpenAPIParser::MissingReferenceError.new(object.ref) if ref_object.nil? && validate_references
72
+
73
+ return ref_object
71
74
  end
72
75
 
73
- object.expand_reference(root) if object.kind_of?(OpenAPIParser::Expandable)
76
+ object.expand_reference(root, validate_references) if object.kind_of?(OpenAPIParser::Expandable)
74
77
  nil
75
78
  end
76
79
 
@@ -46,8 +46,8 @@ module OpenAPIParser::Findable
46
46
  private
47
47
 
48
48
  def find_remote_object(reference)
49
- reference_uri = URI(reference)
50
- fragment = reference_uri.fragment
49
+ uri, fragment = reference.split("#", 2)
50
+ reference_uri = URI(uri)
51
51
  reference_uri.fragment = nil
52
52
  root.load_another_schema(reference_uri)&.find_object("##{fragment}")
53
53
  end
@@ -13,6 +13,12 @@ module OpenAPIParser::ParameterValidatable
13
13
  validate_query_parameter(params, object_reference, options)
14
14
  end
15
15
 
16
+ # @param [PathItem] path_item parent
17
+ def set_parent_path_item(path_item)
18
+ @merged_parameter = (parameters || []) + (path_item.parameters || [])
19
+ nil
20
+ end
21
+
16
22
  private
17
23
 
18
24
  # @param [Hash] params query parameter hash
@@ -44,7 +50,7 @@ module OpenAPIParser::ParameterValidatable
44
50
  # @return [Hash{String => Hash{String => Parameter}}] hash[in][name] => Parameter
45
51
  def divided_parameter_hash
46
52
  @divided_parameter_hash ||=
47
- (parameters || []).
53
+ (@merged_parameter || []).
48
54
  group_by(&:in).
49
55
  map { |in_type, params| # rubocop:disable Style/BlockDelimiters
50
56
  [
@@ -1,5 +1,13 @@
1
1
  class OpenAPIParser::Config
2
2
  def initialize(config)
3
+ # TODO: This deprecation warning can be removed after we set the default to `true`
4
+ # in a later (major?) version update.
5
+ unless config.key?(:strict_reference_validation)
6
+ msg = "[DEPRECATION] strict_reference_validation config is not set. It defaults to `false` now, " +
7
+ "but will be `true` in a future version. Please explicitly set to `false` " +
8
+ "if you want to skip reference validation on schema load."
9
+ warn(msg)
10
+ end
3
11
  @config = config
4
12
  end
5
13
 
@@ -16,9 +24,15 @@ class OpenAPIParser::Config
16
24
  end
17
25
 
18
26
  def strict_response_validation
27
+ # TODO: in a major version update, change this to default to `true`.
28
+ # https://github.com/ota42y/openapi_parser/pull/123/files#r767142217
19
29
  @config.fetch(:strict_response_validation, false)
20
30
  end
21
31
 
32
+ def strict_reference_validation
33
+ @config.fetch(:strict_reference_validation, false)
34
+ end
35
+
22
36
  def validate_header
23
37
  @config.fetch(:validate_header, true)
24
38
  end
@@ -5,6 +5,12 @@ module OpenAPIParser
5
5
  end
6
6
  end
7
7
 
8
+ class MissingReferenceError < OpenAPIError
9
+ def message
10
+ "'#{@reference}' was referenced but could not be found"
11
+ end
12
+ end
13
+
8
14
  class ValidateError < OpenAPIError
9
15
  def initialize(data, type, reference)
10
16
  super(reference)
@@ -189,6 +195,17 @@ module OpenAPIParser
189
195
  end
190
196
  end
191
197
 
198
+ class InvalidDateFormat < OpenAPIError
199
+ def initialize(value, reference)
200
+ super(reference)
201
+ @value = value
202
+ end
203
+
204
+ def message
205
+ "#{@reference} Value: #{@value.inspect} is not conformant with date format"
206
+ end
207
+ end
208
+
192
209
  class NotExistStatusCodeDefinition < OpenAPIError
193
210
  def message
194
211
  "#{@reference} status code definition does not exist"
@@ -1,8 +1,8 @@
1
1
  class OpenAPIParser::ReferenceExpander
2
2
  class << self
3
3
  # @param [OpenAPIParser::Schemas::OpenAPI] openapi
4
- def expand(openapi)
5
- openapi.expand_reference(openapi)
4
+ def expand(openapi, validate_references)
5
+ openapi.expand_reference(openapi, validate_references)
6
6
  openapi.purge_object_cache
7
7
  end
8
8
  end
@@ -65,7 +65,6 @@ class OpenAPIParser::RequestOperation
65
65
  # @param [OpenAPIParser::SchemaValidator::Options] options request validator options
66
66
  def validate_request_parameter(params, headers, options = nil)
67
67
  options ||= config.request_validator_options
68
- path_item&.validate_request_parameter(params, headers, options)
69
68
  operation_object&.validate_request_parameter(params, headers, options)
70
69
  end
71
70
 
@@ -12,6 +12,7 @@ require_relative 'schema_validators/any_of_validator'
12
12
  require_relative 'schema_validators/all_of_validator'
13
13
  require_relative 'schema_validators/one_of_validator'
14
14
  require_relative 'schema_validators/nil_validator'
15
+ require_relative 'schema_validators/unspecified_type_validator'
15
16
 
16
17
  class OpenAPIParser::SchemaValidator
17
18
  # validate value by schema
@@ -113,7 +114,7 @@ class OpenAPIParser::SchemaValidator
113
114
  when 'array'
114
115
  array_validator
115
116
  else
116
- nil
117
+ unspecified_type_validator
117
118
  end
118
119
  end
119
120
 
@@ -156,4 +157,8 @@ class OpenAPIParser::SchemaValidator
156
157
  def nil_validator
157
158
  @nil_validator ||= OpenAPIParser::SchemaValidator::NilValidator.new(self, @coerce_value)
158
159
  end
160
+
161
+ def unspecified_type_validator
162
+ @unspecified_type_validator ||= OpenAPIParser::SchemaValidator::UnspecifiedTypeValidator.new(self, @coerce_value)
163
+ end
159
164
  end
@@ -4,13 +4,18 @@ class OpenAPIParser::SchemaValidator
4
4
  # coerce and validate value
5
5
  # @param [Object] value
6
6
  # @param [OpenAPIParser::Schemas::Schema] schema
7
- def coerce_and_validate(value, schema, **_keyword_args)
7
+ def coerce_and_validate(value, schema, **keyword_args)
8
8
  # if any schema return error, it's not valida all of value
9
9
  remaining_keys = value.kind_of?(Hash) ? value.keys : []
10
10
  nested_additional_properties = false
11
11
  schema.all_of.each do |s|
12
12
  # We need to store the reference to all of, so we can perform strict check on allowed properties
13
- _coerced, err = validatable.validate_schema(value, s, :parent_all_of => true)
13
+ _coerced, err = validatable.validate_schema(
14
+ value,
15
+ s,
16
+ :parent_all_of => true,
17
+ parent_discriminator_schemas: keyword_args[:parent_discriminator_schemas]
18
+ )
14
19
 
15
20
  if s.type == "object"
16
21
  remaining_keys -= (s.properties || {}).keys
@@ -1,6 +1,5 @@
1
1
  class OpenAPIParser::SchemaValidator
2
2
  class Base
3
- # @param [OpenAPIParser::SchemaValidator::Validatable] validatable
4
3
  def initialize(validatable, coerce_value)
5
4
  @validatable = validatable
6
5
  @coerce_value = coerce_value
@@ -8,21 +7,17 @@ class OpenAPIParser::SchemaValidator
8
7
 
9
8
  attr_reader :validatable
10
9
 
11
- # @!attribute [r] validatable
12
- # @return [OpenAPIParser::SchemaValidator::Validatable]
13
-
14
10
  # need override
15
- # @param [Array] _value
16
- # @param [OpenAPIParser::Schemas::Schema] _schema
17
11
  def coerce_and_validate(_value, _schema, **_keyword_args)
18
12
  raise 'need implement'
19
13
  end
20
14
 
21
- def validate_discriminator_schema(discriminator, value)
22
- unless value.key?(discriminator.property_name)
15
+ def validate_discriminator_schema(discriminator, value, parent_discriminator_schemas: [])
16
+ property_name = discriminator.property_name
17
+ unless (property_name && value.key?(property_name))
23
18
  return [nil, OpenAPIParser::NotExistDiscriminatorPropertyName.new(discriminator.property_name, value, discriminator.object_reference)]
24
19
  end
25
- mapping_key = value[discriminator.property_name]
20
+ mapping_key = value[property_name]
26
21
 
27
22
  # it's allowed to have discriminator without mapping, then we need to lookup discriminator.property_name
28
23
  # but the format is not the full path, just model name in the components
@@ -34,7 +29,11 @@ class OpenAPIParser::SchemaValidator
34
29
  unless resolved_schema
35
30
  return [nil, OpenAPIParser::NotExistDiscriminatorMappedSchema.new(mapping_target, discriminator.object_reference)]
36
31
  end
37
- validatable.validate_schema(value, resolved_schema, **{discriminator_property_name: discriminator.property_name})
32
+ validatable.validate_schema(
33
+ value,
34
+ resolved_schema,
35
+ **{discriminator_property_name: discriminator.property_name, parent_discriminator_schemas: parent_discriminator_schemas}
36
+ )
38
37
  end
39
38
  end
40
39
  end
@@ -21,14 +21,14 @@ class OpenAPIParser::SchemaValidator
21
21
 
22
22
  value, err = check_enum_include(value, schema)
23
23
  return [nil, err] if err
24
-
24
+
25
25
  check_minimum_maximum(value, schema)
26
26
  end
27
27
 
28
28
  def coerce(value)
29
29
  Float(value)
30
- rescue ArgumentError => e
31
- raise e unless e.message =~ /invalid value for Float/
30
+ rescue ArgumentError, TypeError
31
+ value
32
32
  end
33
33
  end
34
34
  end
@@ -23,12 +23,10 @@ class OpenAPIParser::SchemaValidator
23
23
  return value if value.kind_of?(Integer)
24
24
 
25
25
  begin
26
- return Integer(value)
27
- rescue ArgumentError => e
28
- raise e unless e.message =~ /invalid value for Integer/
26
+ Integer(value)
27
+ rescue ArgumentError, TypeError
28
+ value
29
29
  end
30
-
31
- value
32
30
  end
33
31
  end
34
32
  end
@@ -4,7 +4,7 @@ class OpenAPIParser::SchemaValidator
4
4
  # @param [OpenAPIParser::Schemas::Schema] schema
5
5
  # @param [Boolean] parent_all_of true if component is nested under allOf
6
6
  # @param [String, nil] discriminator_property_name discriminator.property_name to ignore checking additional_properties
7
- def coerce_and_validate(value, schema, parent_all_of: false, discriminator_property_name: nil)
7
+ def coerce_and_validate(value, schema, parent_all_of: false, parent_discriminator_schemas: [], discriminator_property_name: nil)
8
8
  return OpenAPIParser::ValidateError.build_error_result(value, schema) unless value.kind_of?(Hash)
9
9
 
10
10
  properties = schema.properties || {}
@@ -12,6 +12,16 @@ class OpenAPIParser::SchemaValidator
12
12
  required_set = schema.required ? schema.required.to_set : Set.new
13
13
  remaining_keys = value.keys
14
14
 
15
+ if schema.discriminator && !parent_discriminator_schemas.include?(schema)
16
+ return validate_discriminator_schema(
17
+ schema.discriminator,
18
+ value,
19
+ parent_discriminator_schemas: parent_discriminator_schemas + [schema]
20
+ )
21
+ else
22
+ remaining_keys.delete('discriminator')
23
+ end
24
+
15
25
  coerced_values = value.map do |name, v|
16
26
  s = properties[name]
17
27
  coerced, err = if s
@@ -30,6 +30,9 @@ class OpenAPIParser::SchemaValidator
30
30
  value, err = validate_uuid_format(value, schema)
31
31
  return [nil, err] if err
32
32
 
33
+ value, err = validate_date_format(value, schema)
34
+ return [nil, err] if err
35
+
33
36
  [value, nil]
34
37
  end
35
38
 
@@ -86,5 +89,17 @@ class OpenAPIParser::SchemaValidator
86
89
 
87
90
  return [nil, OpenAPIParser::InvalidUUIDFormat.new(value, schema.object_reference)]
88
91
  end
92
+
93
+ def validate_date_format(value, schema)
94
+ return [value, nil] unless schema.format == 'date'
95
+
96
+ begin
97
+ Date.strptime(value, "%Y-%m-%d")
98
+ rescue ArgumentError
99
+ return [nil, OpenAPIParser::InvalidDateFormat.new(value, schema.object_reference)]
100
+ end
101
+
102
+ return [value, nil]
103
+ end
89
104
  end
90
105
  end
@@ -0,0 +1,8 @@
1
+ class OpenAPIParser::SchemaValidator
2
+ class UnspecifiedTypeValidator < Base
3
+ # @param [Object] value
4
+ def coerce_and_validate(value, _schema, **_keyword_args)
5
+ value
6
+ end
7
+ end
8
+ end
@@ -3,8 +3,6 @@
3
3
 
4
4
  module OpenAPIParser::Schemas
5
5
  class PathItem < Base
6
- include OpenAPIParser::ParameterValidatable
7
-
8
6
  openapi_attr_values :summary, :description
9
7
 
10
8
  openapi_attr_objects :get, :put, :post, :delete, :options, :head, :patch, :trace, Operation
@@ -16,5 +14,9 @@ module OpenAPIParser::Schemas
16
14
  rescue NoMethodError
17
15
  nil
18
16
  end
17
+
18
+ def set_path_item_to_operation
19
+ [:get, :put, :post, :delete, :options, :head, :patch, :trace].each{ |method| operation(method)&.set_parent_path_item(self)}
20
+ end
19
21
  end
20
22
  end
@@ -1,3 +1,3 @@
1
1
  module OpenAPIParser
2
- VERSION = '0.13.0'.freeze
2
+ VERSION = '1.0.0.beta1'.freeze
3
3
  end
@@ -44,9 +44,9 @@ module OpenAPIParser
44
44
  # Open-uri doesn't open file scheme uri, so we try to open file path directly
45
45
  # File scheme uri which points to a remote file is not supported.
46
46
  content = if uri.scheme == 'file'
47
- open(uri.path, &:read)
48
- else
49
- uri.open(&:read)
47
+ open(uri.path)&.read
48
+ elsif uri.is_a?(OpenURI::OpenRead)
49
+ uri.open()&.read
50
50
  end
51
51
 
52
52
  extension = Pathname.new(uri.path).extname
@@ -61,8 +61,8 @@ module OpenAPIParser
61
61
  URI.join("file:///", path.to_s)
62
62
  end
63
63
 
64
- def parse_file(content, extension)
65
- case extension.downcase
64
+ def parse_file(content, ext)
65
+ case ext.downcase
66
66
  when '.yaml', '.yml'
67
67
  parse_yaml(content)
68
68
  when '.json'
@@ -91,7 +91,12 @@ module OpenAPIParser
91
91
  def load_hash(hash, config:, uri:, schema_registry:)
92
92
  root = Schemas::OpenAPI.new(hash, config, uri: uri, schema_registry: schema_registry)
93
93
 
94
- OpenAPIParser::ReferenceExpander.expand(root) if config.expand_reference
94
+ OpenAPIParser::ReferenceExpander.expand(root, config.strict_reference_validation) if config.expand_reference
95
+
96
+ # TODO: use callbacks
97
+ root.paths&.path&.values&.each do | path_item |
98
+ path_item.set_path_item_to_operation
99
+ end
95
100
 
96
101
  root
97
102
  end
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'parser for OpenAPI 3.0 or later'
13
13
  spec.homepage = 'https://github.com/ota42y/openapi_parser'
14
14
  spec.license = 'MIT'
15
+ spec.required_ruby_version = ">= 2.6.0"
15
16
 
16
17
  # Specify which files should be added to the gem when it is released.
17
18
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -30,4 +31,5 @@ Gem::Specification.new do |spec|
30
31
  spec.add_development_dependency 'rspec', '~> 3.0'
31
32
  spec.add_development_dependency 'rspec-parameterized'
32
33
  spec.add_development_dependency 'simplecov'
34
+ spec.add_development_dependency "steep"
33
35
  end
@@ -0,0 +1,17 @@
1
+ # Classes
2
+ module OpenAPIParser
3
+ class Config
4
+ @config: untyped
5
+ alias request_body_options request_validator_options
6
+ alias path_params_options request_validator_options
7
+
8
+ def initialize: (untyped config) -> untyped
9
+ def datetime_coerce_class: -> (singleton(Object) | nil)
10
+ def coerce_value: -> bool
11
+ def expand_reference: -> bool
12
+ def strict_response_validation: -> bool
13
+ def validate_header: -> bool
14
+ def request_validator_options: -> OpenAPIParser::SchemaValidator::Options
15
+ def response_validate_options: -> OpenAPIParser::SchemaValidator::ResponseValidateOptions
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # Classes
2
+ module OpenAPIParser
3
+ class SchemaValidator
4
+ include Validatable
5
+ @value: Hash[bot, bot]
6
+ @schema: OpenAPIParser::Schemas::Schema
7
+ @coerce_value: bool | nil
8
+ @datetime_coerce_class: singleton(Object) | nil
9
+ @string_validator: OpenAPIParser::SchemaValidator::StringValidator | nil
10
+ @integer_validator: OpenAPIParser::SchemaValidator::IntegerValidator | nil
11
+ @float_validator: OpenAPIParser::SchemaValidator::FloatValidator | nil
12
+ @boolean_validator: OpenAPIParser::SchemaValidator::BooleanValidator | nil
13
+ @object_validator: OpenAPIParser::SchemaValidator::ObjectValidator | nil
14
+ @array_validator: OpenAPIParser::SchemaValidator::ArrayValidator | nil
15
+ @any_of_validator: OpenAPIParser::SchemaValidator::AnyOfValidator | nil
16
+ @all_of_validator: OpenAPIParser::SchemaValidator::AllOfValidator | nil
17
+ @one_of_validator: OpenAPIParser::SchemaValidator::OneOfValidator | nil
18
+ @nil_validator: OpenAPIParser::SchemaValidator::NilValidator | nil
19
+ @unspecified_type_validator: OpenAPIParser::SchemaValidator::UnspecifiedTypeValidator | nil
20
+
21
+ def self.validate: (Hash[bot, bot] value, OpenAPIParser::Schemas::Schema schema, OpenAPIParser::SchemaValidator::Options options) -> Object
22
+ def initialize: (Hash[bot, bot] value, OpenAPIParser::Schemas::Schema schema, OpenAPIParser::SchemaValidator::Options options) -> untyped
23
+ def validate_data: -> Object
24
+ def validate_schema: (Object value, OpenAPIParser::Schemas::Schema schema, **bot) -> [Object, OpenAPIParser::validate_error]
25
+ def validate_integer: (Object value, OpenAPIParser::Schemas::Schema schema) -> [Object, OpenAPIParser::validate_error]
26
+
27
+ private
28
+ def validator: (Object value, OpenAPIParser::Schemas::Schema schema) -> [OpenAPIParser::SchemaValidator::Base, OpenAPIParser::validate_error]
29
+ def string_validator: -> OpenAPIParser::SchemaValidator::StringValidator
30
+ def integer_validator: -> OpenAPIParser::SchemaValidator::IntegerValidator
31
+ def float_validator: -> OpenAPIParser::SchemaValidator::FloatValidator
32
+ def boolean_validator: -> OpenAPIParser::SchemaValidator::BooleanValidator
33
+ def object_validator: -> OpenAPIParser::SchemaValidator::ObjectValidator
34
+ def array_validator: -> OpenAPIParser::SchemaValidator::ArrayValidator
35
+ def any_of_validator: -> OpenAPIParser::SchemaValidator::AnyOfValidator
36
+ def all_of_validator: -> OpenAPIParser::SchemaValidator::AllOfValidator
37
+ def one_of_validator: -> OpenAPIParser::SchemaValidator::OneOfValidator
38
+ def nil_validator: -> OpenAPIParser::SchemaValidator::NilValidator
39
+ def unspecified_type_validator: -> OpenAPIParser::SchemaValidator::UnspecifiedTypeValidator
40
+
41
+ module Validatable
42
+ def validate_schema: (Object value, OpenAPIParser::Schemas::Schema schema, **untyped) -> [Object, OpenAPIParser::validate_error]
43
+ def validate_integer: (Object _value, OpenAPIParser::Schemas::Schema _schema) -> [Object, OpenAPIParser::validate_error]
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,17 @@
1
+ # Classes
2
+ module OpenAPIParser
3
+ class SchemaValidator
4
+ class Base
5
+ @coerce_value: bool | nil
6
+
7
+ def initialize: (OpenAPIParser::SchemaValidator::Validatable validatable, (bool | nil) coerce_value) -> untyped
8
+ attr_reader validatable: OpenAPIParser::SchemaValidator::Validatable
9
+ def coerce_and_validate: (Object _value, OpenAPIParser::Schemas::Schema _schema, **untyped) -> bot
10
+ def validate_discriminator_schema: (
11
+ OpenAPIParser::Schemas::Discriminator discriminator,
12
+ Hash[String, bot] value,
13
+ ?parent_discriminator_schemas: Array[OpenAPIParser::Schemas::Schema]
14
+ ) -> [Object | nil, OpenAPIParser::validate_error]
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Classes
2
+ module OpenAPIParser
3
+ class SchemaValidator
4
+ class Options
5
+ attr_reader coerce_value: bool | nil
6
+ attr_reader datetime_coerce_class: singleton(Object) | nil
7
+ attr_reader validate_header: bool
8
+ def initialize: (?coerce_value: bool | nil, ?datetime_coerce_class: singleton(Object) | nil, ?validate_header: bool) -> untyped
9
+ end
10
+
11
+ class ResponseValidateOptions
12
+ attr_reader strict: bool
13
+ attr_reader validate_header: bool
14
+ def initialize: (?strict: bool, ?validate_header: bool) -> untyped
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Classes
2
+ module OpenAPIParser
3
+ module Schemas
4
+ class Base
5
+ include OpenAPIParser::Expandable
6
+ include OpenAPIParser::Findable
7
+
8
+ attr_reader parent: OpenAPIParser::Schemas::Base | nil
9
+ attr_reader raw_schema: Hash[String, bot]
10
+ attr_reader object_reference: String
11
+ attr_reader root: OpenAPIParser::Schemas::OpenAPI
12
+ def initialize: (String object_reference, OpenAPIParser::Schemas::Base | nil parent, OpenAPIParser::Schemas::OpenAPI root, Hash[String, bot] raw_schema) -> nil
13
+ def after_init: -> nil
14
+ def inspect: -> String
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module OpenAPIParser
2
+ VERSION: String
3
+ end
@@ -0,0 +1,19 @@
1
+ module OpenAPIParser
2
+ def self.parse: (Hash[bot, bot] schema, ?Hash[bot, bot] config) -> OpenAPIParser::Schemas::OpenAPI
3
+ def self.parse_with_filepath: (Hash[bot, bot] schema, String filepath, ?Hash[bot, bot] config) -> OpenAPIParser::Schemas::OpenAPI
4
+ def self.load: (String filepath, ?Hash[bot, bot] config) -> OpenAPIParser::Schemas::OpenAPI
5
+ def self.load_uri: (OpenAPIParser::readable_uri uri, config: untyped, schema_registry: Hash[bot, bot]) -> OpenAPIParser::Schemas::OpenAPI
6
+ def self.file_uri: (String filepath) -> URI::Generic
7
+ def self.parse_file: (String? content, String ext) -> Hash[bot, bot]
8
+ def self.parse_yaml: (String? content) -> Hash[bot, bot]
9
+ def self.parse_json: (String? content) -> Hash[bot, bot]
10
+ def self.load_hash: (Hash[bot, bot] hash, config: untyped, uri: OpenAPIParser::readable_uri?, schema_registry: Hash[bot, bot]) -> OpenAPIParser::Schemas::OpenAPI
11
+ end
12
+
13
+ module OpenAPIParser
14
+ module Schemas
15
+ class OpenAPI
16
+ def initialize: (Hash[bot, bot] hash, untyped config, uri: OpenAPIParser::readable_uri?, schema_registry: Hash[bot, bot]) -> OpenAPIParser::Schemas::OpenAPI
17
+ end
18
+ end
19
+ end
data/sig/types.rbs ADDED
@@ -0,0 +1,8 @@
1
+ module OpenURI
2
+ module OpenRead
3
+ def open: () -> (IO | nil)
4
+ def path: () -> String
5
+ def scheme: () -> String
6
+ end
7
+ end
8
+
data/sig/wip_types.rbs ADDED
@@ -0,0 +1,67 @@
1
+ module OpenAPIParser
2
+ type readable_uri = URI::Generic | OpenURI::OpenRead
3
+ type validate_error = nil
4
+ end
5
+
6
+ module OpenAPIParser
7
+ module Schemas
8
+ class Schema
9
+ end
10
+ end
11
+ end
12
+
13
+ class OpenAPIParser::SchemaValidator::Base
14
+ end
15
+
16
+ class OpenAPIParser::SchemaValidator::StringValidator
17
+ end
18
+
19
+ class OpenAPIParser::SchemaValidator::IntegerValidator
20
+ end
21
+
22
+ class OpenAPIParser::SchemaValidator::FloatValidator
23
+ end
24
+
25
+ class OpenAPIParser::SchemaValidator::BooleanValidator
26
+ end
27
+
28
+ class OpenAPIParser::SchemaValidator::ObjectValidator
29
+ end
30
+
31
+ class OpenAPIParser::SchemaValidator::ArrayValidator
32
+ end
33
+
34
+ class OpenAPIParser::SchemaValidator::AnyOfValidator
35
+ end
36
+
37
+ class OpenAPIParser::SchemaValidator::AllOfValidator
38
+ end
39
+
40
+ class OpenAPIParser::SchemaValidator::OneOfValidator
41
+ end
42
+
43
+ class OpenAPIParser::SchemaValidator::NilValidator
44
+ end
45
+
46
+ class OpenAPIParser::SchemaValidator::UnspecifiedTypeValidator
47
+ end
48
+
49
+ class OpenAPIParser::Schemas::OpenAPI < OpenAPIParser::Schemas::Base
50
+ attr_reader paths: untyped
51
+ end
52
+
53
+ module OpenAPIParser::Expandable
54
+ def expand_reference: (OpenAPIParser::Schemas::OpenAPI root) -> nil
55
+ end
56
+
57
+ module OpenAPIParser::Findable
58
+ def find_object: (String reference) -> ::OpenAPIParser::Schemas::Schema
59
+ end
60
+
61
+ class OpenAPIParser::Schemas::Discriminator < OpenAPIParser::Schemas::Base
62
+ attr_reader property_name: (String | nil)
63
+ attr_reader mapping: Hash[String, String]
64
+ end
65
+
66
+ class OpenAPIParser::OpenAPIError
67
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 1.0.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ota42y
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-02 00:00:00.000000000 Z
11
+ date: 2021-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: steep
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  description: parser for OpenAPI 3.0 or later
126
140
  email:
127
141
  - ota42y@gmail.com
@@ -129,17 +143,19 @@ executables: []
129
143
  extensions: []
130
144
  extra_rdoc_files: []
131
145
  files:
146
+ - ".github/workflows/ci.yaml"
132
147
  - ".gitignore"
133
148
  - ".rspec"
134
149
  - ".rubocop.yml"
135
150
  - ".rubocop_ignore.yml"
136
- - ".travis.yml"
151
+ - ".ruby-version"
137
152
  - CHANGELOG.md
138
153
  - CODE_OF_CONDUCT.md
139
154
  - Gemfile
140
155
  - LICENSE.txt
141
156
  - README.md
142
157
  - Rakefile
158
+ - Steepfile
143
159
  - bin/console
144
160
  - bin/setup
145
161
  - lib/openapi_parser.rb
@@ -184,6 +200,7 @@ files:
184
200
  - lib/openapi_parser/schema_validators/one_of_validator.rb
185
201
  - lib/openapi_parser/schema_validators/options.rb
186
202
  - lib/openapi_parser/schema_validators/string_validator.rb
203
+ - lib/openapi_parser/schema_validators/unspecified_type_validator.rb
187
204
  - lib/openapi_parser/schemas.rb
188
205
  - lib/openapi_parser/schemas/base.rb
189
206
  - lib/openapi_parser/schemas/classes.rb
@@ -203,6 +220,15 @@ files:
203
220
  - lib/openapi_parser/schemas/schema.rb
204
221
  - lib/openapi_parser/version.rb
205
222
  - openapi_parser.gemspec
223
+ - sig/openapi_parser.rbs
224
+ - sig/openapi_parser/config.rbs
225
+ - sig/openapi_parser/schema_validator.rbs
226
+ - sig/openapi_parser/schema_validators/base.rbs
227
+ - sig/openapi_parser/schema_validators/options.rbs
228
+ - sig/openapi_parser/schemas/base.rbs
229
+ - sig/openapi_parser/version.rbs
230
+ - sig/types.rbs
231
+ - sig/wip_types.rbs
206
232
  homepage: https://github.com/ota42y/openapi_parser
207
233
  licenses:
208
234
  - MIT
@@ -215,14 +241,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
215
241
  requirements:
216
242
  - - ">="
217
243
  - !ruby/object:Gem::Version
218
- version: '0'
244
+ version: 2.6.0
219
245
  required_rubygems_version: !ruby/object:Gem::Requirement
220
246
  requirements:
221
- - - ">="
247
+ - - ">"
222
248
  - !ruby/object:Gem::Version
223
- version: '0'
249
+ version: 1.3.1
224
250
  requirements: []
225
- rubygems_version: 3.2.3
251
+ rubygems_version: 3.2.32
226
252
  signing_key:
227
253
  specification_version: 4
228
254
  summary: OpenAPI3 parser
data/.travis.yml DELETED
@@ -1,38 +0,0 @@
1
- env:
2
- global:
3
- - CC_TEST_REPORTER_ID=b49a1717b8ff0aef9eced41d0f87d350a88b46d55083ba2e3df8b6f441ae3fb7
4
-
5
- language: ruby
6
-
7
- rvm:
8
- - 2.3.8
9
- - 2.4.10
10
- - 2.5.9
11
- - 2.6.7
12
- - 2.7.3
13
- - 3.0.1
14
- - ruby-head
15
-
16
- cache: bundler
17
-
18
- before_script:
19
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
20
- - chmod +x ./cc-test-reporter
21
- - ./cc-test-reporter before-build
22
-
23
- script: bundle exec rspec
24
-
25
- after_script:
26
- - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
27
-
28
- notifications:
29
- email: false
30
-
31
- sudo: false
32
-
33
- git:
34
- depth: 10
35
-
36
- matrix:
37
- allow_failures:
38
- - rvm: ruby-head