openapi_first 1.1.1 → 1.3.0

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/openapi_first/body_parser.rb +3 -1
  3. data/lib/openapi_first/configuration.rb +3 -1
  4. data/lib/openapi_first/definition/operation.rb +65 -5
  5. data/lib/openapi_first/definition/path_item.rb +1 -0
  6. data/lib/openapi_first/definition/request_body.rb +1 -0
  7. data/lib/openapi_first/definition/response.rb +7 -0
  8. data/lib/openapi_first/definition.rb +43 -3
  9. data/lib/openapi_first/error_response.rb +1 -1
  10. data/lib/openapi_first/errors.rb +6 -0
  11. data/lib/openapi_first/failure.rb +28 -4
  12. data/lib/openapi_first/middlewares/request_validation.rb +2 -5
  13. data/lib/openapi_first/middlewares/response_validation.rb +1 -4
  14. data/lib/openapi_first/plugins/default/error_response.rb +4 -4
  15. data/lib/openapi_first/plugins/default.rb +1 -1
  16. data/lib/openapi_first/plugins/jsonapi/error_response.rb +3 -2
  17. data/lib/openapi_first/plugins/jsonapi.rb +1 -1
  18. data/lib/openapi_first/plugins.rb +1 -0
  19. data/lib/openapi_first/request_validation/request_body_validator.rb +1 -1
  20. data/lib/openapi_first/request_validation/validator.rb +1 -0
  21. data/lib/openapi_first/response_validation/validator.rb +1 -0
  22. data/lib/openapi_first/runtime_request.rb +63 -3
  23. data/lib/openapi_first/runtime_response.rb +43 -4
  24. data/lib/openapi_first/schema/validation_error.rb +2 -0
  25. data/lib/openapi_first/schema/validation_result.rb +2 -7
  26. data/lib/openapi_first/schema.rb +1 -0
  27. data/lib/openapi_first/version.rb +1 -1
  28. data/lib/openapi_first.rb +21 -3
  29. metadata +6 -17
  30. data/.github/CODEOWNERS +0 -1
  31. data/.github/workflows/ruby.yml +0 -13
  32. data/.gitignore +0 -11
  33. data/CHANGELOG.md +0 -274
  34. data/Gemfile +0 -18
  35. data/Gemfile.lock +0 -170
  36. data/Gemfile.rack2 +0 -15
  37. data/Gemfile.rack2.lock +0 -99
  38. data/LICENSE.txt +0 -21
  39. data/README.md +0 -225
  40. data/openapi_first.gemspec +0 -47
