openapi_first 0.21.0 → 1.0.0.beta3
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 +4 -4
- data/CHANGELOG.md +20 -0
- data/Gemfile.lock +14 -25
- data/README.md +31 -185
- data/benchmarks/Gemfile.lock +23 -28
- data/benchmarks/apps/openapi_first_with_hanami_api.ru +1 -2
- data/benchmarks/apps/openapi_first_with_plain_rack.ru +32 -0
- data/benchmarks/apps/openapi_first_with_response_validation.ru +14 -11
- data/benchmarks/apps/openapi_first_with_sinatra.ru +29 -0
- data/examples/app.rb +12 -16
- data/lib/openapi_first/operation.rb +18 -70
- data/lib/openapi_first/request_validation.rb +31 -76
- data/lib/openapi_first/router.rb +9 -5
- data/lib/openapi_first/utils.rb +10 -20
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +3 -53
- data/openapi_first.gemspec +2 -3
- metadata +29 -44
- data/benchmarks/apps/openapi_first.ru +0 -22
- data/lib/openapi_first/app.rb +0 -29
- data/lib/openapi_first/coverage.rb +0 -28
- data/lib/openapi_first/default_operation_resolver.rb +0 -63
- data/lib/openapi_first/inbox.rb +0 -13
- data/lib/openapi_first/rack_responder.rb +0 -12
- data/lib/openapi_first/responder.rb +0 -44
- data/lib/openapi_first/response_object.rb +0 -20
- data/lib/openapi_first/validation.rb +0 -15
- /data/benchmarks/apps/{committee.ru → committee_with_hanami_api.ru} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 800354f7ac2847fb16987e6961589aaebb84c161f3b76ea652a00a4cf8762fbe
|
4
|
+
data.tar.gz: f443b72fa8ce1103bfc5a7b30367ce96f84e0dc7ad137722424cab0ac0c13357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e454ee38c95a21915869e5feae0f38f65e0aa9a0a5001b6b752043fa379836164af1bf7eac9bcff192822d66053dda00d6dfa787296cce82bccf346d218f4c6c
|
7
|
+
data.tar.gz: 11a0a0ac1e1bbd350d1edfe1dcae4ccae15bcfe2f1863320f30f554c3ad16934b07891c578225d2d7d128c3cb7dafd4093407c1c153713567db4db3d9c99c938
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,26 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 1.0.0.beta3
|
6
|
+
|
7
|
+
- Remove obsolete dependency: deep_merge
|
8
|
+
- Remove obsolete dependency: hanami-utils
|
9
|
+
|
10
|
+
## 1.0.0.beta2
|
11
|
+
- Fixed dependencies. Remove unused code.
|
12
|
+
|
13
|
+
## 1.0.0.beta1
|
14
|
+
- Removed: `OpenapiFirst::Responder` and `OpenapiFirst::RackResponder`
|
15
|
+
- Removed: `OpenapiFirst.app` and `OpenapiFirst.middleware`
|
16
|
+
- Removed: `OpenapiFirst::Coverage`
|
17
|
+
- Breaking: Parsed query and path parameters are available at `env[OpenapiFirst::PARAMS]`(or `env['openapi.params']`) instead of `OpenapiFirst::PARAMETERS`.
|
18
|
+
- Breaking: Request body and parameters now use string keys instead of symbols!
|
19
|
+
- Breaking: Query parameters are now parsed exactly like in the API description via the openapi_parameters gem. This means a couple of things:
|
20
|
+
- Query parameters now support `explode: true` (default) and `explode: false` for array and object parameters.
|
21
|
+
- Query parameters with brackets like 'filter[tag]' are no longer deconstructed into nested hashes, but accessible via the `filter[tag]` key.
|
22
|
+
- Query parameters are no longer interpreted as `style: deepObject` by default. If you want to use `style: deepObject`, for example to pass a nested hash as a query parameter like `filter[tag]`, you have to set `style: deepObject` explicitly.
|
23
|
+
- Path parameters are now parsed exactly as in the API description via the openapi_parameters gem.
|
24
|
+
|
5
25
|
## 0.21.0
|
6
26
|
|
7
27
|
- Fix: Query parameter validation does not fail if header parameters are defined (Thanks to [JF Lalonde](https://github.com/JF-Lalonde))
|
data/Gemfile.lock
CHANGED
@@ -1,28 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openapi_first (0.
|
5
|
-
deep_merge (>= 1.2.1)
|
4
|
+
openapi_first (1.0.0.beta3)
|
6
5
|
hanami-router (~> 2.0.0)
|
7
|
-
hanami-utils (~> 2.0.0)
|
8
6
|
json_refs (~> 0.1, >= 0.1.7)
|
9
7
|
json_schemer (~> 0.2.16)
|
10
8
|
multi_json (~> 1.14)
|
11
|
-
|
9
|
+
openapi_parameters (~> 0.2)
|
10
|
+
rack (>= 2.2, < 4.0)
|
12
11
|
|
13
12
|
GEM
|
14
13
|
remote: https://rubygems.org/
|
15
14
|
specs:
|
16
15
|
ast (2.4.2)
|
17
16
|
coderay (1.1.3)
|
18
|
-
concurrent-ruby (1.2.2)
|
19
|
-
deep_merge (1.2.2)
|
20
17
|
diff-lcs (1.5.0)
|
21
|
-
dry-core (1.0.0)
|
22
|
-
concurrent-ruby (~> 1.0)
|
23
|
-
zeitwerk (~> 2.6)
|
24
|
-
dry-transformer (1.0.1)
|
25
|
-
zeitwerk (~> 2.6)
|
26
18
|
ecma-re-validator (0.4.0)
|
27
19
|
regexp_parser (~> 2.2)
|
28
20
|
hana (1.3.7)
|
@@ -30,10 +22,6 @@ GEM
|
|
30
22
|
mustermann (~> 3.0)
|
31
23
|
mustermann-contrib (~> 3.0)
|
32
24
|
rack (~> 2.0)
|
33
|
-
hanami-utils (2.0.3)
|
34
|
-
concurrent-ruby (~> 1.0)
|
35
|
-
dry-core (~> 1.0, < 2)
|
36
|
-
dry-transformer (~> 1.0, < 2)
|
37
25
|
hansi (0.2.1)
|
38
26
|
json (2.6.3)
|
39
27
|
json_refs (0.1.7)
|
@@ -50,13 +38,16 @@ GEM
|
|
50
38
|
mustermann-contrib (3.0.0)
|
51
39
|
hansi (~> 0.2.0)
|
52
40
|
mustermann (= 3.0.0)
|
41
|
+
openapi_parameters (0.2.1)
|
42
|
+
rack (>= 2.2)
|
43
|
+
zeitwerk (~> 2.6)
|
53
44
|
parallel (1.22.1)
|
54
|
-
parser (3.2.1.
|
45
|
+
parser (3.2.1.1)
|
55
46
|
ast (~> 2.4.1)
|
56
47
|
pry (0.14.2)
|
57
48
|
coderay (~> 1.1)
|
58
49
|
method_source (~> 1.0)
|
59
|
-
rack (2.2.6.
|
50
|
+
rack (2.2.6.4)
|
60
51
|
rack-test (1.1.0)
|
61
52
|
rack (>= 1.0, < 3)
|
62
53
|
rainbow (3.1.1)
|
@@ -72,23 +63,23 @@ GEM
|
|
72
63
|
rspec-expectations (3.12.2)
|
73
64
|
diff-lcs (>= 1.2.0, < 2.0)
|
74
65
|
rspec-support (~> 3.12.0)
|
75
|
-
rspec-mocks (3.12.
|
66
|
+
rspec-mocks (3.12.5)
|
76
67
|
diff-lcs (>= 1.2.0, < 2.0)
|
77
68
|
rspec-support (~> 3.12.0)
|
78
69
|
rspec-support (3.12.0)
|
79
|
-
rubocop (1.
|
70
|
+
rubocop (1.48.1)
|
80
71
|
json (~> 2.3)
|
81
72
|
parallel (~> 1.10)
|
82
73
|
parser (>= 3.2.0.0)
|
83
74
|
rainbow (>= 2.2.2, < 4.0)
|
84
75
|
regexp_parser (>= 1.8, < 3.0)
|
85
76
|
rexml (>= 3.2.5, < 4.0)
|
86
|
-
rubocop-ast (>= 1.
|
77
|
+
rubocop-ast (>= 1.26.0, < 2.0)
|
87
78
|
ruby-progressbar (~> 1.7)
|
88
79
|
unicode-display_width (>= 2.4.0, < 3.0)
|
89
|
-
rubocop-ast (1.
|
80
|
+
rubocop-ast (1.28.0)
|
90
81
|
parser (>= 3.2.1.0)
|
91
|
-
ruby-progressbar (1.
|
82
|
+
ruby-progressbar (1.13.0)
|
92
83
|
ruby2_keywords (0.0.5)
|
93
84
|
unicode-display_width (2.4.2)
|
94
85
|
uri_template (0.7.0)
|
@@ -96,8 +87,6 @@ GEM
|
|
96
87
|
|
97
88
|
PLATFORMS
|
98
89
|
arm64-darwin-21
|
99
|
-
x86_64-darwin-20
|
100
|
-
x86_64-linux
|
101
90
|
|
102
91
|
DEPENDENCIES
|
103
92
|
bundler (~> 2)
|
@@ -109,4 +98,4 @@ DEPENDENCIES
|
|
109
98
|
rubocop
|
110
99
|
|
111
100
|
BUNDLED WITH
|
112
|
-
2.3.
|
101
|
+
2.3.10
|
data/README.md
CHANGED
@@ -2,39 +2,27 @@
|
|
2
2
|
|
3
3
|
[](https://gitter.im/openapi_first/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
4
4
|
|
5
|
-
OpenapiFirst helps to implement HTTP APIs based on an [
|
5
|
+
OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description.
|
6
6
|
|
7
|
-
|
7
|
+
It provides these Rack middlewares:
|
8
8
|
|
9
|
-
|
9
|
+
- [`OpenapiFirst::RequestValidation`](#request-validation) – Validates the request against the API description and returns 400 if the request is invalid.
|
10
|
+
- [`OpenapiFirst::ResponseValidation`](#response-validation) Validates the response and raises an exception if the response body is invalid.
|
11
|
+
- [`OpenapiFirst::Router`](#openapifirstrouter) – This internal middleware is added automatically when using request/response validation. It adds the OpenAPI operation for the current request to the Rack env.
|
10
12
|
|
11
|
-
##
|
12
|
-
|
13
|
-
This gem is inspired by [committee](https://github.com/interagent/committee) (Ruby) and [connexion](https://github.com/zalando/connexion) (Python).
|
14
|
-
|
15
|
-
Here's a [comparison between committee and openapi_first](https://gist.github.com/ahx/1538c31f0652f459861713b5259e366a).
|
16
|
-
|
17
|
-
## Rack middlewares
|
18
|
-
|
19
|
-
OpenapiFirst consists of these Rack middlewares:
|
20
|
-
|
21
|
-
- [`OpenapiFirst::RequestValidation`](#OpenapiFirst::RequestValidation) – Validates the request against the API description and returns 400 if the request is invalid.
|
22
|
-
- [`OpenapiFirst::ResponseValidation`](#OpenapiFirst::ResponseValidation) Validates the response and raises an exception if the response body is invalid.
|
23
|
-
- [`OpenapiFirst::Router`](#OpenapiFirst::Router) – This internal middleware is added automatically before request/response validation. Finds the OpenAPI operation for the current request or returns 404 if no operation was found. This can be customized by adding it yourself.
|
24
|
-
|
25
|
-
|
26
|
-
And these Rack apps:
|
27
|
-
- [`OpenapiFirst::Responder`](#OpenapiFirst::Responder) calls the [handler](#handlers) found for the operation, sets the correct content-type and serializes the response body to json if needed.
|
28
|
-
- [`OpenapiFirst::RackResponder`](#OpenapiFirst::RackResponder) calls the [handler](#handlers) found for the operation as a normal Rack application (`call(env)`) and returns the result as is.
|
13
|
+
## Request Validation
|
29
14
|
|
30
|
-
|
31
|
-
|
32
|
-
This middleware returns a 400 status code with a body that describes the error if the request is not valid.
|
15
|
+
The `OpenapiFirst::RequestValidation` middleware returns a 400 status code with a body that describes the error if the request is not valid.
|
33
16
|
|
34
17
|
```ruby
|
35
18
|
use OpenapiFirst::RequestValidation, spec: 'openapi.yaml'
|
36
19
|
```
|
37
20
|
|
21
|
+
It adds these fields to the Rack env:
|
22
|
+
- `env[OpenapiFirst::PARAMS]` – The parsed parameters (query, path) for the current request (string keyed)
|
23
|
+
- `env[OpenapiFirst::REQUEST_BODY]` – The parsed request body (string keyed)
|
24
|
+
- `env[OpenapiFirst::OPERATION]` (Added via Router) – The Operation object for the current request. This is an instance of `OpenapiFirst::Operation`.
|
25
|
+
|
38
26
|
### Options and defaults
|
39
27
|
|
40
28
|
| Name | Possible values | Description | Default |
|
@@ -62,26 +50,27 @@ content-type: "application/vnd.api+json"
|
|
62
50
|
}
|
63
51
|
```
|
64
52
|
|
65
|
-
|
66
|
-
|
67
|
-
### Parameter validation
|
53
|
+
### Parameters
|
68
54
|
|
69
|
-
The middleware
|
55
|
+
The `RequestValidation` middleware adds `env[OpenapiFirst::PARAMS]` (or `env['openapi.params']` ) with the converted query and path parameters. This only includes the 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. So you can do things these:
|
70
56
|
|
71
|
-
|
72
|
-
|
73
|
-
OpenapiFirst
|
57
|
+
```ruby
|
58
|
+
# GET /pets/filter[id]=1,2,3
|
59
|
+
env[OpenapiFirst::PARAMS] # => { 'filter[id]' => [1,2,3] }
|
74
60
|
|
75
|
-
|
61
|
+
# GET /colors/.blue.black.brown?format=csv
|
62
|
+
env[OpenapiFirst::PARAMS] # => { 'color_names' => ['blue', 'black', 'brown'], 'format' => 'csv' }
|
76
63
|
|
77
|
-
|
64
|
+
# And a lot more.
|
65
|
+
```
|
78
66
|
|
79
|
-
|
67
|
+
Integration for specific webframeworks is ongoing. Don't hesitate to create an issue with you specific needs.
|
80
68
|
|
81
69
|
### Request body validation
|
82
70
|
|
71
|
+
This middleware adds the parsed request body to `env[OpenapiFirst::REQUEST_BODY]`.
|
72
|
+
|
83
73
|
The middleware will return a status `415` if the requests content type does not match or `400` if the request body is invalid.
|
84
|
-
This will also add the parsed request body to `env[OpenapiFirst::REQUEST_BODY]`.
|
85
74
|
|
86
75
|
### Header, Cookie, Path parameter validation
|
87
76
|
|
@@ -93,10 +82,9 @@ Request validation fails if request includes a property with `readOnly: true`.
|
|
93
82
|
|
94
83
|
Response validation fails if response body includes a property with `writeOnly: true`.
|
95
84
|
|
96
|
-
##
|
97
|
-
|
98
|
-
This middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
|
85
|
+
## Response validation
|
99
86
|
|
87
|
+
The `OpenapiFirst::ResponseValidation` middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
|
100
88
|
|
101
89
|
```ruby
|
102
90
|
use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] == 'test'
|
@@ -110,13 +98,13 @@ use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] ==
|
|
110
98
|
|
111
99
|
## OpenapiFirst::Router
|
112
100
|
|
113
|
-
This middleware is used automatically, but you can add it to the top of your middleware stack if you want to
|
101
|
+
This middleware is used automatically, but you can add it to the top of your middleware stack if you want to customize the behavior via options.
|
114
102
|
|
115
103
|
```ruby
|
116
104
|
use OpenapiFirst::Router, spec: './openapi/openapi.yaml'
|
117
105
|
```
|
118
106
|
|
119
|
-
This middleware adds `env[OpenapiFirst::OPERATION]` which holds an Operation object that responds to `#operation_id`, `#path` (and `#[]` to access raw fields).
|
107
|
+
This middleware adds `env[OpenapiFirst::OPERATION]` which holds an Operation object that responds to `#operation_id`, `#path` (and `#[string]` to access raw fields).
|
120
108
|
|
121
109
|
### Options and defaults
|
122
110
|
|
@@ -126,115 +114,11 @@ This middleware adds `env[OpenapiFirst::OPERATION]` which holds an Operation obj
|
|
126
114
|
| `raise_error:` | `false`, `true` | If set to true the middleware raises `OpenapiFirst::NotFoundError` when a path or method was not found in the API description. This is useful during testing to spot an incomplete API description. | `false` (don't raise an exception) |
|
127
115
|
| `not_found:` | `:continue`, `:halt` | If set to `:continue` the middleware will not return 404 (405, 415), but just pass handling the request to the next middleware or application in the Rack stack. If combined with `raise_error: true` `raise_error` gets preference and an exception is raised. | `:halt` (return 4xx response) |
|
128
116
|
|
129
|
-
##
|
130
|
-
|
131
|
-
This Rack endpoint maps the HTTP request to a method call based on the [operationId](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operation-object) in your API description and calls it as a normal Rack application.
|
132
|
-
It does not not serialize objects as JSON or adds a content-type.
|
133
|
-
|
134
|
-
```ruby
|
135
|
-
run OpenapiFirst::RackResponder
|
136
|
-
```
|
137
|
-
|
138
|
-
### Options
|
139
|
-
|
140
|
-
| Name | Description |
|
141
|
-
| :----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
142
|
-
| `namespace:` | Optional. A class or module where to find the handler method. |
|
143
|
-
| `resolver:` | Optional. An object that responds to `#call(operation)` and returns a [handler](#handlers). By default this is an instance of [DefaultOperationResolver](#OpenapiFirst::DefaultOperationResolver) |
|
144
|
-
|
145
|
-
## OpenapiFirst::Responder
|
146
|
-
|
147
|
-
This Rack endpoint maps the HTTP request to a method call based on the [operationId](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#operation-object) in your API description and calls it. Responder also adds a content-type to the response.
|
148
|
-
|
149
|
-
```ruby
|
150
|
-
run OpenapiFirst::Responder
|
151
|
-
```
|
152
|
-
|
153
|
-
### Options
|
154
|
-
|
155
|
-
| Name | Description |
|
156
|
-
| :----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
157
|
-
| `namespace:` | Optional. A class or module where to find the handler method. |
|
158
|
-
| `resolver:` | Optional. An object that responds to `#call(operation)` and returns a [handler](#handlers). By default this is an instance of [DefaultOperationResolver](#OpenapiFirst::DefaultOperationResolver) |
|
159
|
-
|
160
|
-
### OpenapiFirst::DefaultOperationResolver
|
161
|
-
|
162
|
-
This is the default way to look up a handler method for an operation. Handlers are always looked up in a namespace module that needs to be specified.
|
163
|
-
|
164
|
-
It works like this:
|
165
|
-
|
166
|
-
- An operationId "create_pet" or "createPet" or "create pet" calls `MyApi.create_pet(params, response)`
|
167
|
-
- "some_things.create" calls: `MyApi::SomeThings.create(params, response)`
|
168
|
-
- "pets#create" instantiates the class once (`MyApi::Pets::Create.new) and calls it on every request(`instance.call(params, response)`).
|
169
|
-
|
170
|
-
### Handlers
|
171
|
-
|
172
|
-
These handler methods are called with two arguments:
|
173
|
-
|
174
|
-
- `params` - Holds the parsed request body, filtered query params and path parameters (same as `env[OpenapiFirst::INBOX]`)
|
175
|
-
- `res` - Holds a Rack::Response that you can modify if needed
|
176
|
-
|
177
|
-
You can call `params.env` to access the Rack env (just like in [Hanami actions](https://guides.hanamirb.org/actions/parameters/))
|
178
|
-
|
179
|
-
There are two ways to set the response body:
|
180
|
-
|
181
|
-
- Calling `res.write "things"` (see [Rack::Response](https://www.rubydoc.info/github/rack/rack/Rack/Response))
|
182
|
-
- Returning a value which will get converted to JSON
|
183
|
-
|
184
|
-
## Standalone usage
|
185
|
-
|
186
|
-
Instead of composing these middlewares yourself you can use `OpenapiFirst.app`.
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
module Pets
|
190
|
-
def self.find_pet(params, res)
|
191
|
-
{
|
192
|
-
id: params[:id],
|
193
|
-
name: 'Oscar'
|
194
|
-
}
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# In config.ru:
|
199
|
-
require 'openapi_first'
|
200
|
-
run OpenapiFirst.app(
|
201
|
-
'./openapi/openapi.yaml',
|
202
|
-
namespace: Pets,
|
203
|
-
response_validation: ENV['RACK_ENV'] == 'test',
|
204
|
-
router_raise_error: ENV['RACK_ENV'] == 'test'
|
205
|
-
)
|
206
|
-
```
|
207
|
-
|
208
|
-
The above will use the mentioned Rack middlewares to:
|
209
|
-
|
210
|
-
- Validate the request and respond with 400 if the request does not match with your API description
|
211
|
-
- Map the request to a method call `Pets.find_pet` based on the `operationId` in the API description
|
212
|
-
- Set the response content type according to your spec (here with the default status code `200`)
|
213
|
-
|
214
|
-
### Options and defaults
|
215
|
-
|
216
|
-
| Name | Possible values | Description | Default |
|
217
|
-
| :-------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
|
218
|
-
| `spec_path` | | A filepath to an OpenAPI definition file. |
|
219
|
-
| `namespace:` | | A class or module where to find the handler methods. |
|
220
|
-
| `response_validation:` | `true`, `false` | If set to true it raises an exception if the response is invalid. This is useful during testing. | `false` |
|
221
|
-
| `router_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request path/method is not specified. This is useful during testing. | `false` |
|
222
|
-
| `request_validation_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request is not valid. | `false` |
|
223
|
-
| `resolver:` | | Option to customize finding the [handler](#handlers) method for an operation. See [OpenapiFirst::Responder](#OpenapiFirst::Responder) for details. |
|
224
|
-
|
225
|
-
Handler functions (`find_pet`) are called with two arguments:
|
226
|
-
|
227
|
-
- `params` - Holds the parsed request body, filtered query params and path parameters
|
228
|
-
- `res` - Holds a Rack::Response that you can modify if needed
|
229
|
-
If you want to access to plain Rack env you can call `params.env`.
|
230
|
-
|
231
|
-
## If your API description does not contain all endpoints
|
117
|
+
## Alternatives
|
232
118
|
|
233
|
-
|
234
|
-
run OpenapiFirst.middleware('./openapi/openapi.yaml', namespace: Pets)
|
235
|
-
```
|
119
|
+
This gem is inspired by [committee](https://github.com/interagent/committee) (Ruby) and [connexion](https://github.com/zalando/connexion) (Python).
|
236
120
|
|
237
|
-
Here
|
121
|
+
Here's a [comparison between committee and openapi_first](https://gist.github.com/ahx/1538c31f0652f459861713b5259e366a).
|
238
122
|
|
239
123
|
## Try it out
|
240
124
|
|
@@ -272,44 +156,6 @@ spec = OpenapiFirst.load('./openapi/openapi.yaml', only: { |path| path.starts_wi
|
|
272
156
|
run OpenapiFirst.app(spec, namespace: Pets)
|
273
157
|
```
|
274
158
|
|
275
|
-
## Coverage
|
276
|
-
|
277
|
-
(This is a bit experimental. Please try it out and give feedback.)
|
278
|
-
|
279
|
-
`OpenapiFirst::Coverage` helps you make sure, that you have called all endpoints of your OAS file when running tests via `rack-test`.
|
280
|
-
|
281
|
-
```ruby
|
282
|
-
# In your test (rspec example):
|
283
|
-
require 'openapi_first/coverage'
|
284
|
-
|
285
|
-
describe MyApp do
|
286
|
-
include Rack::Test::Methods
|
287
|
-
|
288
|
-
before(:all) do
|
289
|
-
@app_wrapper = OpenapiFirst::Coverage.new(MyApp, 'petstore.yaml')
|
290
|
-
end
|
291
|
-
|
292
|
-
after(:all) do
|
293
|
-
message = "The following paths have not been called yet: #{@app_wrapper.to_be_called}"
|
294
|
-
expect(@app_wrapper.to_be_called).to be_empty
|
295
|
-
end
|
296
|
-
|
297
|
-
# Overwrite `#app` to make rack-test call the wrapped app
|
298
|
-
def app
|
299
|
-
@app_wrapper
|
300
|
-
end
|
301
|
-
|
302
|
-
it 'does things' do
|
303
|
-
get '/i/my/stuff'
|
304
|
-
# …
|
305
|
-
end
|
306
|
-
end
|
307
|
-
```
|
308
|
-
|
309
|
-
## Mocking
|
310
|
-
|
311
|
-
Out of scope. Use [Prism](https://github.com/stoplightio/prism) or [fakeit](https://github.com/JustinFeng/fakeit).
|
312
|
-
|
313
159
|
## Development
|
314
160
|
|
315
161
|
Run `bin/setup` to install dependencies.
|
data/benchmarks/Gemfile.lock
CHANGED
@@ -1,24 +1,23 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ..
|
3
3
|
specs:
|
4
|
-
openapi_first (0.
|
5
|
-
deep_merge (>= 1.2.1)
|
4
|
+
openapi_first (1.0.0.beta3)
|
6
5
|
hanami-router (~> 2.0.0)
|
7
|
-
hanami-utils (~> 2.0.0)
|
8
6
|
json_refs (~> 0.1, >= 0.1.7)
|
9
7
|
json_schemer (~> 0.2.16)
|
10
8
|
multi_json (~> 1.14)
|
11
|
-
|
9
|
+
openapi_parameters (~> 0.2)
|
10
|
+
rack (>= 2.2, < 4.0)
|
12
11
|
|
13
12
|
GEM
|
14
13
|
remote: https://rubygems.org/
|
15
14
|
specs:
|
16
|
-
activesupport (7.0.4.
|
15
|
+
activesupport (7.0.4.3)
|
17
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
17
|
i18n (>= 1.6, < 2)
|
19
18
|
minitest (>= 5.1)
|
20
19
|
tzinfo (~> 2.0)
|
21
|
-
benchmark-ips (2.
|
20
|
+
benchmark-ips (2.12.0)
|
22
21
|
benchmark-memory (0.2.0)
|
23
22
|
memory_profiler (~> 1)
|
24
23
|
builder (3.2.4)
|
@@ -26,8 +25,7 @@ GEM
|
|
26
25
|
json_schema (~> 0.14, >= 0.14.3)
|
27
26
|
openapi_parser (~> 1.0)
|
28
27
|
rack (>= 1.5)
|
29
|
-
concurrent-ruby (1.2.
|
30
|
-
deep_merge (1.2.2)
|
28
|
+
concurrent-ruby (1.2.2)
|
31
29
|
dry-core (1.0.0)
|
32
30
|
concurrent-ruby (~> 1.0)
|
33
31
|
zeitwerk (~> 2.6)
|
@@ -36,13 +34,11 @@ GEM
|
|
36
34
|
concurrent-ruby (~> 1.0)
|
37
35
|
dry-core (~> 1.0, < 2)
|
38
36
|
zeitwerk (~> 2.6)
|
39
|
-
dry-
|
40
|
-
zeitwerk (~> 2.6)
|
41
|
-
dry-types (1.7.0)
|
37
|
+
dry-types (1.7.1)
|
42
38
|
concurrent-ruby (~> 1.0)
|
43
|
-
dry-core (~> 1.0
|
44
|
-
dry-inflector (~> 1.0
|
45
|
-
dry-logic (
|
39
|
+
dry-core (~> 1.0)
|
40
|
+
dry-inflector (~> 1.0)
|
41
|
+
dry-logic (~> 1.4)
|
46
42
|
zeitwerk (~> 2.6)
|
47
43
|
ecma-re-validator (0.4.0)
|
48
44
|
regexp_parser (~> 2.2)
|
@@ -60,10 +56,6 @@ GEM
|
|
60
56
|
mustermann (~> 3.0)
|
61
57
|
mustermann-contrib (~> 3.0)
|
62
58
|
rack (~> 2.0)
|
63
|
-
hanami-utils (2.0.3)
|
64
|
-
concurrent-ruby (~> 1.0)
|
65
|
-
dry-core (~> 1.0, < 2)
|
66
|
-
dry-transformer (~> 1.0, < 2)
|
67
59
|
hansi (0.2.1)
|
68
60
|
i18n (1.12.0)
|
69
61
|
concurrent-ruby (~> 1.0)
|
@@ -76,7 +68,7 @@ GEM
|
|
76
68
|
regexp_parser (~> 2.0)
|
77
69
|
uri_template (~> 0.7)
|
78
70
|
memory_profiler (1.0.1)
|
79
|
-
minitest (5.
|
71
|
+
minitest (5.18.0)
|
80
72
|
multi_json (1.15.0)
|
81
73
|
mustermann (3.0.0)
|
82
74
|
ruby2_keywords (~> 0.0.1)
|
@@ -85,29 +77,32 @@ GEM
|
|
85
77
|
mustermann (= 3.0.0)
|
86
78
|
mustermann-grape (1.0.2)
|
87
79
|
mustermann (>= 1.0.0)
|
88
|
-
nio4r (2.5.
|
80
|
+
nio4r (2.5.9)
|
81
|
+
openapi_parameters (0.2.1)
|
82
|
+
rack (>= 2.2)
|
83
|
+
zeitwerk (~> 2.6)
|
89
84
|
openapi_parser (1.0.0)
|
90
|
-
puma (6.
|
85
|
+
puma (6.2.2)
|
91
86
|
nio4r (~> 2.0)
|
92
|
-
rack (2.2.6.
|
87
|
+
rack (2.2.6.4)
|
93
88
|
rack-accept (0.4.5)
|
94
89
|
rack (>= 0.4)
|
95
|
-
rack-protection (3.0.
|
90
|
+
rack-protection (3.0.6)
|
96
91
|
rack
|
97
|
-
regexp_parser (2.
|
98
|
-
roda (3.
|
92
|
+
regexp_parser (2.8.0)
|
93
|
+
roda (3.67.0)
|
99
94
|
rack
|
100
95
|
ruby2_keywords (0.0.5)
|
101
96
|
seg (1.2.0)
|
102
|
-
sinatra (3.0.
|
97
|
+
sinatra (3.0.6)
|
103
98
|
mustermann (~> 3.0)
|
104
99
|
rack (~> 2.2, >= 2.2.4)
|
105
|
-
rack-protection (= 3.0.
|
100
|
+
rack-protection (= 3.0.6)
|
106
101
|
tilt (~> 2.0)
|
107
102
|
syro (3.2.1)
|
108
103
|
rack (>= 1.6.0)
|
109
104
|
seg
|
110
|
-
tilt (2.0
|
105
|
+
tilt (2.1.0)
|
111
106
|
tzinfo (2.0.6)
|
112
107
|
concurrent-ruby (~> 1.0)
|
113
108
|
uri_template (0.7.0)
|
@@ -19,7 +19,6 @@ app = Class.new(Hanami::API) do
|
|
19
19
|
end
|
20
20
|
end.new
|
21
21
|
|
22
|
-
use OpenapiFirst::
|
23
|
-
use OpenapiFirst::RequestValidation
|
22
|
+
use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
|
24
23
|
|
25
24
|
run app
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'multi_json'
|
4
|
+
require 'openapi_first'
|
5
|
+
|
6
|
+
app = Rack::Builder.new do
|
7
|
+
use OpenapiFirst::RequestValidation, spec: File.expand_path('./openapi.yaml', __dir__)
|
8
|
+
|
9
|
+
handlers = {
|
10
|
+
'find_thing' => lambda do |env|
|
11
|
+
params = env[OpenapiFirst::PARAMS]
|
12
|
+
body = MultiJson.dump(hello: 'world', id: params.fetch('id'))
|
13
|
+
[200, { 'Content-Type' => 'application/json' }, [body]]
|
14
|
+
end,
|
15
|
+
'find_things' => lambda do |_env|
|
16
|
+
body = MultiJson.dump(hello: 'world')
|
17
|
+
[200, { 'Content-Type' => 'application/json' }, [body]]
|
18
|
+
end,
|
19
|
+
'create_thing' => lambda do |_env|
|
20
|
+
body = MultiJson.dump(hello: 'world')
|
21
|
+
[201, { 'Content-Type' => 'application/json' }, [body]]
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
not_found = ->(_env) { [404, {}, []] }
|
26
|
+
|
27
|
+
run lambda do |env|
|
28
|
+
handlers.fetch(env[OpenapiFirst::OPERATION]&.operation_id, not_found).call(env)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
run app
|
@@ -2,21 +2,24 @@
|
|
2
2
|
|
3
3
|
require 'multi_json'
|
4
4
|
require 'openapi_first'
|
5
|
+
require 'hanami/api'
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
app = Class.new(Hanami::API) do
|
8
|
+
get '/hello/:id' do
|
9
|
+
json(hello: 'world', id: params.fetch(:id))
|
9
10
|
end
|
10
11
|
|
11
|
-
|
12
|
-
[{ hello: 'world' }]
|
12
|
+
get '/hello' do
|
13
|
+
json([{ hello: 'world' }])
|
13
14
|
end
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
post '/hello' do
|
17
|
+
status 201
|
18
|
+
json(hello: 'world')
|
18
19
|
end
|
19
|
-
end
|
20
|
+
end.new
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
|
23
|
+
use OpenapiFirst::ResponseValidation
|
24
|
+
|
25
|
+
run app
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'multi_json'
|
4
|
+
require 'openapi_first'
|
5
|
+
require 'sinatra'
|
6
|
+
|
7
|
+
class SinatraWithOpenapiFirstExample < Sinatra::Base
|
8
|
+
set :environment, :production
|
9
|
+
|
10
|
+
get '/hello/:id' do
|
11
|
+
content_type :json
|
12
|
+
MultiJson.dump(hello: 'world', id: params.fetch('id'))
|
13
|
+
end
|
14
|
+
|
15
|
+
get '/hello' do
|
16
|
+
content_type :json
|
17
|
+
MultiJson.dump([{ hello: 'world' }])
|
18
|
+
end
|
19
|
+
|
20
|
+
post '/hello' do
|
21
|
+
content_type :json
|
22
|
+
status 201
|
23
|
+
MultiJson.dump(hello: 'world')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
|
28
|
+
|
29
|
+
run SinatraWithOpenapiFirstExample
|