openapi_first 2.2.2 → 2.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de3cced92ef56a41399e4baa7aaf2b42250d2d3e532a22b245edd70d2b7446d2
4
- data.tar.gz: b433a7d18c02176dda8ce12ff41636d4f19bd99632cbc0085879e34b3090cb76
3
+ metadata.gz: d85c4425401207c7ff52f921d34c77c7ee6c6ee5671ecfbac36ca5619685cdb6
4
+ data.tar.gz: afcb639314fd7f049b93d29dd89cbb88260abbcb66f1e5a20b8c1c01ade063b2
5
5
  SHA512:
6
- metadata.gz: 58613cfd3648073dee3189cfced111adcdfd4471ee67a07517d3c800c69aded0ab46b50a69d56fda453a2af9078db74ee45c260c601c16cdd2896428d330068b
7
- data.tar.gz: ae57aa8b109569165e9bb9ce3e9eb380e387a0eb1afb3cde850f7a2d3979391dc62ebe567cacb51492aefe4d9ef2db2db8963c4ca2b256cbf0c5b3e9491a2de3
6
+ metadata.gz: 8c9e2fadb4ba81ab68026b5b2f6c44daf9710d3e3fc27e32175f9691f885839f6fda6cd1f477c7e347b88ce0d27e933bd30642d1773665301613049df092e7a5
7
+ data.tar.gz: 493dcfa9384f1462b7ab713dcc8c3c9b7cb2c6f63a4977f0ed3485d65811a3cd0869c2ddc5f8dc9cd5efacaa773836b0290d2be7f88b4af20de9adad2aa7c7b6
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.2.4
6
+
7
+ - Fix request validation file uploads in multipart/form-data requests with nested fields (https://github.com/ahx/openapi_first/issues/324)
8
+ - Add more error details to validation result (https://github.com/ahx/openapi_first/pull/322)
9
+
10
+ ## 2.2.3
11
+
12
+ - Respect global JSONSchemer configuration (https://github.com/ahx/openapi_first/pull/318)
13
+
5
14
  ## 2.2.2
6
15
 
7
16
  - Fix parsing parameters with referenced schemas (https://github.com/ahx/openapi_first/issues/316)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json_schemer'
4
- require_relative 'json_pointer'
5
4
  require_relative 'ref_resolver'
6
5
 
7
6
  module OpenapiFirst
@@ -18,10 +17,10 @@ module OpenapiFirst
18
17
  end
19
18
 
20
19
  def initialize(contents, filepath:, config:)
21
- @schemer_configuration = JSONSchemer::Configuration.new(
22
- meta_schema: detect_meta_schema(contents, filepath),
23
- insert_property_defaults: true
24
- )
20
+ @schemer_configuration = JSONSchemer.configuration.clone
21
+ @schemer_configuration.meta_schema = detect_meta_schema(contents, filepath)
22
+ @schemer_configuration.insert_property_defaults = true
23
+
25
24
  @config = config
26
25
  @contents = RefResolver.for(contents, dir: filepath && File.dirname(filepath))
27
26
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Integration for rack-test
4
+
5
+ require 'rack/test'
@@ -55,7 +55,7 @@ module OpenapiFirst
55
55
  value = Hana::Pointer.new(pointer[1..]).eval(context)
56
56
  raise "Unknown reference #{pointer} in #{context}" unless value
57
57
 
58
- return RefResolver.for(value, dir:)
58
+ return RefResolver.for(value, dir:, context:)
59
59
  end
60
60
 
61
61
  relative_path, file_pointer = pointer.split('#')
@@ -65,7 +65,7 @@ module OpenapiFirst
65
65
  file_contents = FileLoader.load(full_path)
66
66
  new_dir = File.dirname(full_path)
67
67
  value = Hana::Pointer.new(file_pointer).eval(file_contents)
68
- RefResolver.for(value, dir: new_dir)
68
+ RefResolver.for(value, dir: new_dir, context: file_contents)
69
69
  end
70
70
  end
71
71
 
@@ -36,11 +36,28 @@ module OpenapiFirst
36
36
  Failure.fail!(:invalid_body, message: 'Failed to parse request body as JSON')
37
37
  end)
38
38
 
39
- register('multipart/form-data', lambda { |request|
40
- request.POST.transform_values do |value|
41
- value.is_a?(Hash) && value[:tempfile] ? value[:tempfile].read : value
39
+ # Parses multipart/form-data requests and currently puts the contents of a file upload at the parsed hash values.
40
+ # NOTE: This behavior will probably change in the next major version.
41
+ # The uploaded file should not be read during request validation.
42
+ module MultipartBodyParser
43
+ def self.call(request)
44
+ request.POST.transform_values do |value|
45
+ unpack_value(value)
46
+ end
42
47
  end
43
- })
48
+
49
+ def self.unpack_value(value)
50
+ return value.map { unpack_value(_1) } if value.is_a?(Array)
51
+ return value unless value.is_a?(Hash)
52
+ return value[:tempfile]&.read if value.key?(:tempfile)
53
+
54
+ value.transform_values do |v|
55
+ unpack_value(v)
56
+ end
57
+ end
58
+ end
59
+
60
+ register('multipart/form-data', MultipartBodyParser)
44
61
 
45
62
  register('application/x-www-form-urlencoded', lambda(&:POST))
46
63
  end
@@ -3,7 +3,7 @@
3
3
  module OpenapiFirst
4
4
  class Schema
5
5
  # One of multiple validation errors. Returned by Schema::ValidationResult#errors.
6
- ValidationError = Data.define(:message, :data_pointer, :schema_pointer, :type, :details) do
6
+ ValidationError = Data.define(:value, :message, :data_pointer, :schema_pointer, :type, :details, :schema) do
7
7
  # @deprecated Please use {#message} instead
8
8
  def error
9
9
  warn 'OpenapiFirst::Schema::ValidationError#error is deprecated. Use #message instead.'
@@ -16,11 +16,13 @@ module OpenapiFirst
16
16
  def errors
17
17
  @errors ||= @validation.map do |err|
18
18
  ValidationError.new(
19
+ value: err['data'],
19
20
  message: err['error'],
20
21
  data_pointer: err['data_pointer'],
21
22
  schema_pointer: err['schema_pointer'],
22
23
  type: err['type'],
23
- details: err['details']
24
+ details: err['details'],
25
+ schema: err['schema']
24
26
  )
25
27
  end
26
28
  end
@@ -18,8 +18,13 @@ module OpenapiFirst
18
18
  "from #{request.request_method.upcase} #{request.path}."
19
19
  end
20
20
 
21
- api.validate_request(request, raise_error: true)
22
- api.validate_response(request, response, raise_error: true)
21
+ validated = api.validate_request(request, raise_error: false)
22
+ # :nocov:
23
+ raise validated.error.exception if validated.invalid?
24
+
25
+ validated = api.validate_response(request, response, raise_error: false)
26
+ raise validated.error.exception if validated.invalid?
27
+ # :nocov:
23
28
  end
24
29
  end
25
30
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '2.2.2'
4
+ VERSION = '2.2.4'
5
5
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.2
4
+ version: 2.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-01-21 00:00:00.000000000 Z
10
+ date: 2025-02-11 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: hana
@@ -93,7 +93,6 @@ files:
93
93
  - LICENSE.txt
94
94
  - README.md
95
95
  - lib/openapi_first.rb
96
- - lib/openapi_first/body_parser.rb
97
96
  - lib/openapi_first/builder.rb
98
97
  - lib/openapi_first/configuration.rb
99
98
  - lib/openapi_first/definition.rb
@@ -104,9 +103,9 @@ files:
104
103
  - lib/openapi_first/failure.rb
105
104
  - lib/openapi_first/file_loader.rb
106
105
  - lib/openapi_first/json.rb
107
- - lib/openapi_first/json_pointer.rb
108
106
  - lib/openapi_first/middlewares/request_validation.rb
109
107
  - lib/openapi_first/middlewares/response_validation.rb
108
+ - lib/openapi_first/rack/test.rb
110
109
  - lib/openapi_first/ref_resolver.rb
111
110
  - lib/openapi_first/request.rb
112
111
  - lib/openapi_first/request_body_parsers.rb
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OpenapiFirst
4
- # @!visibility private
5
- module BodyParser
6
- def self.[](content_type)
7
- case content_type
8
- when /json/i
9
- JsonBodyParser
10
- when %r{multipart/form-data}i
11
- MultipartBodyParser
12
- else
13
- DefaultBodyParser
14
- end
15
- end
16
-
17
- def self.read_body(request)
18
- body = request.body&.read
19
- request.body.rewind if request.body.respond_to?(:rewind)
20
- body
21
- end
22
-
23
- JsonBodyParser = lambda do |request|
24
- body = read_body(request)
25
- return if body.nil? || body.empty?
26
-
27
- JSON.parse(body)
28
- rescue JSON::ParserError
29
- Failure.fail!(:invalid_body, message: 'Failed to parse request body as JSON')
30
- end
31
-
32
- MultipartBodyParser = lambda do |request|
33
- request.POST.transform_values do |value|
34
- value.is_a?(Hash) && value[:tempfile] ? value[:tempfile].read : value
35
- end
36
- end
37
-
38
- # This returns the post data parsed by rack or the raw body
39
- DefaultBodyParser = lambda do |request|
40
- return request.POST if request.form_data?
41
-
42
- read_body(request)
43
- end
44
- end
45
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module OpenapiFirst
4
- # Functions to handle JSON Pointers
5
- # @!visibility private
6
- module JsonPointer
7
- ESCAPE_CHARS = { '~' => '~0', '/' => '~1', '+' => '%2B' }.freeze
8
- ESCAPE_REGEX = Regexp.union(ESCAPE_CHARS.keys)
9
-
10
- module_function
11
-
12
- def append(root, *tokens)
13
- "#{root}/" + tokens.map do |token|
14
- escape_json_pointer_token(token)
15
- end.join('/')
16
- end
17
-
18
- def escape_json_pointer_token(token)
19
- token.to_s.gsub(ESCAPE_REGEX, ESCAPE_CHARS)
20
- end
21
- end
22
- end