openapi_first 2.0.2 → 2.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87fceda33e397fa75a8268144e5cbacfb802333a5ef1c315676d14458312b01b
4
- data.tar.gz: a734ee73e2dce89fea8d2fc93c713b0505923b2b2098b560a2b44ef3e5753c84
3
+ metadata.gz: a500abac816ab74de8b74c3d72793f31865d8c0c4863b7af769d748b99a85790
4
+ data.tar.gz: cf0394ab100eea73f66ff5275ff81bc1fb7cb06d63ed69f62cb961db48966f61
5
5
  SHA512:
6
- metadata.gz: 63d51c543d3d689ced06170e1e53f6d0a20056af2643e1f1fccfbf966b14352c55ad9c0dfd92478ceb7e5aaac0b6f7c7563e17d9e69ab2f924f6a9d4846c45ff
7
- data.tar.gz: 8649d5737439a2414802e17ed562a0efdf294f29e7515b0e248e5736cd59235eba876baf52aad8e5d602ff90ba5afc8902ed1609d902dfe03c65d4136afc1042
6
+ metadata.gz: eb03e7141b82b6ae39d59948e5a3ce90a67c8ec2744b257ca18ed95cf537796200923e4bbc1bccfca5a2359995405f5015df29a01947e0add2ec653dfa44b778
7
+ data.tar.gz: 16458594f9cfef02f7faa3bd207487d74c8eaf400704c1631d80edca3ab1ae6d5820213c8efb85145be3f1dc3a1f91020cfb5b7c9330bd52c6acfca601fc7eb5
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.0.4
6
+
7
+ - Fix issue with parsing reponse body when using Rails https://github.com/ahx/openapi_first/issues/281
8
+
9
+ ## 2.0.3
10
+
11
+ - Fix `OpenapiFirst::Test.register` https://github.com/ahx/openapi_first/issues/276
12
+
13
+ - Request validation middleware now accepts `error_response: false` do disable rendering a response. This is useful if you just want to collect metrics (via hooks) during a migration phase.
14
+
5
15
  ## 2.0.2
6
16
 
7
17
  - Fix setting custom error response (thanks @gobijan)
data/README.md CHANGED
@@ -2,17 +2,20 @@
2
2
 
3
3
  OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description. It supports OpenAPI 3.0 and 3.1. It offers request and response validation and it ensures that your implementation follows exactly the API description.
4
4
 