data/README.md DELETED
@@ -1,225 +0,0 @@
1
- # openapi_first
2
-
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
-
5
- ## Contents
6
-
7
- <!-- TOC -->
8
-
9
- - [Manual use](#manual-use)
10
- - [Rack Middlewares](#rack-middlewares)
11
- - [Request validation](#request-validation)
12
- - [Response validation](#response-validation)
13
- - [Configuration](#configuration)
14
- - [Development](#development)
15
- - [Benchmarks](#benchmarks)
16
- - [Contributing](#contributing)
17
-
18
- <!-- /TOC -->
19
-
20
- ## Manual use
21
-
22
- Load the API description:
23
-
24
- ```ruby
25
- require 'openapi_first'
26
-
27
- definition = OpenapiFirst.load('petstore.yaml')
28
- ```
29
-
30
- Validate request / response:
31
-
32
- ```ruby
33
-
34
- # Find the request
35
- rack_request = Rack::Request.new(env) # GET /pets/42
36
- request = definition.request(rack_request)
37
-
38
- # Inspect the request and access parsed parameters
39
- request.known? # Is the request defined in the API description?
40
- request.content_type
41
- request.body # alias: parsed_body
42
- request.path_parameters # => { "pet_id" => 42 }
43
- request.query_parameters # alias: query
44
- request.params # Merged path and query parameters
45
- request.headers
46
- request.cookies
47
- request.request_method # => "get"
48
- request.path # => "/pets/42"
49
- request.path_definition # => "/pets/{pet_id}"
50
-
51
- # Validate the request
52
- request.validate # Returns OpenapiFirst:::Failure if validation fails
53
- request.validate! # Raises OpenapiFirst::RequestInvalidError or OpenapiFirst::NotFoundError if validation fails
54
-
55
- # Find the response
56
- rack_response = Rack::Response[*app.call(env)]
57
- response = request.response(rack_response) # or definition.response(rack_request, rack_response)
58
-
59
- # Inspect the response
60
- response.known? # Is the response defined in the API description?
61
- response.status # => 200
62
- response.content_type
63
- response.body
64
- request.headers # parsed response headers
65
-
66
- # Validate response
67
- response.validate # Returns OpenapiFirst::Failure if validation fails
68
- response.validate! # Raises OpenapiFirst::ResponseInvalidError or OpenapiFirst::ResponseNotFoundError if validation fails
69
- ```
70
-
71
- OpenapiFirst uses [`multi_json`](https://rubygems.org/gems/multi_json).
72
-
73
- ## Rack Middlewares
74
-
75
- All middlewares add a _request_ object to the current Rack env at `env[OpenapiFirst::REQUEST]`), which is in an instance of `OpenapiFirst::RuntimeRequest` that responds to `.params`, `.parsed_body` etc.
76
-
77
- This gives you access to the converted request parameters and body exaclty as described in your API description instead of relying on Rack alone to parse the request. This only includes query parameters that are defined in the API description. It supports every [`style` and `explode` value as described](https://spec.openapis.org/oas/latest.html#style-examples) in the OpenAPI 3.0 and 3.1 specs.
78
-
79
- ### Request validation
80
-
81
- The request validation middleware returns a 4xx if the request is invalid or not defined in the API description.
82
-
83
- ```ruby
84
- use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml'
85
- ```
86
-
87
- #### Options
88
-
89
- | Name | Possible values | Description |
90
- | :---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
91
- | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
92
- | `raise_error:` | `false` (default), `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` or `OpenapiFirst::NotFoundError` instead of returning 4xx. |
93
- | `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` |
94
-
95
- Here in an example response body about an invalid request body. See also [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457).
96
-
97
- ```json
98
- http-status: 400
99
- content-type: "application/problem+json"
100
-
101
- {
102
- "title": "Bad Request Body",
103
- "status": 400,
104
- "errors": [
105
- {
106
- "message": "value at `/data/name` is not a string",
107
- "pointer": "/data/name",
108
- "code": "string"
109
- },
110
- {
111
- "message": "number at `/data/numberOfLegs` is less than: 2",
112
- "pointer": "/data/numberOfLegs",
113
- "code": "minimum"
114
- },
115
- {
116
- "message": "object at `/data` is missing required properties: mandatory",
117
- "pointer": "/data",
118
- "code": "required"
119
- }
120
- ]
121
- }
122
- ```
123
-
124
- openapi_first offers a [JSON:API](https://jsonapi.org/) error response as well:
125
-
126
- ```ruby
127
- use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml, error_response: :jsonapi'
128
- ```
129
-
130
- Here is an example error response:
131
-
132
- ```json
133
- // http-status: 400
134
- // content-type: "application/vnd.api+json"
135
-
136
- {
137
- "errors": [
138
- {
139
- "status": "400",
140
- "source": {
141
- "pointer": "/data/name"
142
- },
143
- "title": "value at `/data/name` is not a string",
144
- "code": "string"
145
- },
146
- {
147
- "status": "400",
148
- "source": {
149
- "pointer": "/data/numberOfLegs"
150
- },
151
- "title": "number at `/data/numberOfLegs` is less than: 2",
152
- "code": "minimum"
153
- },
154
- {
155
- "status": "400",
156
- "source": {
157
- "pointer": "/data"
158
- },
159
- "title": "object at `/data` is missing required properties: mandatory",
160
- "code": "required"
161
- }
162
- ]
163
- }
164
- ```
165
-
166
- You can build your own custom error response with `error_response: MyCustomClass` that implements `OpenapiFirst::ErrorResponse`.
167
-
168
- #### readOnly / writeOnly properties
169
-
170
- Request validation fails if request includes a property with `readOnly: true`.
171
-
172
- Response validation fails if response body includes a property with `writeOnly: true`.
173
-
174
- ### Response validation
175
-
176
- This middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
177
-
178
- ```ruby
179
- use OpenapiFirst::Middlewares::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] == 'test'
180
- ```
181
-
182
- #### Options
183
-
184
- | Name | Possible values | Description |
185
- | :------ | --------------- | ---------------------------------------------------------------- |
186
- | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
187
-
188
- ## Configuration
189
-
190
- You can configure default options globally:
191
-
192
- ```ruby
193
- OpenapiFirst.configure do |config|
194
- # Specify which plugin is used to render error responses returned by the request validation middleware (defaults to :default)
195
- config.request_validation_error_response = :jsonapi
196
- # Configure if the response validation middleware should raise an exception (defaults to false)
197
- config.request_validation_raise_error = true
198
- end
199
- ```
200
-
201
- ## Development
202
-
203
- Run `bin/setup` to install dependencies.
204
-
205
- See `bundle exec rake` to run the linter and the tests.
206
-
207
- Run `bundle exec rspec` to run the tests only.
208
-
209
- ### Benchmarks
210
-
211
- [Results](https://gist.github.com/ahx/e6ffced58bd2e8d5baffb2f4d2c1f823)
212
-
213
- Run benchmarks:
214
-
215
- ```sh
216
- cd benchmarks
217
- bundle
218
- bundle exec ruby benchmarks.rb
219
- ```
220
-
221
- ### Contributing
222
-
223
- If you have a question or an idea or found a bug don't hesitate to [create an issue](https://github.com/ahx/openapi_first/issues) or [start a discussion](https://github.com/ahx/openapi_first/discussions).
224
-
225
- Pull requests are very welcome as well, of course. Feel free to create a "draft" pull request early on, even if your change is still work in progress. 🤗
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- lib = File.expand_path('lib', __dir__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'openapi_first/version'
6
-
7
- Gem::Specification.new do |spec|
8
- spec.name = 'openapi_first'
9
- spec.version = OpenapiFirst::VERSION
10
- spec.authors = ['Andreas Haller']
11
- spec.email = ['andreas.haller@posteo.de']
12
- spec.licenses = ['MIT']
13
-
14
- spec.summary = 'Implement REST APIs based on OpenApi 3.x'
15
- spec.homepage = 'https://github.com/ahx/openapi_first'
16
-
17
- if spec.respond_to?(:metadata)
18
- spec.metadata['https://github.com/ahx/openapi_first'] = spec.homepage
19
- spec.metadata['source_code_uri'] = 'https://github.com/ahx/openapi_first'
20
- spec.metadata['changelog_uri'] = 'https://github.com/ahx/openapi_first/blob/main/CHANGELOG.md'
21
- spec.metadata['rubygems_mfa_required'] = 'true'
22
- else
23
- raise 'RubyGems 2.0 or newer is required to protect against ' \
24
- 'public gem pushes.'
25
- end
26
-
27
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`
29
- .split("\x0")
30
- .reject { |f| f.match(%r{^(test|spec|features|benchmarks|examples|bin)/}) }
31
- .reject do |f|
32
- %w[Dockerfile Jenkinsfile .tool-versions CODEOWNERS .rspec .rubocop.yml .tool-versions
33
- Rakefile].include?(f)
34
- end
35
- end
36
- spec.bindir = 'exe'
37
- spec.require_paths = ['lib']
38
-
39
- spec.required_ruby_version = '>= 3.1.1'
40
-
41
- spec.add_runtime_dependency 'json_refs', '~> 0.1', '>= 0.1.7'
42
- spec.add_runtime_dependency 'json_schemer', '~> 2.1.0'
43
- spec.add_runtime_dependency 'multi_json', '~> 1.15'
44
- spec.add_runtime_dependency 'mustermann-contrib', '~> 3.0.0'
45
- spec.add_runtime_dependency 'openapi_parameters', '>= 0.3.2', '< 2.0'
46
- spec.add_runtime_dependency 'rack', '>= 2.2', '< 4.0'
47
- end