openapi_first 2.0.0 → 2.0.3

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: d712aa1e9c42b4d42bc048fa0cc5c793af9d8e5c4f3afd34deda3afe2136eb70
4
- data.tar.gz: 8a220208d4eb6755a066db142f20271101c97e7c7c26e000d0519770300c0f39
3
+ metadata.gz: 600de21e1747c012275bf68c2d577296a8d82bb158cb4d73949b5b97167e655d
4
+ data.tar.gz: 86bcde27440b46fba91662fc7e6b86ac05d899da927fefc3e9d45a41a6d7fac9
5
5
  SHA512:
6
- metadata.gz: d5424ea246ae643962375a22adddac25ec2de49222aed0a3e6cabc4d8efbbd49faf65e569c219a758b62261a085afd06ee9b6c75f453f0c30397610e9460a793
7
- data.tar.gz: 9599b92d69766a231c7a24f708cabfcaff5f3724117b3f1948d1b9e76c1408f72bafceebaf17752a30e58d752375b807de57ab53dad38abf84720f192c5a3180
6
+ metadata.gz: ee729f6f6fedf5d6e9b82b82bdf86493a0a7ea6274e20b60e2cdd84eda2ce086cf366249f61a28016374355d11ab32654a970a418cf5af24a0b187d31b967618
7
+ data.tar.gz: 125cc38e91e489f30c922f38056c69f9a6d501f3748ee098f2977d8fc6dc7f5f43f77264991f98564cb60ff2af19912ae85537c57d25c92604f8f6ee8766a749
data/CHANGELOG.md CHANGED
@@ -2,7 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- ## 2.0
5
+ ## 2.0.3
6
+
7
+ - 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.
8
+
9
+ ## 2.0.2
10
+
11
+ - Fix setting custom error response (thanks @gobijan)
12
+
13
+ ## 2.0.1 (Janked)
14
+
15
+ ## 2.0.0
6
16
 
7
17
  ### New Features
8
18
  - Test Assertions! 📋 You can now use `assert_api_conform` for contract testing in your rack-test / Rails integration tests. See Readme for details.
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:
@@ -34,7 +34,7 @@ module OpenapiFirst
34
34
 
35
35
  def request_validation_error_response=(mod)
36
36
  @request_validation_error_response = if mod.is_a?(Symbol)
37
- OpenapiFirst.find_plugin(:default)::ErrorResponse
37
+ OpenapiFirst.find_error_response(mod)
38
38
  else
39
39
  mod
40
40
  end
@@ -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
@@ -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)
@@ -5,15 +5,24 @@ require_relative 'test/methods'
5
5
  module OpenapiFirst
6
6
  # Test integration
7
7
  module Test
8
+ class NotRegisteredError < StandardError; end
9
+
10
+ DEFINITIONS = {} # rubocop:disable Style/MutableConstant
11
+
12
+ def self.definitions = DEFINITIONS
13
+
8
14
  def self.register(path, as: :default)
9
- @registry ||= {}
10
- @registry[as] = OpenapiFirst.load(path)
15
+ definitions[as] = OpenapiFirst.load(path)
11
16
  end
12
17
 
13
18
  def self.[](api)
14
- @registry[api] || raise(ArgumentError,
15
- "API description #{api} not found to be used via assert_api_conform. " \
16
- 'Use OpenapiFirst::Test.register to load an API description first.')
19
+ definitions.fetch(api) do
20
+ option = api == :default ? '' : ", as: #{api.inspect}"
21
+ raise(NotRegisteredError,
22
+ "API description '#{api.inspect}' not found." \
23
+ "Please call OpenapiFirst::Test.register('myopenapi.yaml'#{option}) " \
24
+ 'once before calling assert_api_conform.')
25
+ end
17
26
  end
18
27
  end
19
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '2.0.0'
4
+ VERSION = '2.0.3'
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.0
4
+ version: 2.0.3
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-24 00:00:00.000000000 Z
11
+ date: 2024-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hana