openapi_first 1.0.0.beta3 → 1.0.0.beta5
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/.github/workflows/ruby.yml +8 -20
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +18 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +40 -40
- data/README.md +31 -15
- data/benchmarks/Gemfile.lock +40 -28
- data/benchmarks/apps/openapi_first_with_plain_rack.ru +2 -2
- data/lib/openapi_first/body_parser_middleware.rb +4 -17
- data/lib/openapi_first/config.rb +20 -0
- data/lib/openapi_first/definition.rb +8 -1
- data/lib/openapi_first/error_response.rb +45 -0
- data/lib/openapi_first/error_responses/default.rb +58 -0
- data/lib/openapi_first/error_responses/json_api.rb +58 -0
- data/lib/openapi_first/errors.rb +3 -40
- data/lib/openapi_first/json_schema/result.rb +17 -0
- data/lib/openapi_first/json_schema.rb +47 -0
- data/lib/openapi_first/operation.rb +62 -15
- data/lib/openapi_first/plugins.rb +17 -0
- data/lib/openapi_first/request_body_validator.rb +41 -0
- data/lib/openapi_first/request_validation.rb +75 -87
- data/lib/openapi_first/request_validation_error.rb +31 -0
- data/lib/openapi_first/response_validation.rb +38 -7
- data/lib/openapi_first/response_validator.rb +2 -1
- data/lib/openapi_first/router.rb +11 -18
- data/lib/openapi_first/string_keyed_hash.rb +20 -0
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +21 -5
- data/openapi_first.gemspec +5 -10
- metadata +28 -71
- data/lib/openapi_first/schema_validation.rb +0 -46
- data/lib/openapi_first/utils.rb +0 -19
- data/lib/openapi_first/validation_format.rb +0 -55
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e6c1f8f1a6fffd91827a74f95b932bfd5be9d4a897f3704efd6313bac9e6be4
|
|
4
|
+
data.tar.gz: 59041cacdcb634bb25e5d23025c0f86afe97632918f1e10d6f67409d31ba5f9b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1955264ba1b60f477123cd1bbb71a14d611d598664965548a9ebe3c6508d5ac6e205dfe971bc7c1ebe6b27da78a48f1bf5d27239c886a9b4aa7db303224e0cfc
|
|
7
|
+
data.tar.gz: 2f25b5944e546a6619c2be01462008d358a0a80140594b0906ca62e9b152fa97b2b8225d0c137acbe29c64b7732bbd00d3f35459cd0b771b2b38c409303adde8
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -1,24 +1,12 @@
|
|
|
1
|
-
name:
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ master ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ master ]
|
|
8
|
-
|
|
1
|
+
name: Test
|
|
2
|
+
on: [push, pull_request]
|
|
9
3
|
jobs:
|
|
10
|
-
|
|
11
|
-
|
|
4
|
+
test:
|
|
12
5
|
runs-on: ubuntu-latest
|
|
13
|
-
|
|
14
6
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
16
|
-
-
|
|
17
|
-
uses: actions/setup-ruby@v1
|
|
7
|
+
- uses: actions/checkout@v3
|
|
8
|
+
- uses: ruby/setup-ruby@v1
|
|
18
9
|
with:
|
|
19
|
-
ruby-version:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
gem install bundler
|
|
23
|
-
bundle install --jobs 4 --retry 3
|
|
24
|
-
bundle exec rake
|
|
10
|
+
ruby-version: '3.1'
|
|
11
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
12
|
+
- run: bundle exec rake
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -2,15 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.0.0.beta5
|
|
6
|
+
|
|
7
|
+
- Added: `OpenapiFirst::Config.default_options=` to set default options globally
|
|
8
|
+
- Added: You can define custom error responses by subclassing `OpenapiFirst::ErrorResponse` and register it via `OpenapiFirst::Plugins.register_error_response(name, MyCustomErrorResponse)`
|
|
9
|
+
|
|
10
|
+
## 1.0.0.beta4
|
|
11
|
+
|
|
12
|
+
- Update json_schemer to version 2.0
|
|
13
|
+
- Breaking: Requires Ruby 3.1 or later
|
|
14
|
+
- Added: Parameters are available at `env[OpenapiFirst::PATH_PARAMS]`, `env[OpenapiFirst::QUERY_PARAMS]`, `env[OpenapiFirst::HEADER_PARAMS]`, `env[OpenapiFirst::COOKIE_PARAMS]` in case you need to access them separately. Merged path and query parameters are still available at `env[OpenapiFirst::PARAMS]`
|
|
15
|
+
- Breaking / Added: ResponseValidation now validates response headers
|
|
16
|
+
- Breaking / Added: RequestValidation now validates cookie, path and header parameters
|
|
17
|
+
- Breaking: multipart File uploads are now read and then validated
|
|
18
|
+
- Breaking: Remove OpenapiFirst.env method
|
|
19
|
+
- Breaking: Request validation returns 400 instead of 415 if request body is required, but empty
|
|
20
|
+
|
|
5
21
|
## 1.0.0.beta3
|
|
6
22
|
|
|
7
23
|
- Remove obsolete dependency: deep_merge
|
|
8
24
|
- Remove obsolete dependency: hanami-utils
|
|
9
25
|
|
|
10
26
|
## 1.0.0.beta2
|
|
27
|
+
|
|
11
28
|
- Fixed dependencies. Remove unused code.
|
|
12
29
|
|
|
13
30
|
## 1.0.0.beta1
|
|
31
|
+
|
|
14
32
|
- Removed: `OpenapiFirst::Responder` and `OpenapiFirst::RackResponder`
|
|
15
33
|
- Removed: `OpenapiFirst.app` and `OpenapiFirst.middleware`
|
|
16
34
|
- Removed: `OpenapiFirst::Coverage`
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
openapi_first (1.0.0.
|
|
4
|
+
openapi_first (1.0.0.beta5)
|
|
5
5
|
hanami-router (~> 2.0.0)
|
|
6
6
|
json_refs (~> 0.1, >= 0.1.7)
|
|
7
|
-
json_schemer (~> 0.
|
|
8
|
-
multi_json (~> 1.
|
|
9
|
-
openapi_parameters (
|
|
7
|
+
json_schemer (~> 2.0.0)
|
|
8
|
+
multi_json (~> 1.15)
|
|
9
|
+
openapi_parameters (>= 0.3.1, < 2.0)
|
|
10
10
|
rack (>= 2.2, < 4.0)
|
|
11
11
|
|
|
12
12
|
GEM
|
|
13
13
|
remote: https://rubygems.org/
|
|
14
14
|
specs:
|
|
15
15
|
ast (2.4.2)
|
|
16
|
-
coderay (1.1.3)
|
|
17
16
|
diff-lcs (1.5.0)
|
|
18
|
-
ecma-re-validator (0.4.0)
|
|
19
|
-
regexp_parser (~> 2.2)
|
|
20
17
|
hana (1.3.7)
|
|
21
18
|
hanami-router (2.0.2)
|
|
22
19
|
mustermann (~> 3.0)
|
|
@@ -24,77 +21,80 @@ GEM
|
|
|
24
21
|
rack (~> 2.0)
|
|
25
22
|
hansi (0.2.1)
|
|
26
23
|
json (2.6.3)
|
|
27
|
-
json_refs (0.1.
|
|
24
|
+
json_refs (0.1.8)
|
|
28
25
|
hana
|
|
29
|
-
json_schemer (0.
|
|
30
|
-
ecma-re-validator (~> 0.3)
|
|
26
|
+
json_schemer (2.0.0)
|
|
31
27
|
hana (~> 1.3)
|
|
32
28
|
regexp_parser (~> 2.0)
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
simpleidn (~> 0.2)
|
|
30
|
+
language_server-protocol (3.17.0.3)
|
|
35
31
|
multi_json (1.15.0)
|
|
36
32
|
mustermann (3.0.0)
|
|
37
33
|
ruby2_keywords (~> 0.0.1)
|
|
38
34
|
mustermann-contrib (3.0.0)
|
|
39
35
|
hansi (~> 0.2.0)
|
|
40
36
|
mustermann (= 3.0.0)
|
|
41
|
-
openapi_parameters (0.
|
|
37
|
+
openapi_parameters (0.3.1)
|
|
42
38
|
rack (>= 2.2)
|
|
43
39
|
zeitwerk (~> 2.6)
|
|
44
|
-
parallel (1.
|
|
45
|
-
parser (3.2.
|
|
40
|
+
parallel (1.23.0)
|
|
41
|
+
parser (3.2.2.4)
|
|
46
42
|
ast (~> 2.4.1)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
rack (2.
|
|
51
|
-
|
|
52
|
-
rack (>= 1.0, < 3)
|
|
43
|
+
racc
|
|
44
|
+
racc (1.7.3)
|
|
45
|
+
rack (2.2.8)
|
|
46
|
+
rack-test (2.1.0)
|
|
47
|
+
rack (>= 1.3)
|
|
53
48
|
rainbow (3.1.1)
|
|
54
|
-
rake (13.0
|
|
55
|
-
regexp_parser (2.
|
|
56
|
-
rexml (3.2.
|
|
49
|
+
rake (13.1.0)
|
|
50
|
+
regexp_parser (2.8.2)
|
|
51
|
+
rexml (3.2.6)
|
|
57
52
|
rspec (3.12.0)
|
|
58
53
|
rspec-core (~> 3.12.0)
|
|
59
54
|
rspec-expectations (~> 3.12.0)
|
|
60
55
|
rspec-mocks (~> 3.12.0)
|
|
61
|
-
rspec-core (3.12.
|
|
56
|
+
rspec-core (3.12.2)
|
|
62
57
|
rspec-support (~> 3.12.0)
|
|
63
|
-
rspec-expectations (3.12.
|
|
58
|
+
rspec-expectations (3.12.3)
|
|
64
59
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
65
60
|
rspec-support (~> 3.12.0)
|
|
66
|
-
rspec-mocks (3.12.
|
|
61
|
+
rspec-mocks (3.12.6)
|
|
67
62
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
68
63
|
rspec-support (~> 3.12.0)
|
|
69
|
-
rspec-support (3.12.
|
|
70
|
-
rubocop (1.
|
|
64
|
+
rspec-support (3.12.1)
|
|
65
|
+
rubocop (1.57.2)
|
|
71
66
|
json (~> 2.3)
|
|
67
|
+
language_server-protocol (>= 3.17.0)
|
|
72
68
|
parallel (~> 1.10)
|
|
73
|
-
parser (>= 3.2.
|
|
69
|
+
parser (>= 3.2.2.4)
|
|
74
70
|
rainbow (>= 2.2.2, < 4.0)
|
|
75
71
|
regexp_parser (>= 1.8, < 3.0)
|
|
76
72
|
rexml (>= 3.2.5, < 4.0)
|
|
77
|
-
rubocop-ast (>= 1.
|
|
73
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
|
78
74
|
ruby-progressbar (~> 1.7)
|
|
79
75
|
unicode-display_width (>= 2.4.0, < 3.0)
|
|
80
|
-
rubocop-ast (1.
|
|
76
|
+
rubocop-ast (1.30.0)
|
|
81
77
|
parser (>= 3.2.1.0)
|
|
82
78
|
ruby-progressbar (1.13.0)
|
|
83
79
|
ruby2_keywords (0.0.5)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
simpleidn (0.2.1)
|
|
81
|
+
unf (~> 0.1.4)
|
|
82
|
+
unf (0.1.4)
|
|
83
|
+
unf_ext
|
|
84
|
+
unf_ext (0.0.9)
|
|
85
|
+
unicode-display_width (2.5.0)
|
|
86
|
+
zeitwerk (2.6.12)
|
|
87
87
|
|
|
88
88
|
PLATFORMS
|
|
89
89
|
arm64-darwin-21
|
|
90
|
+
x86_64-linux
|
|
90
91
|
|
|
91
92
|
DEPENDENCIES
|
|
92
|
-
bundler
|
|
93
|
+
bundler
|
|
93
94
|
openapi_first!
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
rspec (~> 3)
|
|
95
|
+
rack-test
|
|
96
|
+
rake
|
|
97
|
+
rspec
|
|
98
98
|
rubocop
|
|
99
99
|
|
|
100
100
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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 [OpenAPI](https://www.openapis.org/) API description.
|
|
5
|
+
OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description. It supports OpenAPI 3.0 and 3.1.
|
|
6
6
|
|
|
7
7
|
It provides these Rack middlewares:
|
|
8
8
|
|
|
@@ -10,6 +10,8 @@ It provides these Rack middlewares:
|
|
|
10
10
|
- [`OpenapiFirst::ResponseValidation`](#response-validation) Validates the response and raises an exception if the response body is invalid.
|
|
11
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.
|
|
12
12
|
|
|
13
|
+
Using Request and Response validation together ensures that your implementation follows exactly the API description. This enables you to use the API description as a single source of truth for your API, reason about details and use various tooling.
|
|
14
|
+
|
|
13
15
|
## Request Validation
|
|
14
16
|
|
|
15
17
|
The `OpenapiFirst::RequestValidation` middleware returns a 400 status code with a body that describes the error if the request is not valid.
|
|
@@ -19,16 +21,18 @@ use OpenapiFirst::RequestValidation, spec: 'openapi.yaml'
|
|
|
19
21
|
```
|
|
20
22
|
|
|
21
23
|
It adds these fields to the Rack env:
|
|
24
|
+
|
|
22
25
|
- `env[OpenapiFirst::PARAMS]` – The parsed parameters (query, path) for the current request (string keyed)
|
|
23
26
|
- `env[OpenapiFirst::REQUEST_BODY]` – The parsed request body (string keyed)
|
|
24
27
|
- `env[OpenapiFirst::OPERATION]` (Added via Router) – The Operation object for the current request. This is an instance of `OpenapiFirst::Operation`.
|
|
25
28
|
|
|
26
29
|
### Options and defaults
|
|
27
30
|
|
|
28
|
-
| Name
|
|
29
|
-
|
|
|
30
|
-
| `spec:`
|
|
31
|
-
| `raise_error:`
|
|
31
|
+
| Name | Possible values | Description | Default |
|
|
32
|
+
| :---------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
33
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
|
|
34
|
+
| `raise_error:` | `false`, `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` instead of returning 4xx. | `false` (don't raise an exception) |
|
|
35
|
+
| `error_response:` | `:default`, `:json_api`, Your implementation of `ErrorResponse` | :default |
|
|
32
36
|
|
|
33
37
|
The error responses conform with [JSON:API](https://jsonapi.org).
|
|
34
38
|
|
|
@@ -36,7 +40,7 @@ Here's an example response body for a missing query parameter "search":
|
|
|
36
40
|
|
|
37
41
|
```json
|
|
38
42
|
http-status: 400
|
|
39
|
-
content-type: "application/
|
|
43
|
+
content-type: "application/json"
|
|
40
44
|
|
|
41
45
|
{
|
|
42
46
|
"errors": [
|
|
@@ -72,9 +76,10 @@ This middleware adds the parsed request body to `env[OpenapiFirst::REQUEST_BODY]
|
|
|
72
76
|
|
|
73
77
|
The middleware will return a status `415` if the requests content type does not match or `400` if the request body is invalid.
|
|
74
78
|
|
|
75
|
-
### Header, Cookie, Path parameter validation
|
|
79
|
+
### Header, Cookie, Query and Path parameter validation
|
|
76
80
|
|
|
77
|
-
|
|
81
|
+
The `RequestValidation` middleware validates the request headers, cookies and path parameters as defined in you API description. It returns a `400` status code if the request is invalid. It adds the parsed merged _path_ and _query_ parameters to `env['openapi.params']`.
|
|
82
|
+
Separate parsed parameters are made available by location at `env['openapi.path_params']`, `env['openapi.query']`, `env['openapi.headers']`, `env['openapi.cookies']` as well if you need to access them separately.
|
|
78
83
|
|
|
79
84
|
### readOnly / writeOnly properties
|
|
80
85
|
|
|
@@ -92,9 +97,9 @@ use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] ==
|
|
|
92
97
|
|
|
93
98
|
### Options
|
|
94
99
|
|
|
95
|
-
| Name
|
|
96
|
-
|
|
|
97
|
-
| `spec:`
|
|
100
|
+
| Name | Possible values | Description | Default |
|
|
101
|
+
| :------ | --------------- | ---------------------------------------------------------------- | ------- |
|
|
102
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
|
|
98
103
|
|
|
99
104
|
## OpenapiFirst::Router
|
|
100
105
|
|
|
@@ -104,16 +109,27 @@ This middleware is used automatically, but you can add it to the top of your mid
|
|
|
104
109
|
use OpenapiFirst::Router, spec: './openapi/openapi.yaml'
|
|
105
110
|
```
|
|
106
111
|
|
|
107
|
-
This middleware adds `env[
|
|
112
|
+
This middleware adds `env['openapi.operation']` which holds an instance of `OpenapiFirst::Operation` that responds to `#operation_id`, `#path` (and `#[]` to access raw fields).
|
|
108
113
|
|
|
109
114
|
### Options and defaults
|
|
110
115
|
|
|
111
116
|
| Name | Possible values | Description | Default |
|
|
112
117
|
| :------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
|
113
|
-
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load`
|
|
118
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` | |
|
|
114
119
|
| `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) |
|
|
115
120
|
| `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) |
|
|
116
121
|
|
|
122
|
+
## Global configuration
|
|
123
|
+
|
|
124
|
+
You can configure default options gobally via `OpenapiFirst::Config`:
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
OpenapiFirst::Config.default_options = {
|
|
128
|
+
error_response: :json_api,
|
|
129
|
+
request_validation_raise_error: true
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
117
133
|
## Alternatives
|
|
118
134
|
|
|
119
135
|
This gem is inspired by [committee](https://github.com/interagent/committee) (Ruby) and [connexion](https://github.com/zalando/connexion) (Python).
|
|
@@ -160,9 +176,9 @@ run OpenapiFirst.app(spec, namespace: Pets)
|
|
|
160
176
|
|
|
161
177
|
Run `bin/setup` to install dependencies.
|
|
162
178
|
|
|
163
|
-
|
|
179
|
+
See `bundle exec rake` to run the linter and the tests.
|
|
164
180
|
|
|
165
|
-
|
|
181
|
+
Run `bundle exec rspec` to run the tests only.
|
|
166
182
|
|
|
167
183
|
## Benchmarks
|
|
168
184
|
|
data/benchmarks/Gemfile.lock
CHANGED
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
openapi_first (1.0.0.
|
|
4
|
+
openapi_first (1.0.0.beta5)
|
|
5
5
|
hanami-router (~> 2.0.0)
|
|
6
6
|
json_refs (~> 0.1, >= 0.1.7)
|
|
7
|
-
json_schemer (~> 0.
|
|
8
|
-
multi_json (~> 1.
|
|
9
|
-
openapi_parameters (
|
|
7
|
+
json_schemer (~> 2.0.0)
|
|
8
|
+
multi_json (~> 1.15)
|
|
9
|
+
openapi_parameters (>= 0.3.1, < 2.0)
|
|
10
10
|
rack (>= 2.2, < 4.0)
|
|
11
11
|
|
|
12
12
|
GEM
|
|
13
13
|
remote: https://rubygems.org/
|
|
14
14
|
specs:
|
|
15
|
-
activesupport (7.
|
|
15
|
+
activesupport (7.1.2)
|
|
16
|
+
base64
|
|
17
|
+
bigdecimal
|
|
16
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
19
|
+
connection_pool (>= 2.2.5)
|
|
20
|
+
drb
|
|
17
21
|
i18n (>= 1.6, < 2)
|
|
18
22
|
minitest (>= 5.1)
|
|
23
|
+
mutex_m
|
|
19
24
|
tzinfo (~> 2.0)
|
|
25
|
+
base64 (0.2.0)
|
|
20
26
|
benchmark-ips (2.12.0)
|
|
21
27
|
benchmark-memory (0.2.0)
|
|
22
28
|
memory_profiler (~> 1)
|
|
29
|
+
bigdecimal (3.1.4)
|
|
23
30
|
builder (3.2.4)
|
|
24
31
|
committee (5.0.0)
|
|
25
32
|
json_schema (~> 0.14, >= 0.14.3)
|
|
26
33
|
openapi_parser (~> 1.0)
|
|
27
34
|
rack (>= 1.5)
|
|
28
35
|
concurrent-ruby (1.2.2)
|
|
29
|
-
|
|
36
|
+
connection_pool (2.4.1)
|
|
37
|
+
drb (2.2.0)
|
|
38
|
+
ruby2_keywords
|
|
39
|
+
dry-core (1.0.1)
|
|
30
40
|
concurrent-ruby (~> 1.0)
|
|
31
41
|
zeitwerk (~> 2.6)
|
|
32
42
|
dry-inflector (1.0.0)
|
|
@@ -40,10 +50,8 @@ GEM
|
|
|
40
50
|
dry-inflector (~> 1.0)
|
|
41
51
|
dry-logic (~> 1.4)
|
|
42
52
|
zeitwerk (~> 2.6)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
grape (1.7.0)
|
|
46
|
-
activesupport
|
|
53
|
+
grape (2.0.0)
|
|
54
|
+
activesupport (>= 5)
|
|
47
55
|
builder
|
|
48
56
|
dry-types (>= 1.1)
|
|
49
57
|
mustermann-grape (~> 1.0.0)
|
|
@@ -57,18 +65,17 @@ GEM
|
|
|
57
65
|
mustermann-contrib (~> 3.0)
|
|
58
66
|
rack (~> 2.0)
|
|
59
67
|
hansi (0.2.1)
|
|
60
|
-
i18n (1.
|
|
68
|
+
i18n (1.14.1)
|
|
61
69
|
concurrent-ruby (~> 1.0)
|
|
62
|
-
json_refs (0.1.
|
|
70
|
+
json_refs (0.1.8)
|
|
63
71
|
hana
|
|
64
72
|
json_schema (0.21.0)
|
|
65
|
-
json_schemer (0.
|
|
66
|
-
ecma-re-validator (~> 0.3)
|
|
73
|
+
json_schemer (2.0.0)
|
|
67
74
|
hana (~> 1.3)
|
|
68
75
|
regexp_parser (~> 2.0)
|
|
69
|
-
|
|
76
|
+
simpleidn (~> 0.2)
|
|
70
77
|
memory_profiler (1.0.1)
|
|
71
|
-
minitest (5.
|
|
78
|
+
minitest (5.20.0)
|
|
72
79
|
multi_json (1.15.0)
|
|
73
80
|
mustermann (3.0.0)
|
|
74
81
|
ruby2_keywords (~> 0.0.1)
|
|
@@ -77,36 +84,41 @@ GEM
|
|
|
77
84
|
mustermann (= 3.0.0)
|
|
78
85
|
mustermann-grape (1.0.2)
|
|
79
86
|
mustermann (>= 1.0.0)
|
|
87
|
+
mutex_m (0.2.0)
|
|
80
88
|
nio4r (2.5.9)
|
|
81
|
-
openapi_parameters (0.
|
|
89
|
+
openapi_parameters (0.3.1)
|
|
82
90
|
rack (>= 2.2)
|
|
83
91
|
zeitwerk (~> 2.6)
|
|
84
92
|
openapi_parser (1.0.0)
|
|
85
|
-
puma (6.
|
|
93
|
+
puma (6.4.0)
|
|
86
94
|
nio4r (~> 2.0)
|
|
87
|
-
rack (2.2.
|
|
95
|
+
rack (2.2.8)
|
|
88
96
|
rack-accept (0.4.5)
|
|
89
97
|
rack (>= 0.4)
|
|
90
|
-
rack-protection (3.0
|
|
91
|
-
rack
|
|
92
|
-
regexp_parser (2.8.
|
|
93
|
-
roda (3.
|
|
98
|
+
rack-protection (3.1.0)
|
|
99
|
+
rack (~> 2.2, >= 2.2.4)
|
|
100
|
+
regexp_parser (2.8.2)
|
|
101
|
+
roda (3.73.0)
|
|
94
102
|
rack
|
|
95
103
|
ruby2_keywords (0.0.5)
|
|
96
104
|
seg (1.2.0)
|
|
97
|
-
|
|
105
|
+
simpleidn (0.2.1)
|
|
106
|
+
unf (~> 0.1.4)
|
|
107
|
+
sinatra (3.1.0)
|
|
98
108
|
mustermann (~> 3.0)
|
|
99
109
|
rack (~> 2.2, >= 2.2.4)
|
|
100
|
-
rack-protection (= 3.0
|
|
110
|
+
rack-protection (= 3.1.0)
|
|
101
111
|
tilt (~> 2.0)
|
|
102
112
|
syro (3.2.1)
|
|
103
113
|
rack (>= 1.6.0)
|
|
104
114
|
seg
|
|
105
|
-
tilt (2.
|
|
115
|
+
tilt (2.3.0)
|
|
106
116
|
tzinfo (2.0.6)
|
|
107
117
|
concurrent-ruby (~> 1.0)
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
unf (0.1.4)
|
|
119
|
+
unf_ext
|
|
120
|
+
unf_ext (0.0.9)
|
|
121
|
+
zeitwerk (2.6.12)
|
|
110
122
|
|
|
111
123
|
PLATFORMS
|
|
112
124
|
arm64-darwin-21
|
|
@@ -4,29 +4,16 @@ require 'multi_json'
|
|
|
4
4
|
|
|
5
5
|
module OpenapiFirst
|
|
6
6
|
class BodyParserMiddleware
|
|
7
|
-
def initialize(app
|
|
7
|
+
def initialize(app)
|
|
8
8
|
@app = app
|
|
9
|
-
@raise = options.fetch(:raise_error, false)
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
RACK_INPUT = 'rack.input'
|
|
13
11
|
ROUTER_PARSED_BODY = 'router.parsed_body'
|
|
12
|
+
private_constant :ROUTER_PARSED_BODY
|
|
14
13
|
|
|
15
14
|
def call(env)
|
|
16
15
|
env[ROUTER_PARSED_BODY] = parse_body(env)
|
|
17
16
|
@app.call(env)
|
|
18
|
-
rescue BodyParsingError => e
|
|
19
|
-
raise if @raise
|
|
20
|
-
|
|
21
|
-
err = { title: "Failed to parse body as #{env['CONTENT_TYPE']}", status: '400' }
|
|
22
|
-
err[:detail] = e.cause unless ENV['RACK_ENV'] == 'production'
|
|
23
|
-
errors = [err]
|
|
24
|
-
|
|
25
|
-
Rack::Response.new(
|
|
26
|
-
MultiJson.dump(errors: errors),
|
|
27
|
-
400,
|
|
28
|
-
Rack::CONTENT_TYPE => 'application/vnd.api+json'
|
|
29
|
-
).finish
|
|
30
17
|
end
|
|
31
18
|
|
|
32
19
|
private
|
|
@@ -40,8 +27,8 @@ module OpenapiFirst
|
|
|
40
27
|
return request.POST if request.form_data?
|
|
41
28
|
|
|
42
29
|
body
|
|
43
|
-
rescue MultiJson::ParseError
|
|
44
|
-
raise BodyParsingError,
|
|
30
|
+
rescue MultiJson::ParseError
|
|
31
|
+
raise BodyParsingError, 'Failed to parse body as application/json'
|
|
45
32
|
end
|
|
46
33
|
|
|
47
34
|
def read_body(request)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiFirst
|
|
4
|
+
class Config
|
|
5
|
+
def initialize(error_response: :default, request_validation_raise_error: false)
|
|
6
|
+
@error_response = Plugins.find_error_response(error_response)
|
|
7
|
+
@request_validation_raise_error = request_validation_raise_error
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :error_response, :request_validation_raise_error
|
|
11
|
+
|
|
12
|
+
def self.default_options
|
|
13
|
+
@default_options ||= new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.default_options=(options)
|
|
17
|
+
@default_options = new(**options)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative 'operation'
|
|
4
4
|
|
|
5
5
|
module OpenapiFirst
|
|
6
|
+
# Represents an OpenAPI API Description document
|
|
6
7
|
class Definition
|
|
7
8
|
attr_reader :filepath, :operations
|
|
8
9
|
|
|
@@ -11,9 +12,15 @@ module OpenapiFirst
|
|
|
11
12
|
methods = %w[get head post put patch delete trace options]
|
|
12
13
|
@operations = resolved['paths'].flat_map do |path, path_item|
|
|
13
14
|
path_item.slice(*methods).map do |request_method, _operation_object|
|
|
14
|
-
Operation.new(path, request_method, path_item)
|
|
15
|
+
Operation.new(path, request_method, path_item, openapi_version: detect_version(resolved))
|
|
15
16
|
end
|
|
16
17
|
end
|
|
17
18
|
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def detect_version(resolved)
|
|
23
|
+
(resolved['openapi'] || resolved['swagger'])[0..2]
|
|
24
|
+
end
|
|
18
25
|
end
|
|
19
26
|
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiFirst
|
|
4
|
+
# This is the base class for error responses
|
|
5
|
+
class ErrorResponse
|
|
6
|
+
## @param request [Hash] The Rack request env
|
|
7
|
+
## @param request_validation_error [OpenapiFirst::RequestValidationError]
|
|
8
|
+
def initialize(env, request_validation_error)
|
|
9
|
+
@env = env
|
|
10
|
+
@request_validation_error = request_validation_error
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
extend Forwardable
|
|
14
|
+
|
|
15
|
+
attr_reader :env, :request_validation_error
|
|
16
|
+
|
|
17
|
+
def_delegators :@request_validation_error, :status, :location, :schema_validation
|
|
18
|
+
|
|
19
|
+
def validation_output
|
|
20
|
+
schema_validation&.output
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def schema
|
|
24
|
+
schema_validation&.schema
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def data
|
|
28
|
+
schema_validation&.data
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def message
|
|
32
|
+
request_validation_error.message
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def render
|
|
36
|
+
Rack::Response.new(body, status, Rack::CONTENT_TYPE => content_type).finish
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def content_type = 'application/json'
|
|
40
|
+
|
|
41
|
+
def body
|
|
42
|
+
raise NotImplementedError
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|