openapi_contracts 0.8.0 → 0.9.1

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -20
  3. data/lib/openapi_contracts/doc/operation.rb +27 -0
  4. data/lib/openapi_contracts/doc/parameter.rb +22 -59
  5. data/lib/openapi_contracts/doc/path.rb +23 -38
  6. data/lib/openapi_contracts/doc/pointer.rb +81 -0
  7. data/lib/openapi_contracts/doc/request.rb +17 -0
  8. data/lib/openapi_contracts/doc/response.rb +5 -5
  9. data/lib/openapi_contracts/doc/schema.rb +49 -10
  10. data/lib/openapi_contracts/doc/with_parameters.rb +9 -0
  11. data/lib/openapi_contracts/doc.rb +17 -20
  12. data/lib/openapi_contracts/match.rb +34 -10
  13. data/lib/openapi_contracts/operation_router.rb +33 -0
  14. data/lib/openapi_contracts/parser/transformers/base.rb +15 -0
  15. data/lib/openapi_contracts/parser/transformers/nullable.rb +10 -0
  16. data/lib/openapi_contracts/parser/transformers/pointer.rb +34 -0
  17. data/lib/openapi_contracts/parser/transformers.rb +5 -0
  18. data/lib/openapi_contracts/parser.rb +61 -0
  19. data/lib/openapi_contracts/payload_parser.rb +39 -0
  20. data/lib/openapi_contracts/rspec.rb +2 -2
  21. data/lib/openapi_contracts/validators/base.rb +5 -1
  22. data/lib/openapi_contracts/validators/documented.rb +12 -5
  23. data/lib/openapi_contracts/validators/headers.rb +4 -0
  24. data/lib/openapi_contracts/validators/http_status.rb +2 -6
  25. data/lib/openapi_contracts/validators/request_body.rb +26 -0
  26. data/lib/openapi_contracts/validators/response_body.rb +28 -0
  27. data/lib/openapi_contracts/validators/schema_validation.rb +40 -0
  28. data/lib/openapi_contracts/validators.rb +9 -6
  29. data/lib/openapi_contracts.rb +10 -5
  30. metadata +32 -22
  31. data/lib/openapi_contracts/doc/file_parser.rb +0 -85
  32. data/lib/openapi_contracts/doc/method.rb +0 -18
  33. data/lib/openapi_contracts/doc/parser.rb +0 -44
  34. data/lib/openapi_contracts/validators/body.rb +0 -38
@@ -1,11 +1,18 @@
1
1
  module OpenapiContracts
2
2
  class Match
3
+ DEFAULT_OPTIONS = {request_body: false}.freeze
4
+ MIN_REQUEST_ANCESTORS = %w(Rack::Request::Env Rack::Request::Helpers).freeze
5
+ MIN_RESPONSE_ANCESTORS = %w(Rack::Response::Helpers).freeze
6
+
3
7
  attr_reader :errors
4
8
 
5
9
  def initialize(doc, response, options = {})
6
10
  @doc = doc
7
11
  @response = response
8
- @options = options
12
+ @request = options.delete(:request) { response.request }
13
+ @options = DEFAULT_OPTIONS.merge(options)
14
+ raise ArgumentError, "#{@response} must be compatible with Rack::Response::Helpers" unless response_compatible?
15
+ raise ArgumentError, "#{@request} must be compatible with Rack::Request::{Env,Helpers}" unless request_compatible?
9
16
  end
10
17
 
11
18
  def valid?
@@ -17,18 +24,35 @@ module OpenapiContracts
17
24
 
18
25
  private
19
26
 
