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