openapi_first 2.7.2 → 2.7.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: 93e611196a63a7f9bf41d6ee90c25f22dbeaf49bc7c08ff00c26dd579c68a70d
4
- data.tar.gz: dbe36e65d24186b0c3ac258508f4ae5ecbd7eea55a6679d6107c7b2d2dee6547
3
+ metadata.gz: 2d3e13bc24feccc8015701ca7360e77fbb8c44d8c7dad9e87310f52b04ddebe6
4
+ data.tar.gz: 8a5895ce7b3a5a7a2669e862f4821125538573a6e694db36e8d2687580c3e0c9
5
5
  SHA512:
6
- metadata.gz: 2ef33d27a35ed67937026a735a35ee4934519b4463f09e95a4e0987f17c50c72d40990827a1f05f8bfad6b2e1ba4e79fea86d055d3b29d2e11b07810a7eba6fb
7
- data.tar.gz: 5b7825b079722c7718f84b7ebb211a6201a03723077aac83947d654470b773ba389e95424a5038fab74370a521890c848cd0016381ef34104e1b4c2a3daa4fda
6
+ metadata.gz: 3420becb820a470942b12e39d7e59197f9a50696a0f63c2af601ce26cad4e95c6b6e9d915e5b3f4528da78bf1f012993af9401d1863578e8e64c2059896c71bb
7
+ data.tar.gz: fe61cf2f437b9a725b377b7dcc5917ac130639ca67514450bee3ba919f289f083fa232b9a94770d175232da2546b5b999c5e544abe20d0dba060adc6d4eb6f07
data/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2.7.4
6
+
7
+ - Return 400 if Rack cannot parse query string instead of raising an exception. Fixes https://github.com/ahx/openapi_first/issues/372
8
+
9
+ ## 2.7.3
10
+
11
+ - Accept loading OAD documents with numeric status codes. Fixes "Unknown reference" error. https://github.com/ahx/openapi_first/issues/367
12
+
13
+ - Support QUERY request method
14
+ OpenAPI 3.0, 3.1 does not support that, but this does
15
+
5
16
  ## 2.7.2
6
17
 
7
18
  - Fix $ref-resolving for referenced arrays.
data/README.md CHANGED
@@ -2,18 +2,17 @@
2
2
 