20
- def lookup_api_spec
21
- @doc.response_for(
22
- @options.fetch(:path, @response.request.path),
23
- @response.request.request_method.downcase,
24
- @response.status.to_s
27
+ def matchers
28
+ env = Env.new(
29
+ options: @options,
30
+ operation: operation,
31
+ request: @request,
32
+ response: @response
25
33
  )
34
+ validators = Validators::ALL.dup
35
+ validators.delete(Validators::HttpStatus) unless @options[:status]
36
+ validators.delete(Validators::RequestBody) unless @options[:request_body]
37
+ validators.reverse
38
+ .reduce(->(err) { err }) { |s, m| m.new(s, env) }
26
39
  end
27
40
 
28
- def matchers
29
- env = Env.new(lookup_api_spec, @response, @options[:status])
30
- Validators::ALL.reverse
31
- .reduce(->(err) { err }) { |s, m| m.new(s, env) }
41
+ def operation
42
+ @doc.operation_for(
43
+ @options.fetch(:path, @request.path),
44
+ @request.request_method.downcase
45
+ )
46
+ end
47
+
48
+ def request_compatible?
49
+ ancestors = @request.class.ancestors.map(&:to_s)
50
+ MIN_REQUEST_ANCESTORS.all? { |s| ancestors.include?(s) }
51
+ end
52
+
53
+ def response_compatible?
54
+ ancestors = @response.class.ancestors.map(&:to_s)
55
+ MIN_RESPONSE_ANCESTORS.all? { |s| ancestors.include?(s) }
32
56
  end
33
57
  end
34
58
  end
@@ -0,0 +1,33 @@
1
+ module OpenapiContracts
2
+ class OperationRouter
3
+ def initialize(doc)
4
+ @doc = doc
5
+ @dynamic_paths = doc.paths.select(&:dynamic?)
6
+ end
7
+
8
+ def route(actual_path, method)
9
+ @doc.with_path(actual_path)&.then { |p| return p.with_method(method) }
10
+
11
+ @dynamic_paths.each do |path|
12
+ next unless path.supports_method?(method)
13
+ next unless m = path.path_regexp.match(actual_path)
14
+
15
+ operation = path.with_method(method)
16
+ parameters = (path.parameters + operation.parameters).select(&:in_path?)
17
+
18
+ return operation if parameter_match?(m.named_captures, parameters)
19
+ end
20
+
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ def parameter_match?(actual_params, parameters)
27
+ actual_params.each do |k, v|
28
+ return false unless parameters&.find { |s| s.name == k }&.matches?(v)
29
+ end
30
+ true
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ module OpenapiContracts::Parser::Transformers
2
+ class Base
3
+ def initialize(parser, cwd, pointer)
4
+ @parser = parser
5
+ @cwd = cwd
6
+ @pointer = pointer
7
+ end
8
+
9
+ # :nocov:
10
+ def call
11
+ raise NotImplementedError
12
+ end
13
+ # :nocov:
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ module OpenapiContracts::Parser::Transformers
2
+ class Nullable < Base
3
+ def call(object)
4
+ return unless object['type'].present? && object['nullable'] == true
5
+
6
+ object.delete('nullable')
7
+ object['type'] = [object['type'], 'null']
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ module OpenapiContracts::Parser::Transformers
2
+ class Pointer < Base
3
+ def call(object)
4
+ return unless object['$ref'].present?
5
+
6
+ object['$ref'] = transform_pointer(object['$ref'])
7
+ end
8
+
9
+ private
10
+
11
+ def transform_pointer(target)
12
+ if %r{^#/(?<pointer>.*)} =~ target
13
+ # A JSON Pointer
14
+ generate_absolute_pointer(pointer)
15
+ elsif %r{^(?<relpath>[^#]+)(?:#/(?<pointer>.*))?} =~ target
16
+ ptr = @parser.filenesting[@cwd.join(relpath)]
17
+ tgt = ptr.to_json_pointer
18
+ tgt += "/#{pointer}" if pointer
19
+ tgt
20
+ else
21
+ target
22
+ end
23
+ end
24
+
25
+ # A JSON pointer to the currently parsed file as seen from the root openapi file
26
+ def generate_absolute_pointer(json_pointer)
27
+ if @pointer.empty?
28
+ "#/#{json_pointer}"
29
+ else
30
+ "#{@pointer.to_json_pointer}/#{json_pointer}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ module OpenapiContracts::Parser::Transformers
2
+ autoload :Base, 'openapi_contracts/parser/transformers/base'
3
+ autoload :Nullable, 'openapi_contracts/parser/transformers/nullable'
4
+ autoload :Pointer, 'openapi_contracts/parser/transformers/pointer'
5
+ end
@@ -0,0 +1,61 @@
1
+ module OpenapiContracts
2
+ class Parser
3
+ autoload :Transformers, 'openapi_contracts/parser/transformers'
4
+
5
+ TRANSFORMERS = [Transformers::Nullable, Transformers::Pointer].freeze
6
+
7
+ def self.call(dir, filename)
8
+ new(dir.join(filename)).parse
9
+ end
10
+
11
+ attr_reader :filenesting, :rootfile
12
+
13
+ def initialize(rootfile)
14
+ @cwd = rootfile.parent
15
+ @rootfile = rootfile
16
+ @filenesting = {}
17
+ end
18
+
19
+ def parse
20
+ @filenesting = build_file_list
21
+ @filenesting.each_with_object({}) do |(path, pointer), schema|
22
+ target = pointer.to_a.reduce(schema) { |d, k| d[k] ||= {} }
23
+ target.delete('$ref') # ref file pointers must be replaced
24
+ target.merge! file_to_data(path, pointer)
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def build_file_list
31
+ list = {@rootfile.relative_path_from(@cwd) => Doc::Pointer[]}
32
+ Dir[File.expand_path('components/**/*.yaml', @cwd)].each do |file|
33
+ pathname = Pathname(file).relative_path_from(@cwd)
34
+ pointer = Doc::Pointer.from_path pathname.sub_ext('')
35
+ list.merge! pathname => pointer
36
+ end
37
+ YAML.safe_load_file(@rootfile).fetch('paths') { {} }.each_pair do |k, v|
38
+ next unless v['$ref'] && !v['$ref'].start_with?('#')
39
+
40
+ list.merge! Pathname(v['$ref']) => Doc::Pointer['paths', k]
41
+ end
42
+ list
43
+ end
44
+
45
+ def file_to_data(pathname, pointer)
46
+ YAML.safe_load_file(@cwd.join(pathname)).tap do |data|
47
+ transform_objects!(data, pathname.parent, pointer)
48
+ end
49
+ end
50
+
51
+ def transform_objects!(object, cwd, pointer)
52
+ case object
53
+ when Hash
54
+ object.each_value { |v| transform_objects!(v, cwd, pointer) }
55
+ TRANSFORMERS.map { |t| t.new(self, cwd, pointer) }.each { |t| t.call(object) }
56
+ when Array
57
+ object.each { |o| transform_objects!(o, cwd, pointer) }
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ require 'singleton'
2
+
3
+ module OpenapiContracts
4
+ class PayloadParser
5
+ include Singleton
6
+
7
+ class << self
8
+ delegate :parse, :register, to: :instance
9
+ end
10
+
11
+ Entry = Struct.new(:matcher, :parser) do
12
+ def call(raw)
13
+ parser.call(raw)
14
+ end
15
+
16
+ def match?(media_type)
17
+ matcher == media_type || matcher.match?(media_type)
18
+ end
19
+ end
20
+
21
+ def initialize
22
+ @parsers = []
23
+ end
24
+
25
+ def parse(media_type, payload)
26
+ parser = @parsers.find { |e| e.match?(media_type) }
27
+ raise ArgumentError, "#{media_type.inspect} is not supported yet" unless parser
28
+
29
+ parser.call(payload)
30
+ end
31
+
32
+ def register(matcher, parser)
33
+ @parsers << Entry.new(matcher, parser)
34
+ end
35
+ end
36
+
37
+ PayloadParser.register(%r{(/|\+)json$}, ->(raw) { JSON(raw) })
38
+ PayloadParser.register('application/x-www-form-urlencoded', ->(raw) { Rack::Utils.parse_nested_query(raw) })
39
+ end
@@ -14,12 +14,12 @@ RSpec::Matchers.define :match_openapi_doc do |doc, options = {}| # rubocop:disab
14
14
  end
15
15
 
16
16
  description do
17
- desc = 'to match the openapi schema'
17
+ desc = 'match the openapi schema'
18
18
  desc << " with #{http_status_desc(@status)}" if @status
19
19
  desc
20
20
  end
21
21
 
22
- failure_message do |_reponse|
22
+ failure_message do |_response|
23
23
  @errors.map { |e| "* #{e}" }.join("\n")
24
24
  end
25
25
 
@@ -22,7 +22,11 @@ module OpenapiContracts::Validators
22
22
 
23
23
  private
24
24
 
25
- delegate :expected_status, :response, :spec, to: :@env
25
+ delegate :operation, :options, :request, :response, to: :@env
26
+
27
+ def response_desc
28
+ "#{request.request_method} #{request.path}"
29
+ end
26
30
 
27
31
  # :nocov:
28
32
  def validate
@@ -1,18 +1,25 @@
1
1
  module OpenapiContracts::Validators
2
+ # Purpose of this validator
3
+ # * ensure the operation is documented (combination http-method + path)
4
+ # * ensure the response-status is documented on the operation
2
5
  class Documented < Base
3
6
  self.final = true
4
7
 
5
8
  private
6
9
 
7
10
  def validate
8
- return if spec
11
+ return operation_missing unless operation
9
12
 
10
- status_desc = http_status_desc(response.status)
11
- @errors << "Undocumented request/response for #{response_desc.inspect} with #{status_desc}"
13
+ response_missing unless operation.response_for_status(response.status)
14
+ end
15
+
16
+ def operation_missing
17
+ @errors << "Undocumented operation for #{response_desc.inspect}"
12
18
  end
13
19
 
14
- def response_desc
15
- "#{response.request.request_method} #{response.request.path}"
20
+ def response_missing
21
+ status_desc = http_status_desc(response.status)
22
+ @errors << "Undocumented response for #{response_desc.inspect} with #{status_desc}"
16
23
  end
17
24
  end
18
25
  end
@@ -2,6 +2,10 @@ module OpenapiContracts::Validators
2
2
  class Headers < Base
3
3
  private
4
4
 
5
+ def spec
6
+ @spec ||= operation.response_for_status(response.status)
7
+ end
8
+
5
9
  def validate
6
10
  spec.headers.each do |header|
7
11
  value = response.headers[header.name]
@@ -5,13 +5,9 @@ module OpenapiContracts::Validators
5
5
  private
6
6
 
7
7
  def validate
8
- return if expected_status.blank? || expected_status == response.status
8
+ return if options[:status] == response.status
9
9
 
10
- @errors << "Response has #{http_status_desc}"
11
- end
12
-
13
- def http_status_desc
14
- "http status #{Rack::Utils::HTTP_STATUS_CODES[response.status]} (#{response.status})"
10
+ @errors << "Response has #{http_status_desc(response.status)}"
15
11
  end
16
12
  end
17
13
  end
@@ -0,0 +1,26 @@
1
+ module OpenapiContracts::Validators
2
+ class RequestBody < Base
3
+ include SchemaValidation
4
+
5
+ private
6
+
7
+ delegate :media_type, to: :request
8
+ delegate :request_body, to: :operation
9
+
10
+ def data_for_validation
11
+ request.body.rewind
12
+ raw = request.body.read
13
+ OpenapiContracts::PayloadParser.parse(media_type, raw)
14
+ end
15
+
16
+ def validate
17
+ if !request_body
18
+ @errors << "Undocumented request body for #{response_desc.inspect}"
19
+ elsif !request_body.supports_media_type?(media_type)
20
+ @errors << "Undocumented request with media-type #{media_type.inspect}"
21
+ else
22
+ @errors += validate_schema(request_body.schema_for(media_type), data_for_validation)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ module OpenapiContracts::Validators
2
+ class ResponseBody < Base
3
+ include SchemaValidation
4
+
5
+ private
6
+
7
+ delegate :media_type, to: :response
8
+
9
+ def data_for_validation
10
+ # ActionDispatch::Response body is a plain string, while Rack::Response returns an array
11
+ OpenapiContracts::PayloadParser.parse(media_type, Array.wrap(response.body).join)
12
+ end
13
+
14
+ def spec
15
+ @spec ||= operation.response_for_status(response.status)
16
+ end
17
+
18
+ def validate
19
+ if spec.no_content?
20
+ @errors << 'Expected empty response body' if Array.wrap(response.body).any?(&:present?)
21
+ elsif !spec.supports_media_type?(media_type)
22
+ @errors << "Undocumented response with content-type #{media_type.inspect}"
23
+ else
24
+ @errors += validate_schema(spec.schema_for(media_type), data_for_validation)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ module OpenapiContracts::Validators
2
+ module SchemaValidation
3
+ module_function
4
+
5
+ def build_validation_schema(schema)
6
+ schema.raw.merge(
7
+ '$ref' => schema.fragment,
8
+ '$schema' => schema_draft_version(schema)
9
+ )
10
+ end
11
+
12
+ def error_to_message(error)
13
+ pointer = " at #{error['data_pointer']}" if error['data_pointer'].present?
14
+ if error.key?('details')
15
+ error['details'].to_a.map { |(key, val)|
16
+ "#{key.humanize}: #{val}#{pointer}"
17
+ }.to_sentence
18
+ else
19
+ "#{error['data'].inspect}#{pointer} does not match the schema"
20
+ end
21
+ end
22
+
23
+ def schema_draft_version(schema)
24
+ if schema.openapi_version.blank? || schema.openapi_version < Gem::Version.new('3.1')
25
+ # Closest compatible version is actually draft 5 but not supported by json-schemer
26
+ 'http://json-schema.org/draft-04/schema#'
27
+ else
28
+ # >= 3.1 is actually comptable with 2020-12 but not yet supported by json-schemer
29
+ 'http://json-schema.org/draft-07/schema#'
30
+ end
31
+ end
32
+
33
+ def validate_schema(schema, data)
34
+ schemer = JSONSchemer.schema(build_validation_schema(schema))
35
+ schemer.validate(data).map do |err|
36
+ error_to_message(err)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,16 +1,19 @@
1
1
  module OpenapiContracts
2
2
  module Validators
3
- autoload :Base, 'openapi_contracts/validators/base'
4
- autoload :Body, 'openapi_contracts/validators/body'
5
- autoload :Documented, 'openapi_contracts/validators/documented'
6
- autoload :Headers, 'openapi_contracts/validators/headers'
7
- autoload :HttpStatus, 'openapi_contracts/validators/http_status'
3
+ autoload :Base, 'openapi_contracts/validators/base'
4
+ autoload :Documented, 'openapi_contracts/validators/documented'
5
+ autoload :Headers, 'openapi_contracts/validators/headers'
6
+ autoload :HttpStatus, 'openapi_contracts/validators/http_status'
7
+ autoload :RequestBody, 'openapi_contracts/validators/request_body'
8
+ autoload :ResponseBody, 'openapi_contracts/validators/response_body'
9
+ autoload :SchemaValidation, 'openapi_contracts/validators/schema_validation'
8
10
 
9
11
  # Defines order of matching
10
12
  ALL = [
11
13
  Documented,
12
14
  HttpStatus,
13
- Body,
15
+ RequestBody,
16
+ ResponseBody,
14
17
  Headers
15
18
  ].freeze
16
19
  end
@@ -4,17 +4,22 @@ require 'active_support/core_ext/hash'
4
4
  require 'active_support/core_ext/class'
5
5
  require 'active_support/core_ext/module'
6
6
  require 'active_support/core_ext/string'
7
+ require 'rubygems/version'
7
8
 
8
9
  require 'json_schemer'
10
+ require 'rack'
9
11
  require 'yaml'
10
12
 
11
13
  module OpenapiContracts
12
- autoload :Doc, 'openapi_contracts/doc'
13
- autoload :Helper, 'openapi_contracts/helper'
14
- autoload :Match, 'openapi_contracts/match'
15
- autoload :Validators, 'openapi_contracts/validators'
14
+ autoload :Doc, 'openapi_contracts/doc'
15
+ autoload :Helper, 'openapi_contracts/helper'
16
+ autoload :Match, 'openapi_contracts/match'
17
+ autoload :OperationRouter, 'openapi_contracts/operation_router'
18
+ autoload :Parser, 'openapi_contracts/parser'
19
+ autoload :PayloadParser, 'openapi_contracts/payload_parser'
20
+ autoload :Validators, 'openapi_contracts/validators'
16
21
 
17
- Env = Struct.new(:spec, :response, :expected_status)
22
+ Env = Struct.new(:operation, :options, :request, :response, keyword_init: true)
18
23
 
19
24
  module_function
20
25
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_contracts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - mkon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-07 00:00:00.000000000 Z
11
+ date: 2023-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -36,42 +36,42 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 0.2.20
39
+ version: 1.0.3
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 0.2.20
46
+ version: 1.0.3
47
47
  - !ruby/object:Gem::Dependency
48
- name: json_spec
48
+ name: rack
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - "~>"
51
+ - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: 1.1.5
54
- type: :development
53
+ version: 2.0.0
54
+ type: :runtime
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - "~>"
58
+ - - ">="
59
59
  - !ruby/object:Gem::Version
60
- version: 1.1.5
60
+ version: 2.0.0
61
61
  - !ruby/object:Gem::Dependency
62
- name: rack
62
+ name: json_spec
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 3.0.0
67
+ version: 1.1.5
68
68
  type: :development
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 3.0.0
74
+ version: 1.1.5
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: rspec
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -92,28 +92,28 @@ dependencies:
92
92
  requirements:
93
93
  - - '='
94
94
  - !ruby/object:Gem::Version
95
- version: 1.52.0
95
+ version: 1.55.1
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - '='
101
101
  - !ruby/object:Gem::Version
102
- version: 1.52.0
102
+ version: 1.55.1
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: rubocop-rspec
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - '='
108
108
  - !ruby/object:Gem::Version
109
- version: 2.22.0
109
+ version: 2.23.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
114
  - - '='
115
115
  - !ruby/object:Gem::Version
116
- version: 2.22.0
116
+ version: 2.23.0
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: simplecov
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -138,23 +138,33 @@ files:
138
138
  - README.md
139
139
  - lib/openapi_contracts.rb
140
140
  - lib/openapi_contracts/doc.rb
141
- - lib/openapi_contracts/doc/file_parser.rb
142
141
  - lib/openapi_contracts/doc/header.rb
143
- - lib/openapi_contracts/doc/method.rb
142
+ - lib/openapi_contracts/doc/operation.rb
144
143
  - lib/openapi_contracts/doc/parameter.rb
145
- - lib/openapi_contracts/doc/parser.rb
146
144
  - lib/openapi_contracts/doc/path.rb
145
+ - lib/openapi_contracts/doc/pointer.rb
146
+ - lib/openapi_contracts/doc/request.rb
147
147
  - lib/openapi_contracts/doc/response.rb
148
148
  - lib/openapi_contracts/doc/schema.rb
149
+ - lib/openapi_contracts/doc/with_parameters.rb
149
150
  - lib/openapi_contracts/helper.rb
150
151
  - lib/openapi_contracts/match.rb
152
+ - lib/openapi_contracts/operation_router.rb
153
+ - lib/openapi_contracts/parser.rb
154
+ - lib/openapi_contracts/parser/transformers.rb
155
+ - lib/openapi_contracts/parser/transformers/base.rb
156
+ - lib/openapi_contracts/parser/transformers/nullable.rb
157
+ - lib/openapi_contracts/parser/transformers/pointer.rb
158
+ - lib/openapi_contracts/payload_parser.rb
151
159
  - lib/openapi_contracts/rspec.rb
152
160
  - lib/openapi_contracts/validators.rb
153
161
  - lib/openapi_contracts/validators/base.rb
154
- - lib/openapi_contracts/validators/body.rb
155
162
  - lib/openapi_contracts/validators/documented.rb
156
163
  - lib/openapi_contracts/validators/headers.rb
157
164
  - lib/openapi_contracts/validators/http_status.rb
165
+ - lib/openapi_contracts/validators/request_body.rb
166
+ - lib/openapi_contracts/validators/response_body.rb
167
+ - lib/openapi_contracts/validators/schema_validation.rb
158
168
  homepage: https://github.com/mkon/openapi_contracts
159
169
  licenses:
160
170
  - MIT
@@ -168,7 +178,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
168
178
  requirements:
169
179
  - - ">="
170
180
  - !ruby/object:Gem::Version
171
- version: '2.7'
181
+ version: '3.0'
172
182
  - - "<"
173
183
  - !ruby/object:Gem::Version
174
184
  version: '3.3'