openapi_first 1.0.0.beta3 → 1.0.0.beta4
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 +13 -0
- data/Gemfile +4 -1
- data/Gemfile.lock +39 -37
- data/README.md +17 -11
- data/benchmarks/Gemfile.lock +21 -20
- data/benchmarks/apps/openapi_first_with_plain_rack.ru +2 -2
- data/lib/openapi_first/body_parser_middleware.rb +1 -1
- data/lib/openapi_first/config.rb +19 -0
- data/lib/openapi_first/default_error_response.rb +47 -0
- data/lib/openapi_first/definition.rb +8 -1
- data/lib/openapi_first/error_response.rb +31 -0
- data/lib/openapi_first/errors.rb +3 -40
- data/lib/openapi_first/operation.rb +33 -14
- data/lib/openapi_first/operation_schemas.rb +52 -0
- 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 +66 -84
- data/lib/openapi_first/response_validation.rb +38 -7
- data/lib/openapi_first/response_validator.rb +1 -1
- data/lib/openapi_first/router.rb +15 -14
- data/lib/openapi_first/schema_validation.rb +22 -21
- data/lib/openapi_first/string_keyed_hash.rb +20 -0
- data/lib/openapi_first/validation_result.rb +15 -0
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +30 -3
- data/openapi_first.gemspec +4 -9
- metadata +16 -66
- 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: c02c053192b6b39cb8f05acd35f7a257ee98b449c49f8ceeec4ac6e542f09e15
|
4
|
+
data.tar.gz: c36d3598b263ebd3d1a9bd23e6aa0b66256890f68e2f70fe245ca932d2f004f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e70192d7c2cb58734b8ebb6ccf53b2f243a98e78adbea271a3b0df1468f400d60080ead1ea68c07dd33bd9985d67de02ec4e0200e3ca0d49272a893c97611ffb
|
7
|
+
data.tar.gz: c59e62cd24dd767330f7e9ce6ad96a9c13005a4498c39f3ccf10bc801b49b6bddccf3ac62f858ae3ecf56fa4c695b4ad010fea7292171f98b640dec968ea357d
|
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,28 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 1.0.0.beta4
|
6
|
+
|
7
|
+
- Update json_schemer to version 2.0
|
8
|
+
- Breaking: Requires Ruby 3.1 or later
|
9
|
+
- 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]`
|
10
|
+
- Breaking / Added: ResponseValidation now validates response headers
|
11
|
+
- Breaking / Added: RequestValidation now validates cookie, path and header parameters
|
12
|
+
- Breaking: multipart File uploads are now read and then validated
|
13
|
+
- Breaking: Remove OpenapiFirst.env method
|
14
|
+
- Breaking: Request validation returns 400 instead of 415 if request body is required, but empty
|
15
|
+
|
5
16
|
## 1.0.0.beta3
|
6
17
|
|
7
18
|
- Remove obsolete dependency: deep_merge
|
8
19
|
- Remove obsolete dependency: hanami-utils
|
9
20
|
|
10
21
|
## 1.0.0.beta2
|
22
|
+
|
11
23
|
- Fixed dependencies. Remove unused code.
|
12
24
|
|
13
25
|
## 1.0.0.beta1
|
26
|
+
|
14
27
|
- Removed: `OpenapiFirst::Responder` and `OpenapiFirst::RackResponder`
|
15
28
|
- Removed: `OpenapiFirst.app` and `OpenapiFirst.middleware`
|
16
29
|
- Removed: `OpenapiFirst::Coverage`
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,22 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openapi_first (1.0.0.
|
4
|
+
openapi_first (1.0.0.beta4)
|
5
5
|
hanami-router (~> 2.0.0)
|
6
6
|
json_refs (~> 0.1, >= 0.1.7)
|
7
|
-
json_schemer (~> 0.
|
7
|
+
json_schemer (~> 2.0.0)
|
8
8
|
multi_json (~> 1.14)
|
9
|
-
openapi_parameters (~> 0.2)
|
9
|
+
openapi_parameters (~> 0.2.2)
|
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
|
-
|
16
|
+
base64 (0.1.1)
|
17
17
|
diff-lcs (1.5.0)
|
18
|
-
ecma-re-validator (0.4.0)
|
19
|
-
regexp_parser (~> 2.2)
|
20
18
|
hana (1.3.7)
|
21
19
|
hanami-router (2.0.2)
|
22
20
|
mustermann (~> 3.0)
|
@@ -24,77 +22,81 @@ GEM
|
|
24
22
|
rack (~> 2.0)
|
25
23
|
hansi (0.2.1)
|
26
24
|
json (2.6.3)
|
27
|
-
json_refs (0.1.
|
25
|
+
json_refs (0.1.8)
|
28
26
|
hana
|
29
|
-
json_schemer (0.
|
30
|
-
ecma-re-validator (~> 0.3)
|
27
|
+
json_schemer (2.0.0)
|
31
28
|
hana (~> 1.3)
|
32
29
|
regexp_parser (~> 2.0)
|
33
|
-
|
34
|
-
|
30
|
+
simpleidn (~> 0.2)
|
31
|
+
language_server-protocol (3.17.0.3)
|
35
32
|
multi_json (1.15.0)
|
36
33
|
mustermann (3.0.0)
|
37
34
|
ruby2_keywords (~> 0.0.1)
|
38
35
|
mustermann-contrib (3.0.0)
|
39
36
|
hansi (~> 0.2.0)
|
40
37
|
mustermann (= 3.0.0)
|
41
|
-
openapi_parameters (0.2.
|
38
|
+
openapi_parameters (0.2.2)
|
42
39
|
rack (>= 2.2)
|
43
40
|
zeitwerk (~> 2.6)
|
44
|
-
parallel (1.
|
45
|
-
parser (3.2.
|
41
|
+
parallel (1.23.0)
|
42
|
+
parser (3.2.2.3)
|
46
43
|
ast (~> 2.4.1)
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
rack (2.
|
51
|
-
|
52
|
-
rack (>= 1.0, < 3)
|
44
|
+
racc
|
45
|
+
racc (1.7.1)
|
46
|
+
rack (2.2.8)
|
47
|
+
rack-test (2.1.0)
|
48
|
+
rack (>= 1.3)
|
53
49
|
rainbow (3.1.1)
|
54
50
|
rake (13.0.6)
|
55
|
-
regexp_parser (2.
|
56
|
-
rexml (3.2.
|
51
|
+
regexp_parser (2.8.1)
|
52
|
+
rexml (3.2.6)
|
57
53
|
rspec (3.12.0)
|
58
54
|
rspec-core (~> 3.12.0)
|
59
55
|
rspec-expectations (~> 3.12.0)
|
60
56
|
rspec-mocks (~> 3.12.0)
|
61
|
-
rspec-core (3.12.
|
57
|
+
rspec-core (3.12.2)
|
62
58
|
rspec-support (~> 3.12.0)
|
63
|
-
rspec-expectations (3.12.
|
59
|
+
rspec-expectations (3.12.3)
|
64
60
|
diff-lcs (>= 1.2.0, < 2.0)
|
65
61
|
rspec-support (~> 3.12.0)
|
66
|
-
rspec-mocks (3.12.
|
62
|
+
rspec-mocks (3.12.6)
|
67
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
68
64
|
rspec-support (~> 3.12.0)
|
69
|
-
rspec-support (3.12.
|
70
|
-
rubocop (1.
|
65
|
+
rspec-support (3.12.1)
|
66
|
+
rubocop (1.56.3)
|
67
|
+
base64 (~> 0.1.1)
|
71
68
|
json (~> 2.3)
|
69
|
+
language_server-protocol (>= 3.17.0)
|
72
70
|
parallel (~> 1.10)
|
73
|
-
parser (>= 3.2.
|
71
|
+
parser (>= 3.2.2.3)
|
74
72
|
rainbow (>= 2.2.2, < 4.0)
|
75
73
|
regexp_parser (>= 1.8, < 3.0)
|
76
74
|
rexml (>= 3.2.5, < 4.0)
|
77
|
-
rubocop-ast (>= 1.
|
75
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
78
76
|
ruby-progressbar (~> 1.7)
|
79
77
|
unicode-display_width (>= 2.4.0, < 3.0)
|
80
|
-
rubocop-ast (1.
|
78
|
+
rubocop-ast (1.29.0)
|
81
79
|
parser (>= 3.2.1.0)
|
82
80
|
ruby-progressbar (1.13.0)
|
83
81
|
ruby2_keywords (0.0.5)
|
82
|
+
simpleidn (0.2.1)
|
83
|
+
unf (~> 0.1.4)
|
84
|
+
unf (0.1.4)
|
85
|
+
unf_ext
|
86
|
+
unf_ext (0.0.8.2)
|
84
87
|
unicode-display_width (2.4.2)
|
85
|
-
|
86
|
-
zeitwerk (2.6.7)
|
88
|
+
zeitwerk (2.6.11)
|
87
89
|
|
88
90
|
PLATFORMS
|
89
91
|
arm64-darwin-21
|
92
|
+
x86_64-linux
|
90
93
|
|
91
94
|
DEPENDENCIES
|
92
|
-
bundler
|
95
|
+
bundler
|
93
96
|
openapi_first!
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
rspec (~> 3)
|
97
|
+
rack-test
|
98
|
+
rake
|
99
|
+
rspec
|
98
100
|
rubocop
|
99
101
|
|
100
102
|
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,6 +21,7 @@ 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`.
|
@@ -27,8 +30,9 @@ It adds these fields to the Rack env:
|
|
27
30
|
|
28
31
|
| Name | Possible values | Description | Default |
|
29
32
|
| :------------- | --------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
30
|
-
| `spec:` |
|
33
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
|
31
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`, Your implementation of `ErrorResponse` | :default
|
32
36
|
|
33
37
|
The error responses conform with [JSON:API](https://jsonapi.org).
|
34
38
|
|
@@ -50,6 +54,7 @@ content-type: "application/vnd.api+json"
|
|
50
54
|
}
|
51
55
|
```
|
52
56
|
|
57
|
+
|
53
58
|
### Parameters
|
54
59
|
|
55
60
|
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,9 +77,10 @@ This middleware adds the parsed request body to `env[OpenapiFirst::REQUEST_BODY]
|
|
72
77
|
|
73
78
|
The middleware will return a status `415` if the requests content type does not match or `400` if the request body is invalid.
|
74
79
|
|
75
|
-
### Header, Cookie, Path parameter validation
|
80
|
+
### Header, Cookie, Query and Path parameter validation
|
76
81
|
|
77
|
-
|
82
|
+
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']`.
|
83
|
+
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
84
|
|
79
85
|
### readOnly / writeOnly properties
|
80
86
|
|
@@ -92,9 +98,9 @@ use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] ==
|
|
92
98
|
|
93
99
|
### Options
|
94
100
|
|
95
|
-
| Name
|
96
|
-
|
|
97
|
-
| `spec:`
|
101
|
+
| Name | Possible values | Description | Default |
|
102
|
+
| :------ | --------------- | ---------------------------------------------------------------- | ------- |
|
103
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
|
98
104
|
|
99
105
|
## OpenapiFirst::Router
|
100
106
|
|
@@ -104,13 +110,13 @@ This middleware is used automatically, but you can add it to the top of your mid
|
|
104
110
|
use OpenapiFirst::Router, spec: './openapi/openapi.yaml'
|
105
111
|
```
|
106
112
|
|
107
|
-
This middleware adds `env[
|
113
|
+
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
114
|
|
109
115
|
### Options and defaults
|
110
116
|
|
111
117
|
| Name | Possible values | Description | Default |
|
112
118
|
| :------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
|
113
|
-
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load`
|
119
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` | |
|
114
120
|
| `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
121
|
| `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
122
|
|
@@ -160,9 +166,9 @@ run OpenapiFirst.app(spec, namespace: Pets)
|
|
160
166
|
|
161
167
|
Run `bin/setup` to install dependencies.
|
162
168
|
|
163
|
-
|
169
|
+
See `bundle exec rake` to run the linter and the tests.
|
164
170
|
|
165
|
-
|
171
|
+
Run `bundle exec rspec` to run the tests only.
|
166
172
|
|
167
173
|
## Benchmarks
|
168
174
|
|
data/benchmarks/Gemfile.lock
CHANGED
@@ -4,15 +4,15 @@ PATH
|
|
4
4
|
openapi_first (1.0.0.beta3)
|
5
5
|
hanami-router (~> 2.0.0)
|
6
6
|
json_refs (~> 0.1, >= 0.1.7)
|
7
|
-
json_schemer (~> 0.
|
7
|
+
json_schemer (~> 2.0.0)
|
8
8
|
multi_json (~> 1.14)
|
9
|
-
openapi_parameters (~> 0.2)
|
9
|
+
openapi_parameters (~> 0.2.2)
|
10
10
|
rack (>= 2.2, < 4.0)
|
11
11
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
|
-
activesupport (7.0.
|
15
|
+
activesupport (7.0.8)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 1.6, < 2)
|
18
18
|
minitest (>= 5.1)
|
@@ -40,14 +40,12 @@ GEM
|
|
40
40
|
dry-inflector (~> 1.0)
|
41
41
|
dry-logic (~> 1.4)
|
42
42
|
zeitwerk (~> 2.6)
|
43
|
-
|
44
|
-
regexp_parser (~> 2.2)
|
45
|
-
grape (1.7.0)
|
43
|
+
grape (1.7.1)
|
46
44
|
activesupport
|
47
45
|
builder
|
48
46
|
dry-types (>= 1.1)
|
49
47
|
mustermann-grape (~> 1.0.0)
|
50
|
-
rack (>= 1.3.0)
|
48
|
+
rack (>= 1.3.0, < 3)
|
51
49
|
rack-accept
|
52
50
|
hana (1.3.7)
|
53
51
|
hanami-api (0.3.0)
|
@@ -57,18 +55,17 @@ GEM
|
|
57
55
|
mustermann-contrib (~> 3.0)
|
58
56
|
rack (~> 2.0)
|
59
57
|
hansi (0.2.1)
|
60
|
-
i18n (1.
|
58
|
+
i18n (1.14.1)
|
61
59
|
concurrent-ruby (~> 1.0)
|
62
|
-
json_refs (0.1.
|
60
|
+
json_refs (0.1.8)
|
63
61
|
hana
|
64
62
|
json_schema (0.21.0)
|
65
|
-
json_schemer (0.
|
66
|
-
ecma-re-validator (~> 0.3)
|
63
|
+
json_schemer (2.0.0)
|
67
64
|
hana (~> 1.3)
|
68
65
|
regexp_parser (~> 2.0)
|
69
|
-
|
66
|
+
simpleidn (~> 0.2)
|
70
67
|
memory_profiler (1.0.1)
|
71
|
-
minitest (5.
|
68
|
+
minitest (5.20.0)
|
72
69
|
multi_json (1.15.0)
|
73
70
|
mustermann (3.0.0)
|
74
71
|
ruby2_keywords (~> 0.0.1)
|
@@ -78,22 +75,24 @@ GEM
|
|
78
75
|
mustermann-grape (1.0.2)
|
79
76
|
mustermann (>= 1.0.0)
|
80
77
|
nio4r (2.5.9)
|
81
|
-
openapi_parameters (0.2.
|
78
|
+
openapi_parameters (0.2.2)
|
82
79
|
rack (>= 2.2)
|
83
80
|
zeitwerk (~> 2.6)
|
84
81
|
openapi_parser (1.0.0)
|
85
|
-
puma (6.
|
82
|
+
puma (6.3.1)
|
86
83
|
nio4r (~> 2.0)
|
87
|
-
rack (2.2.
|
84
|
+
rack (2.2.8)
|
88
85
|
rack-accept (0.4.5)
|
89
86
|
rack (>= 0.4)
|
90
87
|
rack-protection (3.0.6)
|
91
88
|
rack
|
92
|
-
regexp_parser (2.8.
|
93
|
-
roda (3.
|
89
|
+
regexp_parser (2.8.1)
|
90
|
+
roda (3.68.0)
|
94
91
|
rack
|
95
92
|
ruby2_keywords (0.0.5)
|
96
93
|
seg (1.2.0)
|
94
|
+
simpleidn (0.2.1)
|
95
|
+
unf (~> 0.1.4)
|
97
96
|
sinatra (3.0.6)
|
98
97
|
mustermann (~> 3.0)
|
99
98
|
rack (~> 2.2, >= 2.2.4)
|
@@ -105,8 +104,10 @@ GEM
|
|
105
104
|
tilt (2.1.0)
|
106
105
|
tzinfo (2.0.6)
|
107
106
|
concurrent-ruby (~> 1.0)
|
108
|
-
|
109
|
-
|
107
|
+
unf (0.1.4)
|
108
|
+
unf_ext
|
109
|
+
unf_ext (0.0.8.2)
|
110
|
+
zeitwerk (2.6.11)
|
110
111
|
|
111
112
|
PLATFORMS
|
112
113
|
arm64-darwin-21
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenapiFirst
|
4
|
+
class Config
|
5
|
+
def initialize(error_response: :default)
|
6
|
+
@error_response = error_response
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :error_response
|
10
|
+
|
11
|
+
def self.default_options
|
12
|
+
@default_options ||= new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.default_options=(options)
|
16
|
+
@default_options = new(**options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenapiFirst
|
4
|
+
class DefaultErrorResponse < ErrorResponse
|
5
|
+
OpenapiFirst::Plugins.register_error_response(:default, self)
|
6
|
+
|
7
|
+
def body
|
8
|
+
MultiJson.dump({ errors: serialized_errors })
|
9
|
+
end
|
10
|
+
|
11
|
+
def serialized_errors
|
12
|
+
return default_errors unless validation_output
|
13
|
+
|
14
|
+
key = pointer_key
|
15
|
+
[
|
16
|
+
{
|
17
|
+
source: { key => pointer(validation_output['instanceLocation']) },
|
18
|
+
title: validation_output['error']
|
19
|
+
}
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_errors
|
24
|
+
[{
|
25
|
+
status: status.to_s,
|
26
|
+
title:
|
27
|
+
}]
|
28
|
+
end
|
29
|
+
|
30
|
+
def pointer_key
|
31
|
+
case location
|
32
|
+
when :request_body
|
33
|
+
:pointer
|
34
|
+
when :query, :path
|
35
|
+
:parameter
|
36
|
+
else
|
37
|
+
location
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def pointer(data_pointer)
|
42
|
+
return data_pointer if location == :request_body
|
43
|
+
|
44
|
+
data_pointer.delete_prefix('/')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
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,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OpenapiFirst
|
4
|
+
# This is the base class for error responses
|
5
|
+
class ErrorResponse
|
6
|
+
## @param status [Integer] The HTTP status code.
|
7
|
+
## @param title [String] The title of the error. Usually the name of the HTTP status code.
|
8
|
+
## @param location [Symbol] The location of the error (:request_body, :query, :header, :cookie, :path).
|
9
|
+
## @param validation_result [ValidationResult]
|
10
|
+
def initialize(status:, location:, title:, validation_result:)
|
11
|
+
@status = status
|
12
|
+
@title = title
|
13
|
+
@location = location
|
14
|
+
@validation_output = validation_result&.output
|
15
|
+
@schema = validation_result&.schema
|
16
|
+
@data = validation_result&.data
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :status, :location, :title, :schema, :data, :validation_output
|
20
|
+
|
21
|
+
def render
|
22
|
+
Rack::Response.new(body, status, Rack::CONTENT_TYPE => content_type).finish
|
23
|
+
end
|
24
|
+
|
25
|
+
def content_type = 'application/json'
|
26
|
+
|
27
|
+
def body
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/openapi_first/errors.rb
CHANGED
@@ -5,15 +5,6 @@ module OpenapiFirst
|
|
5
5
|
|
6
6
|
class NotFoundError < Error; end
|
7
7
|
|
8
|
-
class HandlerNotFoundError < Error; end
|
9
|
-
|
10
|
-
class NotImplementedError < Error
|
11
|
-
def initialize(message)
|
12
|
-
warn 'NotImplementedError is deprecated. Handle HandlerNotFoundError instead'
|
13
|
-
super
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
8
|
class ResponseInvalid < Error; end
|
18
9
|
|
19
10
|
class ResponseCodeNotFoundError < ResponseInvalid; end
|
@@ -22,37 +13,9 @@ module OpenapiFirst
|
|
22
13
|
|
23
14
|
class ResponseBodyInvalidError < ResponseInvalid; end
|
24
15
|
|
16
|
+
class ResponseHeaderInvalidError < ResponseInvalid; end
|
17
|
+
|
25
18
|
class BodyParsingError < Error; end
|
26
19
|
|
27
|
-
class RequestInvalidError < Error
|
28
|
-
def initialize(serialized_errors)
|
29
|
-
message = error_message(serialized_errors)
|
30
|
-
super message
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def error_message(errors)
|
36
|
-
errors.map do |error|
|
37
|
-
[human_source(error), human_error(error)].compact.join(' ')
|
38
|
-
end.join(', ')
|
39
|
-
end
|
40
|
-
|
41
|
-
def human_source(error)
|
42
|
-
return unless error[:source]
|
43
|
-
|
44
|
-
source_key = error[:source].keys.first
|
45
|
-
source = {
|
46
|
-
pointer: 'Request body invalid:',
|
47
|
-
parameter: 'Query parameter invalid:'
|
48
|
-
}.fetch(source_key, source_key)
|
49
|
-
name = error[:source].values.first
|
50
|
-
source += " #{name}" unless name.nil? || name.empty?
|
51
|
-
source
|
52
|
-
end
|
53
|
-
|
54
|
-
def human_error(error)
|
55
|
-
error[:title]
|
56
|
-
end
|
57
|
-
end
|
20
|
+
class RequestInvalidError < Error; end
|
58
21
|
end
|