3
3
  openapi_first is a Ruby gem for request / response validation and contract-testing against an [OpenAPI](https://www.openapis.org/) 3.0 or 3.1 API description. It makes an APIFirst workflow easy and reliable.
4
4
 
5
- You can use openapi_first on production for [request validation](#request-validation) and in your tests to avoid API drift with it's request/response validation and coverage features.
5
+ You can use openapi_first on production for [request validation](#request-validation) and in your [tests](#contract-testing) to avoid API drift with it's request/response validation and coverage features.
6
6
 
7
7
  ## Contents
8
8
 
9
9
  <!-- TOC -->
10
10
 
11
+ - [Contract testing](#contract-testing)
11
12
  - [Rack Middlewares](#rack-middlewares)
12
13
  - [Request validation](#request-validation)
13
14
  - [Response validation](#response-validation)
14
- - [Contract testing](#contract-testing)
15
- - [Coverage](#coverage)
16
- - [Test assertions](#test-assertions)
15
+ - [Test assertions](#test-assertions)
17
16
  - [Manual use](#manual-use)
18
17
  - [Framework integration](#framework-integration)
19
18
  - [Configuration](#configuration)
@@ -26,6 +25,41 @@ You can use openapi_first on production for [request validation](#request-valida
26
25
 
27
26
  <!-- /TOC -->
28
27
 
28
+ ## Contract Testing
29
+
30
+ You can see your OpenAPI API description as a contract that your clients can rely on as how your API behaves. There are two aspects of contract testing: Validation and Coverage. By validating requests and responses, you can avoid that your API implementation processes requests or returns responses that don't match your API description. To make sure your _whole_ API description is implemented, openapi_first can check that all of your API description is covered when you test your API with [rack-test](https://github.com/rack/rack-test).
31
+
32
+ Here is how to set it up:
33
+
34
+ 1. Register all OpenAPI documents to track coverage for.
35
+ This should go at the top of your test helper file before loading your application code.
36
+ ```ruby
37
+ require 'openapi_first'
38
+ OpenapiFirst::Test.setup do |test|
39
+ test.register('openapi/openapi.yaml')
40
+ # Optional: Make tests fail if coverage is below minimum
41
+ test.minimum_coverage = 100
42
+ # Optional: Skip certain responses, which are described in your API description, but need no test coverage
43
+ test.skip_response_coverage { it.status == '500' } #
44
+ end
45
+ ```
46
+ 2. Add an `app` method to your tests by including a Module. This `app` method wraps your application with silent request / response validation. This validates all requests/responses in your test run. (✷1)
47
+ ```ruby
48
+ RSpec.configure do |config|
49
+ config.include OpenapiFirst::Test::Methods[MyApp], type: :request
50
+ end
51
+ ```
52
+ Or add the `app` method yourself:
53
+
54
+ ```ruby
55
+ def app
56
+ OpenapiFirst::Test.app(MyApp)
57
+ end
58
+ ```
59
+ 3. Run your tests. The Coverage feature will tell you about missing or invalid requests/responses.
60
+
61
+ (✷1): It does not matter what method of openapi_first you use to validate requests/responses. Instead of using `OpenapiFirstTest.app` to wrap your application, you could also use the [middlewares](#rack-middlewares) or [test assertion method](#test-assertions), but you would have to do that for all requests/responses defined in your API description to make coverage work.
62
+
29
63
  ## Rack Middlewares
30
64
 
31
65
  ### Request validation
@@ -135,51 +169,7 @@ use OpenapiFirst::Middlewares::ResponseValidation, 'openapi.yaml', raise_error:
135
169
 
136
170
  If you are adopting OpenAPI you can use these options together with [hooks](#hooks) to get notified about requests/responses that do match your API description.
137
171
 
138
- ## Contract Testing
139
-
140
- Here are two aspects of contract testing: Validation and Coverage
141
-
142
- ### Validation
143
-
144
- By validating requests and responses, you can avoid that your API implementation processes requests or returns responses that don't match your API description. You can use [test assertions](#test-assertions) or [rack middlewares](#rack-middlewares) or manual validation to validate requests and responses with openapi_first.
145
-
146
- ### Coverage
147
-
148
- To make sure your _whole_ API description is implemented, openapi_first ships with a coverage feature.
149
-
150
- > [!NOTE]
151
- > This is a brand new feature. ✨ Your feedback is very welcome.
152
-
153
- This feature tracks all requests/responses that are validated via openapi_first and tells you about which request/responses are missing.
154
- Here is how to set it up with [rack-test](https://github.com/rack/rack-test):
155
-
156
- 1. Register all OpenAPI documents to track coverage for. This should go at the top of your test helper file before loading your application code.
157
- ```ruby
158
- require 'openapi_first'
159
- OpenapiFirst::Test.setup do |test|
160
- test.register('openapi/openapi.yaml')
161
- test.minimum_coverage = 100 # (Optional) Setting this will lead to an `exit 2` if coverage is below minimum
162
- test.skip_response_coverage { it.status == '500' } # (Optional) Skip certain responses
163
- end
164
- ```
165
- 2. Add an `app` method to your tests, which wraps your application with silent request / response validation. This validates all requests/responses in your test run. (✷1)
166
-
167
- ```ruby
168
- def app
169
- OpenapiFirst::Test.app(MyApp)
170
- end
171
- ```
172
- 3. Run your tests. The Coverage feature will tell you about missing request/responses.
173
-
174
- Or you can generate a Module and include it in your rspec spec_helper.rb:
175
-
176
- ```ruby
177
- config.include OpenapiFirst::Test::Methods[MyApp], type: :request
178
- ```
179
-
180
- (✷1): It does not matter what method of openapi_first you use to validate requests/responses. Instead of using `OpenapiFirstTest.app` to wrap your application, you could also use the middlewares or [test assertion method](#test-assertions), but you would have to do that for all requests/responses defined in your API description to make coverage work.
181
-
182
- ### Test assertions
172
+ ## Test assertions
183
173
 
184
174
  openapi_first ships with a simple but powerful Test method to run request and response validation in your tests without using the middlewares. This is designed to be used with rack-test or Ruby on Rails integration tests or request specs.
185
175
 
@@ -14,7 +14,7 @@ module OpenapiFirst
14
14
  # Builds parts of a Definition
15
15
  # This knows how to read a resolved OpenAPI document and build {Request} and {Response} objects.
16
16
  class Builder # rubocop:disable Metrics/ClassLength
17
- REQUEST_METHODS = %w[get head post put patch delete trace options].freeze
17
+ REQUEST_METHODS = %w[get head post put patch delete trace options query].freeze
18
18
 
19
19
  # Builds a router from a resolved OpenAPI document.
20
20
  # @param contents [Hash] The OpenAPI document Hash.
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'json'
3
4
  require 'yaml'
4
5
 
5
6
  module OpenapiFirst
@@ -27,11 +27,19 @@ module OpenapiFirst
27
27
  def parse(request, route_params:)
28
28
  ParsedRequest.new(
29
29
  path: @path_parser&.unpack(route_params),
30
- query: @query_parser&.unpack(request.env[Rack::QUERY_STRING]),
30
+ query: parse_query(request.env[Rack::QUERY_STRING]),
31
31
  headers: @headers_parser&.unpack_env(request.env),
32
32
  cookies: @cookies_parser&.unpack(request.env[Rack::HTTP_COOKIE]),
33
33
  body: @body_parsers&.call(request)
34
34
  )
35
35
  end
36
+
37
+ private
38
+
39
+ def parse_query(query_string)
40
+ @query_parser&.unpack(query_string)
41
+ rescue OpenapiParameters::InvalidParameterError
42
+ Failure.fail!(:invalid_query, message: 'Invalid query parameter.')
43
+ end
36
44
  end
37
45
  end
@@ -3,7 +3,7 @@
3
3
  require_relative 'validation_error'
4
4
 
5
5
  module OpenapiFirst
6
- class Schema
6
+ module Schema
7
7
  # A hash of Schemas
8
8
  class Hash
9
9
  # @param schema Hash of schemas
@@ -29,11 +29,7 @@ module OpenapiFirst
29
29
  hook.call(root_value, key, schema.value, nil)
30
30
  end
31
31
  enum.chain(key_validation.map do |err|
32
- data_pointer = "/#{key}"
33
- err.merge(
34
- 'error' => JSONSchemer::Errors.pretty(err),
35
- 'data_pointer' => data_pointer
36
- )
32
+ err.merge('data_pointer' => "/#{key}")
37
33
  end)
38
34
  end
39
35
  ValidationResult.new(validations)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- class Schema
4
+ module Schema
5
5
  # One of multiple validation errors. Returned by Schema::ValidationResult#errors.
6
6
  ValidationError = Data.define(:value, :data_pointer, :schema_pointer, :type, :details, :schema) do
7
7
  # This returns an error message for this specific error.
@@ -3,7 +3,7 @@
3
3
  require_relative 'validation_error'
4
4
 
5
5
  module OpenapiFirst
6
- class Schema
6
+ module Schema
7
7
  # Result of validating data against a schema. Return value of Schema#validate.
8
8
  class ValidationResult
9
9
  def initialize(validation)
@@ -54,6 +54,8 @@ module OpenapiFirst
54
54
  end
55
55
  end
56
56
 
57
+ # Sets up OpenAPI test coverage and OAD registration.
58
+ # @yieldparam [OpenapiFirst::Test::Setup] setup A setup for configuration
57
59
  def self.setup(&)
58
60
  unless block_given?
59
61
  raise ArgumentError, "Please provide a block to #{self.class}.setup to register you API descriptions"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '2.7.2'
4
+ VERSION = '2.7.4'
5
5
  end
data/lib/openapi_first.rb CHANGED
@@ -64,8 +64,9 @@ module OpenapiFirst
64
64
 
65
65
  # Parse a dereferenced Hash
66
66
  # @return [Definition]
67
+ # TODO: This needs to work with unresolved contents as well
67
68
  def self.parse(contents, only: nil, filepath: nil, &)
68
- # TODO: This needs to work with unresolved contents as well
69
+ contents = JSON.parse(JSON.generate(contents)) # Deeply stringify keys, because of YAML. See https://github.com/ahx/openapi_first/issues/367
69
70
  contents['paths'].filter!(&->(key, _) { only.call(key) }) if only
70
71
  Definition.new(contents, filepath, &)
71
72
  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.7.2
4
+ version: 2.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-17 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: hana
@@ -49,7 +49,7 @@ dependencies:
49
49
  requirements:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 0.3.3
52
+ version: 0.5.1
53
53
  - - "<"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '2.0'
@@ -59,7 +59,7 @@ dependencies:
59
59
  requirements:
60
60
  - - ">="
61
61
  - !ruby/object:Gem::Version
62
- version: 0.3.3
62
+ version: 0.5.1
63
63
  - - "<"
64
64
  - !ruby/object:Gem::Version
65
65
  version: '2.0'
@@ -162,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
162
  - !ruby/object:Gem::Version
163
163
  version: '0'
164
164
  requirements: []
165
- rubygems_version: 3.6.5
165
+ rubygems_version: 3.6.7
166
166
  specification_version: 4
167
167
  summary: OpenAPI based request validation, response validation, contract-testing and
168
168
  coverage