5
+ [![Tests](https://github.com/ahx/openapi_first/actions/workflows/ruby.yml/badge.svg)](https://github.com/ahx/openapi_first/actions/workflows/ruby.yml)
6
+ [![CodeQL](https://github.com/ahx/openapi_first/actions/workflows/codeql.yml/badge.svg)](https://github.com/ahx/openapi_first/blob/codeql/.github/workflows/codeql.yml)
7
+
5
8
  ## Contents
6
9
 
7
10
  <!-- TOC -->
8
11
 
12
+ - [Manual use](#manual-use)
13
+ - [Validate request](#validate-request)
14
+ - [Validate response](#validate-response)
9
15
  - [Rack Middlewares](#rack-middlewares)
10
16
  - [Request validation](#request-validation)
11
17
  - [Response validation](#response-validation)
12
18
  - [Test assertions](#test-assertions)
13
- - [Manual use](#manual-use)
14
- - [Validate request](#validate-request)
15
- - [Validate response](#validate-response)
16
19
  - [Framework integration](#framework-integration)
17
20
  - [Configuration](#configuration)
18
21
  - [Hooks](#hooks)
@@ -23,6 +26,61 @@ OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.open
23
26
 
24
27
  <!-- /TOC -->
25
28
 
29
+ ## Manual use
30
+
31
+ Load the API description:
32
+
33
+ ```ruby
34
+ require 'openapi_first'
35
+
36
+ definition = OpenapiFirst.load('openapi.yaml')
37
+ ```
38
+
39
+ ### Validate request
40
+
41
+ ```ruby
42
+ validated_request = definition.validate_request(rack_request)
43
+
44
+ # Inspect the request and access parsed parameters
45
+ validated_request.valid?
46
+ validated_request.invalid?
47
+ validated_request.error # => Failure object or nil
48
+ validated_request.parsed_body # => The parsed request body (Hash)
49
+ validated_request.parsed_query # A Hash of query parameters that are defined in the API description, parsed exactly as described.
50
+ validated_request.parsed_path_parameters
51
+ validated_request.parsed_headers
52
+ validated_request.parsed_cookies
53
+ validated_request.parsed_params # Merged parsed path, query parameters and request body
54
+ # Access the Openapi 3 Operation Object Hash
55
+ validated_request.operation['x-foo']
56
+ validated_request.operation['operationId'] => "getStuff"
57
+ # or the whole request definition
58
+ validated_request.request_definition.path # => "/pets/{petId}"
59
+ validated_request.request_definition.operation_id # => "showPetById"
60
+
61
+ # Or you can raise an exception if validation fails:
62
+ definition.validate_request(rack_request, raise_error: true) # Raises OpenapiFirst::RequestInvalidError or OpenapiFirst::NotFoundError if request is invalid
63
+ ```
64
+
65
+ ### Validate response
66
+
67
+ ```ruby
68
+ validated_response = definition.validate_response(rack_request, rack_response)
69
+
70
+ # Inspect the response and access parsed parameters and
71
+ validated_response.valid?
72
+ validated_response.invalid?
73
+ validated_response.error # => Failure object or nil
74
+ validated_response.status # => 200
75
+ validated_response.parsed_body
76
+ validated_response.parsed_headers
77
+
78
+ # Or you can raise an exception if validation fails:
79
+ definition.validate_response(rack_request,rack_response, raise_error: true) # Raises OpenapiFirst::ResponseInvalidError or OpenapiFirst::ResponseNotFoundError
80
+ ```
81
+
82
+ OpenapiFirst uses [`multi_json`](https://rubygems.org/gems/multi_json).
83
+
26
84
  ## Rack Middlewares
27
85
 
28
86
  ### Request validation
@@ -39,7 +97,7 @@ use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml'
39
97
  | :---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
40
98
  | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
41
99
  | `raise_error:` | `false` (default), `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` or `OpenapiFirst::NotFoundError` instead of returning 4xx. |
42
- | `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` |
100
+ | `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` or `false` to disable responding |
43
101
 
44
102
  #### Error responses
45
103
 
@@ -174,67 +232,6 @@ class TripsApiTest < ActionDispatch::IntegrationTest
174
232
  end
175
233
  ```
176
234
 
177
- ## Manual use
178
-
179
- Load the API description:
180
-
181
- ```ruby
182
- require 'openapi_first'
183
-
184
- definition = OpenapiFirst.load('openapi.yaml')
185
- ```
186
-
187
- ### Validate request
188
-
189
- ```ruby
190
- # Find and validate request
191
- rack_request = Rack::Request.new(env)
192
- validated_request = definition.validate_request(rack_request)
193
- # Or raise an exception if validation fails:
194
- definition.validate_request(rack_request, raise_error: true) # Raises OpenapiFirst::RequestInvalidError or OpenapiFirst::NotFoundError if request is invalid
195
-
196
- # Inspect the request and access parsed parameters
197
- validated_request.known? # Is the request defined in the API description?
198
- validated_request.valid? # => true / false
199
- validated_request.invalid? # => true / false
200
- validated_request.error # => Failure object if request is invalid
201
- validated_request.parsed_params # Merged parsed path, query parameters and request body
202
- validated_request.parsed_body
203
- validated_request.parsed_path_parameters # => { "pet_id" => 42 }
204
- validated_request.parsed_headers
205
- validated_request.parsed_cookies
206
- validated_request.parsed_query
207
-
208
- # Access the Openapi 3 Operation Object Hash
209
- validated_request.operation['x-foo']
210
- validated_request.operation['operationId']
211
- # or the whole request definition
212
- validated_request.request_definition.path # => "/pets/{petId}"
213
- validated_request.request_definition.operation_id # => "showPetById"
214
- ```
215
-
216
- ### Validate response
217
-
218
- ```ruby
219
- # Find and validate the response
220
- rack_response = Rack::Response[*app.call(env)]
221
- validated_response = definition.validate_response(rack_request, rack_response)
222
-
223
- # Raise an exception if validation fails:
224
- definition.validate_response(rack_request,rack_response, raise_error: true) # Raises OpenapiFirst::ResponseInvalidError or OpenapiFirst::ResponseNotFoundError
225
-
226
- # Inspect the response and access parsed parameters and
227
- response.known? # Is the response defined in the API description?
228
- response.valid? # => true / false
229
- response.invalid? # => true / false
230
- response.error # => Failure object if response is invalid
231
- response.status # => 200
232
- response.parsed_body
233
- response.parsed_headers
234
- ```
235
-
236
- OpenapiFirst uses [`multi_json`](https://rubygems.org/gems/multi_json).
237
-
238
235
  ## Configuration
239
236
 
240
237
  You can configure default options globally:
@@ -10,11 +10,13 @@ module OpenapiFirst
10
10
  # :raise_error A Boolean indicating whether to raise an error if validation fails.
11
11
  # default: false
12
12
  # :error_response The Class to use for error responses.
13
+ # This can be a Symbol-name of an registered error response (:default, :jsonapi)
14
+ # or it can be set to false to disable returning a response.
13
15
  # default: OpenapiFirst::Plugins::Default::ErrorResponse (Config.default_options.error_response)
14
16
  def initialize(app, options = {})
15
17
  @app = app
16
18
  @raise = options.fetch(:raise_error, OpenapiFirst.configuration.request_validation_raise_error)
17
- @error_response_class = error_response(options[:error_response])
19
+ @error_response_class = error_response_option(options[:error_response])
18
20
 
19
21
  spec = options.fetch(:spec)
20
22
  raise "You have to pass spec: when initializing #{self.class}" unless spec
@@ -29,17 +31,18 @@ module OpenapiFirst
29
31
  validated = @definition.validate_request(Rack::Request.new(env), raise_error: @raise)
30
32
  env[REQUEST] = validated
31
33
  failure = validated.error
32
- return @error_response_class.new(failure:).render if failure
34
+ return @error_response_class.new(failure:).render if failure && @error_response_class
33
35
 
34
36
  @app.call(env)
35
37
  end
36
38
 
37
39
  private
38
40
 
39
- def error_response(mod)
40
- return OpenapiFirst.find_error_response(mod) if mod.is_a?(Symbol)
41
+ def error_response_option(value)
42
+ return if value == false
43
+ return OpenapiFirst.find_error_response(value) if value.is_a?(Symbol)
41
44
 
42
- mod || OpenapiFirst.configuration.request_validation_error_response
45
+ value || OpenapiFirst.configuration.request_validation_error_response
43
46
  end
44
47
  end
45
48
  end
@@ -25,20 +25,9 @@ module OpenapiFirst
25
25
 
26
26
  def call(env)
27
27
  status, headers, body = @app.call(env)
28
- body = read_body(body)
29
28
  @definition.validate_response(Rack::Request.new(env), Rack::Response[status, headers, body], raise_error: @raise)
30
29
  [status, headers, body]
31
30
  end
32
-
33
- private
34
-
35
- def read_body(body)
36
- return body.to_ary if body.respond_to?(:to_ary)
37
-
38
- result = []
39
- body.each { |part| result << part }
40
- result
41
- end
42
31
  end
43
32
  end
44
33
  end
@@ -5,8 +5,8 @@ module OpenapiFirst
5
5
  # @visibility private
6
6
  class PathTemplate
7
7
  # See also https://spec.openapis.org/oas/v3.1.0#path-templating
8
- TEMPLATE_EXPRESSION = /(\{[^}]+\})/
9
- TEMPLATE_EXPRESSION_NAME = /\{([^}]+)\}/
8
+ TEMPLATE_EXPRESSION = /(\{[^{}]+\})/
9
+ TEMPLATE_EXPRESSION_NAME = /\{([^{}]+)\}/
10
10
  ALLOWED_PARAMETER_CHARACTERS = %r{([^/?#]+)}
11
11
 
12
12
  def self.template?(string)
@@ -7,7 +7,8 @@ module OpenapiFirst
7
7
  module Test
8
8
  class NotRegisteredError < StandardError; end
9
9
 
10
- DEFINITIONS = {}.freeze
10
+ DEFINITIONS = {} # rubocop:disable Style/MutableConstant
11
+
11
12
  def self.definitions = DEFINITIONS
12
13
 
13
14
  def self.register(path, as: :default)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '2.0.2'
4
+ VERSION = '2.0.4'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-06-26 00:00:00.000000000 Z
11
+ date: 2024-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hana