openapi_first 1.0.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +77 -10
- data/Gemfile.rack2.lock +2 -6
- data/README.md +72 -15
- data/lib/openapi_first/body_parser.rb +7 -2
- data/lib/openapi_first/definition/operation.rb +48 -28
- data/lib/openapi_first/definition/request_body.rb +28 -26
- data/lib/openapi_first/definition.rb +3 -3
- data/lib/openapi_first/errors.rb +1 -0
- data/lib/openapi_first/failure.rb +16 -3
- data/lib/openapi_first/middlewares/response_validation.rb +6 -3
- data/lib/openapi_first/plugins/default/error_response.rb +1 -1
- data/lib/openapi_first/plugins/jsonapi/error_response.rb +2 -1
- data/lib/openapi_first/request_validation/validator.rb +13 -13
- data/lib/openapi_first/response_validation/validator.rb +22 -26
- data/lib/openapi_first/runtime_request.rb +19 -8
- data/lib/openapi_first/runtime_response.rb +47 -3
- data/lib/openapi_first/schema/validation_result.rb +0 -7
- data/lib/openapi_first/schema.rb +0 -4
- data/lib/openapi_first/version.rb +1 -1
- data/lib/openapi_first.rb +13 -3
- data/openapi_first.gemspec +1 -1
- metadata +3 -8
- data/lib/openapi_first/definition/cookie_parameters.rb +0 -12
- data/lib/openapi_first/definition/header_parameters.rb +0 -12
- data/lib/openapi_first/definition/parameters.rb +0 -47
- data/lib/openapi_first/definition/path_parameters.rb +0 -12
- data/lib/openapi_first/definition/query_parameters.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ee379d839bec13bd899f690b3275d761b7425461f2241da4ccb62de384f0c94
|
4
|
+
data.tar.gz: 6ea27d437c7cb443d5b5c84b8e6d0ef34782e4f5d8bb5ce7666acc78797ca523
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab20a8bcd7d61961f4a1d654a36291f295ee0d69c50829cd8f1965998ca9a480a0636e2b0ca62d220a53c62b60d8bedb934fc8a57bed39a8e45683796a5cee8a
|
7
|
+
data.tar.gz: 43206c1bf540193863bd1f02c14f79d172fff662974c8efbfd90dfd6334f11f23c0dee62c9c70c4bd45bbf202e68a76c9750a00aa4a45f5dd8682b6837a1177b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.2.0
|
4
|
+
|
5
|
+
- Added `OpenapiFirst.parse(hash)` to load ("parse") a resolved/de-referenced Hash
|
6
|
+
- Added support for unescaped special characters in the path params (https://github.com/ahx/openapi_first/pull/217)
|
7
|
+
- Added `operation` to `RuntimeRequest` by [@MrBananaLord](https://github.com/ahx/openapi_first/pull/216)
|
8
|
+
|
9
|
+
## 1.1.1
|
10
|
+
|
11
|
+
- Fix reading response body for example when running Rails (`ActionDispatch::Response::RackBody`)
|
12
|
+
- Add `known?`, `status`, `body`, `headers`, `content_type` methods to inspect the parsed response (`RuntimeResponse`)
|
13
|
+
- Add `OpenapiFirst::ParseError` which is raised by low-level interfaces like `request.body` if the body could not be parsed.
|
14
|
+
- Add "code" field to errors in JSON:API error response
|
15
|
+
|
16
|
+
## 1.1.0 (yanked)
|
17
|
+
|
3
18
|
## 1.0.0
|
4
19
|
|
5
20
|
- Breaking: The default error uses application/problem+json content-type
|
data/Gemfile
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
|
3
3
|
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
# Specify your gem's dependencies in openapi_first.gemspec
|
6
5
|
gemspec
|
7
6
|
|
8
7
|
gem 'rack', '>= 3.0.0'
|
8
|
+
gem 'rackup'
|
9
9
|
|
10
10
|
group :test, :development do
|
11
|
+
gem 'actionpack'
|
11
12
|
gem 'bundler'
|
12
13
|
gem 'rack-test'
|
13
14
|
gem 'rake'
|
14
15
|
gem 'rspec'
|
15
16
|
gem 'rubocop'
|
17
|
+
gem 'simplecov'
|
16
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,21 +1,58 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openapi_first (1.
|
4
|
+
openapi_first (1.2.1)
|
5
5
|
json_refs (~> 0.1, >= 0.1.7)
|
6
6
|
json_schemer (~> 2.1.0)
|
7
7
|
multi_json (~> 1.15)
|
8
|
-
mustermann
|
8
|
+
mustermann (~> 3.0.0)
|
9
9
|
openapi_parameters (>= 0.3.2, < 2.0)
|
10
10
|
rack (>= 2.2, < 4.0)
|
11
11
|
|
12
12
|
GEM
|
13
13
|
remote: https://rubygems.org/
|
14
14
|
specs:
|
15
|
+
actionpack (7.1.3)
|
16
|
+
actionview (= 7.1.3)
|
17
|
+
activesupport (= 7.1.3)
|
18
|
+
nokogiri (>= 1.8.5)
|
19
|
+
racc
|
20
|
+
rack (>= 2.2.4)
|
21
|
+
rack-session (>= 1.0.1)
|
22
|
+
rack-test (>= 0.6.3)
|
23
|
+
rails-dom-testing (~> 2.2)
|
24
|
+
rails-html-sanitizer (~> 1.6)
|
25
|
+
actionview (7.1.3)
|
26
|
+
activesupport (= 7.1.3)
|
27
|
+
builder (~> 3.1)
|
28
|
+
erubi (~> 1.11)
|
29
|
+
rails-dom-testing (~> 2.2)
|
30
|
+
rails-html-sanitizer (~> 1.6)
|
31
|
+
activesupport (7.1.3)
|
32
|
+
base64
|
33
|
+
bigdecimal
|
34
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
35
|
+
connection_pool (>= 2.2.5)
|
36
|
+
drb
|
37
|
+
i18n (>= 1.6, < 2)
|
38
|
+
minitest (>= 5.1)
|
39
|
+
mutex_m
|
40
|
+
tzinfo (~> 2.0)
|
15
41
|
ast (2.4.2)
|
42
|
+
base64 (0.2.0)
|
43
|
+
bigdecimal (3.1.6)
|
44
|
+
builder (3.2.4)
|
45
|
+
concurrent-ruby (1.2.3)
|
46
|
+
connection_pool (2.4.1)
|
47
|
+
crass (1.0.6)
|
16
48
|
diff-lcs (1.5.0)
|
49
|
+
docile (1.4.0)
|
50
|
+
drb (2.2.0)
|
51
|
+
ruby2_keywords
|
52
|
+
erubi (1.12.0)
|
17
53
|
hana (1.3.7)
|
18
|
-
|
54
|
+
i18n (1.14.1)
|
55
|
+
concurrent-ruby (~> 1.0)
|
19
56
|
json (2.7.1)
|
20
57
|
json_refs (0.1.8)
|
21
58
|
hana
|
@@ -24,26 +61,44 @@ GEM
|
|
24
61
|
regexp_parser (~> 2.0)
|
25
62
|
simpleidn (~> 0.2)
|
26
63
|
language_server-protocol (3.17.0.3)
|
64
|
+
loofah (2.22.0)
|
65
|
+
crass (~> 1.0.2)
|
66
|
+
nokogiri (>= 1.12.0)
|
67
|
+
minitest (5.21.2)
|
27
68
|
multi_json (1.15.0)
|
28
69
|
mustermann (3.0.0)
|
29
70
|
ruby2_keywords (~> 0.0.1)
|
30
|
-
|
31
|
-
|
32
|
-
|
71
|
+
mutex_m (0.2.0)
|
72
|
+
nokogiri (1.16.0-arm64-darwin)
|
73
|
+
racc (~> 1.4)
|
74
|
+
nokogiri (1.16.0-x86_64-linux)
|
75
|
+
racc (~> 1.4)
|
33
76
|
openapi_parameters (0.3.2)
|
34
77
|
rack (>= 2.2)
|
35
78
|
zeitwerk (~> 2.6)
|
36
79
|
parallel (1.24.0)
|
37
|
-
parser (3.3.0.
|
80
|
+
parser (3.3.0.5)
|
38
81
|
ast (~> 2.4.1)
|
39
82
|
racc
|
40
83
|
racc (1.7.3)
|
41
84
|
rack (3.0.8)
|
85
|
+
rack-session (2.0.0)
|
86
|
+
rack (>= 3.0.0)
|
42
87
|
rack-test (2.1.0)
|
43
88
|
rack (>= 1.3)
|
89
|
+
rackup (2.1.0)
|
90
|
+
rack (>= 3)
|
91
|
+
webrick (~> 1.8)
|
92
|
+
rails-dom-testing (2.2.0)
|
93
|
+
activesupport (>= 5.0.0)
|
94
|
+
minitest
|
95
|
+
nokogiri (>= 1.6)
|
96
|
+
rails-html-sanitizer (1.6.0)
|
97
|
+
loofah (~> 2.21)
|
98
|
+
nokogiri (~> 1.14)
|
44
99
|
rainbow (3.1.1)
|
45
100
|
rake (13.1.0)
|
46
|
-
regexp_parser (2.
|
101
|
+
regexp_parser (2.9.0)
|
47
102
|
rexml (3.2.6)
|
48
103
|
rspec (3.12.0)
|
49
104
|
rspec-core (~> 3.12.0)
|
@@ -58,11 +113,11 @@ GEM
|
|
58
113
|
diff-lcs (>= 1.2.0, < 2.0)
|
59
114
|
rspec-support (~> 3.12.0)
|
60
115
|
rspec-support (3.12.1)
|
61
|
-
rubocop (1.
|
116
|
+
rubocop (1.60.1)
|
62
117
|
json (~> 2.3)
|
63
118
|
language_server-protocol (>= 3.17.0)
|
64
119
|
parallel (~> 1.10)
|
65
|
-
parser (>= 3.
|
120
|
+
parser (>= 3.3.0.2)
|
66
121
|
rainbow (>= 2.2.2, < 4.0)
|
67
122
|
regexp_parser (>= 1.8, < 3.0)
|
68
123
|
rexml (>= 3.2.5, < 4.0)
|
@@ -73,12 +128,21 @@ GEM
|
|
73
128
|
parser (>= 3.2.1.0)
|
74
129
|
ruby-progressbar (1.13.0)
|
75
130
|
ruby2_keywords (0.0.5)
|
131
|
+
simplecov (0.22.0)
|
132
|
+
docile (~> 1.1)
|
133
|
+
simplecov-html (~> 0.11)
|
134
|
+
simplecov_json_formatter (~> 0.1)
|
135
|
+
simplecov-html (0.12.3)
|
136
|
+
simplecov_json_formatter (0.1.4)
|
76
137
|
simpleidn (0.2.1)
|
77
138
|
unf (~> 0.1.4)
|
139
|
+
tzinfo (2.0.6)
|
140
|
+
concurrent-ruby (~> 1.0)
|
78
141
|
unf (0.1.4)
|
79
142
|
unf_ext
|
80
143
|
unf_ext (0.0.9.1)
|
81
144
|
unicode-display_width (2.5.0)
|
145
|
+
webrick (1.8.1)
|
82
146
|
zeitwerk (2.6.12)
|
83
147
|
|
84
148
|
PLATFORMS
|
@@ -87,13 +151,16 @@ PLATFORMS
|
|
87
151
|
x86_64-linux
|
88
152
|
|
89
153
|
DEPENDENCIES
|
154
|
+
actionpack
|
90
155
|
bundler
|
91
156
|
openapi_first!
|
92
157
|
rack (>= 3.0.0)
|
93
158
|
rack-test
|
159
|
+
rackup
|
94
160
|
rake
|
95
161
|
rspec
|
96
162
|
rubocop
|
163
|
+
simplecov
|
97
164
|
|
98
165
|
BUNDLED WITH
|
99
166
|
2.3.10
|
data/Gemfile.rack2.lock
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
openapi_first (1.
|
4
|
+
openapi_first (1.1.1)
|
5
5
|
json_refs (~> 0.1, >= 0.1.7)
|
6
6
|
json_schemer (~> 2.1.0)
|
7
7
|
multi_json (~> 1.15)
|
8
|
-
mustermann
|
8
|
+
mustermann (~> 3.0.0)
|
9
9
|
openapi_parameters (>= 0.3.2, < 2.0)
|
10
10
|
rack (>= 2.2, < 4.0)
|
11
11
|
|
@@ -15,7 +15,6 @@ GEM
|
|
15
15
|
ast (2.4.2)
|
16
16
|
diff-lcs (1.5.0)
|
17
17
|
hana (1.3.7)
|
18
|
-
hansi (0.2.1)
|
19
18
|
json (2.7.1)
|
20
19
|
json_refs (0.1.8)
|
21
20
|
hana
|
@@ -27,9 +26,6 @@ GEM
|
|
27
26
|
multi_json (1.15.0)
|
28
27
|
mustermann (3.0.0)
|
29
28
|
ruby2_keywords (~> 0.0.1)
|
30
|
-
mustermann-contrib (3.0.0)
|
31
|
-
hansi (~> 0.2.0)
|
32
|
-
mustermann (= 3.0.0)
|
33
29
|
openapi_parameters (0.3.2)
|
34
30
|
rack (>= 2.2)
|
35
31
|
zeitwerk (~> 2.6)
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# openapi_first
|
2
2
|
|
3
|
-
OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description. It supports OpenAPI 3.0 and 3.1. It offers request and response validation
|
4
|
-
|
5
|
-
This makes your API description reliable, reason about API design and use various tooling on top of OpenAPI.
|
3
|
+
OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description. It supports OpenAPI 3.0 and 3.1. It offers request and response validation and it ensures that your implementation follows exactly the API description.
|
6
4
|
|
7
5
|
## Contents
|
8
6
|
|
@@ -10,8 +8,12 @@ This makes your API description reliable, reason about API design and use variou
|
|
10
8
|
|
11
9
|
- [Manual use](#manual-use)
|
12
10
|
- [Rack Middlewares](#rack-middlewares)
|
11
|
+
- [Request validation](#request-validation)
|
12
|
+
- [Response validation](#response-validation)
|
13
13
|
- [Configuration](#configuration)
|
14
14
|
- [Development](#development)
|
15
|
+
- [Benchmarks](#benchmarks)
|
16
|
+
- [Contributing](#contributing)
|
15
17
|
|
16
18
|
<!-- /TOC -->
|
17
19
|
|
@@ -30,17 +32,21 @@ Validate request / response:
|
|
30
32
|
```ruby
|
31
33
|
|
32
34
|
# Find the request
|
33
|
-
rack_request = Rack::Request.new(env)
|
35
|
+
rack_request = Rack::Request.new(env) # GET /pets/42
|
34
36
|
request = definition.request(rack_request)
|
35
37
|
|
36
38
|
# Inspect the request and access parsed parameters
|
37
39
|
request.known? # Is the request defined in the API description?
|
38
|
-
request.
|
39
|
-
request.
|
40
|
-
request.
|
40
|
+
request.content_type
|
41
|
+
request.body # alias: parsed_body
|
42
|
+
request.path_parameters # => { "pet_id" => 42 }
|
43
|
+
request.query_parameters # alias: query
|
41
44
|
request.params # Merged path and query parameters
|
42
45
|
request.headers
|
43
46
|
request.cookies
|
47
|
+
request.request_method # => "get"
|
48
|
+
request.path # => "/pets/42"
|
49
|
+
request.path_definition # => "/pets/{pet_id}"
|
44
50
|
|
45
51
|
# Validate the request
|
46
52
|
request.validate # Returns OpenapiFirst:::Failure if validation fails
|
@@ -50,8 +56,15 @@ request.validate! # Raises OpenapiFirst::RequestInvalidError or OpenapiFirst::No
|
|
50
56
|
rack_response = Rack::Response[*app.call(env)]
|
51
57
|
response = request.response(rack_response) # or definition.response(rack_request, rack_response)
|
52
58
|
|
59
|
+
# Inspect the response
|
60
|
+
response.known? # Is the response defined in the API description?
|
61
|
+
response.status # => 200
|
62
|
+
response.content_type
|
63
|
+
response.body
|
64
|
+
request.headers # parsed response headers
|
65
|
+
|
53
66
|
# Validate response
|
54
|
-
response.validate # Returns OpenapiFirst::Failure
|
67
|
+
response.validate # Returns OpenapiFirst::Failure if validation fails
|
55
68
|
response.validate! # Raises OpenapiFirst::ResponseInvalidError or OpenapiFirst::ResponseNotFoundError if validation fails
|
56
69
|
```
|
57
70
|
|
@@ -73,13 +86,13 @@ use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml'
|
|
73
86
|
|
74
87
|
#### Options
|
75
88
|
|
76
|
-
| Name | Possible values
|
77
|
-
| :---------------- |
|
78
|
-
| `spec:` |
|
79
|
-
| `raise_error:` | `false` (default), `true`
|
80
|
-
| `error_response:` | `:default` (default), `:
|
89
|
+
| Name | Possible values | Description |
|
90
|
+
| :---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
|
91
|
+
| `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
|
92
|
+
| `raise_error:` | `false` (default), `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` or `OpenapiFirst::NotFoundError` instead of returning 4xx. |
|
93
|
+
| `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` |
|
81
94
|
|
82
|
-
Here
|
95
|
+
Here in an example response body about an invalid request body. See also [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457).
|
83
96
|
|
84
97
|
```json
|
85
98
|
http-status: 400
|
@@ -108,6 +121,50 @@ content-type: "application/problem+json"
|
|
108
121
|
}
|
109
122
|
```
|
110
123
|
|
124
|
+
openapi_first offers a [JSON:API](https://jsonapi.org/) error response as well:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml, error_response: :jsonapi'
|
128
|
+
```
|
129
|
+
|
130
|
+
Here is an example error response:
|
131
|
+
|
132
|
+
```json
|
133
|
+
// http-status: 400
|
134
|
+
// content-type: "application/vnd.api+json"
|
135
|
+
|
136
|
+
{
|
137
|
+
"errors": [
|
138
|
+
{
|
139
|
+
"status": "400",
|
140
|
+
"source": {
|
141
|
+
"pointer": "/data/name"
|
142
|
+
},
|
143
|
+
"title": "value at `/data/name` is not a string",
|
144
|
+
"code": "string"
|
145
|
+
},
|
146
|
+
{
|
147
|
+
"status": "400",
|
148
|
+
"source": {
|
149
|
+
"pointer": "/data/numberOfLegs"
|
150
|
+
},
|
151
|
+
"title": "number at `/data/numberOfLegs` is less than: 2",
|
152
|
+
"code": "minimum"
|
153
|
+
},
|
154
|
+
{
|
155
|
+
"status": "400",
|
156
|
+
"source": {
|
157
|
+
"pointer": "/data"
|
158
|
+
},
|
159
|
+
"title": "object at `/data` is missing required properties: mandatory",
|
160
|
+
"code": "required"
|
161
|
+
}
|
162
|
+
]
|
163
|
+
}
|
164
|
+
```
|
165
|
+
|
166
|
+
You can build your own custom error response with `error_response: MyCustomClass` that implements `OpenapiFirst::ErrorResponse`.
|
167
|
+
|
111
168
|
#### readOnly / writeOnly properties
|
112
169
|
|
113
170
|
Request validation fails if request includes a property with `readOnly: true`.
|
@@ -135,7 +192,7 @@ You can configure default options globally:
|
|
135
192
|
```ruby
|
136
193
|
OpenapiFirst.configure do |config|
|
137
194
|
# Specify which plugin is used to render error responses returned by the request validation middleware (defaults to :default)
|
138
|
-
config.request_validation_error_response = :
|
195
|
+
config.request_validation_error_response = :jsonapi
|
139
196
|
# Configure if the response validation middleware should raise an exception (defaults to false)
|
140
197
|
config.request_validation_raise_error = true
|
141
198
|
end
|
@@ -4,7 +4,12 @@ require 'multi_json'
|
|
4
4
|
|
5
5
|
module OpenapiFirst
|
6
6
|
class BodyParser
|
7
|
-
|
7
|
+
def self.const_missing(const_name)
|
8
|
+
super unless const_name == :ParsingError
|
9
|
+
warn 'DEPRECATION WARNING: OpenapiFirst::BodyParser::ParsingError is deprecated. ' \
|
10
|
+
'Use OpenapiFirst::ParseError instead.'
|
11
|
+
OpenapiFirst::ParseError
|
12
|
+
end
|
8
13
|
|
9
14
|
def parse(request, content_type)
|
10
15
|
body = read_body(request)
|
@@ -15,7 +20,7 @@ module OpenapiFirst
|
|
15
20
|
|
16
21
|
body
|
17
22
|
rescue MultiJson::ParseError
|
18
|
-
raise
|
23
|
+
raise ParseError, 'Failed to parse body as JSON'
|
19
24
|
end
|
20
25
|
|
21
26
|
private
|
@@ -2,11 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
4
|
require 'set'
|
5
|
+
require 'openapi_parameters'
|
5
6
|
require_relative 'request_body'
|
6
|
-
require_relative 'query_parameters'
|
7
|
-
require_relative 'header_parameters'
|
8
|
-
require_relative 'path_parameters'
|
9
|
-
require_relative 'cookie_parameters'
|
10
7
|
require_relative 'responses'
|
11
8
|
|
12
9
|
module OpenapiFirst
|
@@ -14,14 +11,11 @@ module OpenapiFirst
|
|
14
11
|
class Operation
|
15
12
|
extend Forwardable
|
16
13
|
def_delegators :operation_object,
|
17
|
-
:[]
|
18
|
-
:dig
|
14
|
+
:[]
|
19
15
|
|
20
16
|
WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
|
21
17
|
private_constant :WRITE_METHODS
|
22
18
|
|
23
|
-
attr_reader :path, :method, :openapi_version
|
24
|
-
|
25
19
|
def initialize(path, request_method, path_item_object, openapi_version:)
|
26
20
|
@path = path
|
27
21
|
@method = request_method
|
@@ -30,6 +24,9 @@ module OpenapiFirst
|
|
30
24
|
@operation_object = @path_item_object[request_method]
|
31
25
|
end
|
32
26
|
|
27
|
+
attr_reader :path, :method, :openapi_version
|
28
|
+
alias request_method method
|
29
|
+
|
33
30
|
def operation_id
|
34
31
|
operation_object['operationId']
|
35
32
|
end
|
@@ -66,34 +63,63 @@ module OpenapiFirst
|
|
66
63
|
@name ||= "#{method.upcase} #{path} (#{operation_id})"
|
67
64
|
end
|
68
65
|
|
69
|
-
def query_parameters
|
70
|
-
@query_parameters ||= build_parameters(all_parameters.filter { |p| p['in'] == 'query' }, QueryParameters)
|
71
|
-
end
|
72
|
-
|
73
66
|
def path_parameters
|
74
|
-
|
67
|
+
all_parameters['path']
|
75
68
|
end
|
76
69
|
|
77
|
-
|
78
|
-
|
70
|
+
def query_parameters
|
71
|
+
all_parameters['query']
|
72
|
+
end
|
79
73
|
|
80
74
|
def header_parameters
|
81
|
-
|
75
|
+
all_parameters['header']
|
82
76
|
end
|
83
77
|
|
84
78
|
def cookie_parameters
|
85
|
-
|
79
|
+
all_parameters['cookie']
|
80
|
+
end
|
81
|
+
|
82
|
+
def path_parameters_schema
|
83
|
+
@path_parameters_schema ||= build_schema(path_parameters)
|
84
|
+
end
|
85
|
+
|
86
|
+
def query_parameters_schema
|
87
|
+
@query_parameters_schema ||= build_schema(query_parameters)
|
88
|
+
end
|
89
|
+
|
90
|
+
def header_parameters_schema
|
91
|
+
@header_parameters_schema ||= build_schema(header_parameters)
|
92
|
+
end
|
93
|
+
|
94
|
+
def cookie_parameters_schema
|
95
|
+
@cookie_parameters_schema ||= build_schema(cookie_parameters)
|
86
96
|
end
|
87
97
|
|
88
98
|
private
|
89
99
|
|
100
|
+
IGNORED_HEADERS = Set['Content-Type', 'Accept', 'Authorization'].freeze
|
101
|
+
private_constant :IGNORED_HEADERS
|
102
|
+
|
90
103
|
def all_parameters
|
91
|
-
@all_parameters ||=
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
@all_parameters ||= (@path_item_object.fetch('parameters', []) + operation_object.fetch('parameters', []))
|
105
|
+
.reject { |p| p['in'] == 'header' && IGNORED_HEADERS.include?(p['name']) }
|
106
|
+
.group_by { _1['in'] }
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_schema(parameters)
|
110
|
+
return unless parameters&.any?
|
111
|
+
|
112
|
+
init_schema = {
|
113
|
+
'type' => 'object',
|
114
|
+
'properties' => {},
|
115
|
+
'required' => []
|
116
|
+
}
|
117
|
+
schema = parameters.each_with_object(init_schema) do |parameter_def, result|
|
118
|
+
parameter = OpenapiParameters::Parameter.new(parameter_def)
|
119
|
+
result['properties'][parameter.name] = parameter.schema if parameter.schema
|
120
|
+
result['required'] << parameter.name if parameter.required?
|
96
121
|
end
|
122
|
+
Schema.new(schema, openapi_version: @openapi_version)
|
97
123
|
end
|
98
124
|
|
99
125
|
def responses
|
@@ -105,12 +131,6 @@ module OpenapiFirst
|
|
105
131
|
def build_parameters(parameters, klass)
|
106
132
|
klass.new(parameters, openapi_version:) if parameters.any?
|
107
133
|
end
|
108
|
-
|
109
|
-
def find_header_parameters
|
110
|
-
all_parameters.filter do |p|
|
111
|
-
p['in'] == 'header' && !IGNORED_HEADERS.include?(p['name'])
|
112
|
-
end
|
113
|
-
end
|
114
134
|
end
|
115
135
|
end
|
116
136
|
end
|
@@ -3,40 +3,42 @@
|
|
3
3
|
require_relative '../schema'
|
4
4
|
|
5
5
|
module OpenapiFirst
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
class Definition
|
7
|
+
class RequestBody
|
8
|
+
def initialize(request_body_object, operation)
|
9
|
+
@request_body_object = request_body_object
|
10
|
+
@operation = operation
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def description
|
14
|
+
@request_body_object['description']
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
def required?
|
18
|
+
!!@request_body_object['required']
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
def schema_for(content_type)
|
22
|
+
content = @request_body_object['content']
|
23
|
+
return unless content&.any?
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
content_schemas&.fetch(content_type) do
|
26
|
+
type = content_type.split(';')[0]
|
27
|
+
content_schemas[type] || content_schemas["#{type.split('/')[0]}/*"] || content_schemas['*/*']
|
28
|
+
end
|
27
29
|
end
|
28
|
-
end
|
29
30
|
|
30
|
-
|
31
|
+
private
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
def content_schemas
|
34
|
+
@content_schemas ||= @request_body_object['content']&.each_with_object({}) do |kv, result|
|
35
|
+
type, media_type = kv
|
36
|
+
schema_object = media_type['schema']
|
37
|
+
next unless schema_object
|
37
38
|
|
38
|
-
|
39
|
-
|
39
|
+
result[type] = Schema.new(schema_object, write: @operation.write?,
|
40
|
+
openapi_version: @operation.openapi_version)
|
41
|
+
end
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'mustermann
|
3
|
+
require 'mustermann'
|
4
4
|
require_relative 'definition/path_item'
|
5
5
|
require_relative 'runtime_request'
|
6
6
|
|
@@ -9,7 +9,7 @@ module OpenapiFirst
|
|
9
9
|
class Definition
|
10
10
|
attr_reader :filepath, :paths, :openapi_version
|
11
11
|
|
12
|
-
def initialize(resolved, filepath)
|
12
|
+
def initialize(resolved, filepath = nil)
|
13
13
|
@filepath = filepath
|
14
14
|
@paths = resolved['paths']
|
15
15
|
@openapi_version = detect_version(resolved)
|
@@ -60,7 +60,7 @@ module OpenapiFirst
|
|
60
60
|
|
61
61
|
def search_for_path_item(request_path)
|
62
62
|
paths.find do |path, path_item_object|
|
63
|
-
template = Mustermann
|
63
|
+
template = Mustermann.new(path)
|
64
64
|
path_params = template.params(request_path)
|
65
65
|
next unless path_params
|
66
66
|
next unless path_params.size == template.names.size
|
data/lib/openapi_first/errors.rb
CHANGED
@@ -20,7 +20,7 @@ module OpenapiFirst
|
|
20
20
|
private_constant :TYPES
|
21
21
|
|
22
22
|
# @param error_type [Symbol] See Failure::TYPES.keys
|
23
|
-
# @param errors [Array<OpenapiFirst::Schema::
|
23
|
+
# @param errors [Array<OpenapiFirst::Schema::ValidationError>]
|
24
24
|
def self.fail!(error_type, message: nil, errors: nil)
|
25
25
|
throw FAILURE, new(
|
26
26
|
error_type,
|
@@ -47,9 +47,22 @@ module OpenapiFirst
|
|
47
47
|
|
48
48
|
# Raise an exception that fits the failure.
|
49
49
|
def raise!
|
50
|
-
exception,
|
50
|
+
exception, = TYPES.fetch(error_type)
|
51
|
+
raise exception, exception_message
|
52
|
+
end
|
53
|
+
|
54
|
+
def exception_message
|
55
|
+
_, message_prefix = TYPES.fetch(error_type)
|
56
|
+
|
57
|
+
"#{message_prefix} #{@message || generate_message}"
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
51
61
|
|
52
|
-
|
62
|
+
def generate_message
|
63
|
+
messages = errors&.take(4)&.map(&:error)
|
64
|
+
messages << "... (#{errors.size} errors total)" if errors && errors.size > 4
|
65
|
+
messages&.join('. ')
|
53
66
|
end
|
54
67
|
end
|
55
68
|
end
|
@@ -19,10 +19,13 @@ module OpenapiFirst
|
|
19
19
|
|
20
20
|
def call(env)
|
21
21
|
request = find_request(env)
|
22
|
-
|
23
|
-
request.response(response).validate!
|
22
|
+
status, headers, body = @app.call(env)
|
24
23
|
|
25
|
-
|
24
|
+
body = body.to_ary if body.respond_to?(:to_ary)
|
25
|
+
|
26
|
+
request.response(Rack::Response[status, headers, body]).validate!
|
27
|
+
|
28
|
+
[status, headers, body]
|
26
29
|
end
|
27
30
|
|
28
31
|
private
|
@@ -16,7 +16,7 @@ module OpenapiFirst
|
|
16
16
|
invalid_query: 'Bad Query Parameter',
|
17
17
|
invalid_header: 'Bad Request Header',
|
18
18
|
invalid_path: 'Bad Request Path',
|
19
|
-
invalid_cookie: '
|
19
|
+
invalid_cookie: 'Bad Request Cookie'
|
20
20
|
}.freeze
|
21
21
|
private_constant :TITLES
|
22
22
|
|
@@ -38,34 +38,34 @@ module OpenapiFirst
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def validate_path_params!(request)
|
41
|
-
|
42
|
-
return unless
|
41
|
+
schema = operation.path_parameters_schema
|
42
|
+
return unless schema
|
43
43
|
|
44
|
-
validation =
|
44
|
+
validation = schema.validate(request.path_parameters)
|
45
45
|
Failure.fail!(:invalid_path, errors: validation.errors) if validation.error?
|
46
46
|
end
|
47
47
|
|
48
48
|
def validate_query_params!(request)
|
49
|
-
|
50
|
-
return unless
|
49
|
+
schema = operation.query_parameters_schema
|
50
|
+
return unless schema
|
51
51
|
|
52
|
-
validation =
|
52
|
+
validation = schema.validate(request.query)
|
53
53
|
Failure.fail!(:invalid_query, errors: validation.errors) if validation.error?
|
54
54
|
end
|
55
55
|
|
56
56
|
def validate_cookie_params!(request)
|
57
|
-
|
58
|
-
return unless
|
57
|
+
schema = operation.cookie_parameters_schema
|
58
|
+
return unless schema
|
59
59
|
|
60
|
-
validation =
|
60
|
+
validation = schema.validate(request.cookies)
|
61
61
|
Failure.fail!(:invalid_cookie, errors: validation.errors) if validation.error?
|
62
62
|
end
|
63
63
|
|
64
64
|
def validate_header_params!(request)
|
65
|
-
|
66
|
-
return unless
|
65
|
+
schema = operation.header_parameters_schema
|
66
|
+
return unless schema
|
67
67
|
|
68
|
-
validation =
|
68
|
+
validation = schema.validate(request.headers)
|
69
69
|
Failure.fail!(:invalid_header, errors: validation.errors) if validation.error?
|
70
70
|
end
|
71
71
|
|
@@ -73,7 +73,7 @@ module OpenapiFirst
|
|
73
73
|
return unless operation.request_body
|
74
74
|
|
75
75
|
RequestBodyValidator.new(operation).validate!(request.body, request.content_type)
|
76
|
-
rescue
|
76
|
+
rescue ParseError => e
|
77
77
|
Failure.fail!(:invalid_body, message: e.message)
|
78
78
|
end
|
79
79
|
end
|
@@ -9,14 +9,14 @@ module OpenapiFirst
|
|
9
9
|
@operation = operation
|
10
10
|
end
|
11
11
|
|
12
|
-
def validate(
|
12
|
+
def validate(runtime_response)
|
13
13
|
return unless operation
|
14
14
|
|
15
|
-
response = Rack::Response[*rack_response.to_a]
|
16
15
|
catch Failure::FAILURE do
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
validate_defined(runtime_response)
|
17
|
+
response_definition = runtime_response.response_definition
|
18
|
+
validate_response_body(response_definition.content_schema, runtime_response)
|
19
|
+
validate_response_headers(response_definition.headers, runtime_response.headers)
|
20
20
|
nil
|
21
21
|
end
|
22
22
|
end
|
@@ -25,37 +25,40 @@ module OpenapiFirst
|
|
25
25
|
|
26
26
|
attr_reader :operation
|
27
27
|
|
28
|
-
def
|
29
|
-
|
30
|
-
return response if response
|
28
|
+
def validate_defined(runtime_response)
|
29
|
+
return if runtime_response.known?
|
31
30
|
|
32
|
-
unless
|
33
|
-
message = "Response status '#{status}' not found for '#{
|
31
|
+
unless runtime_response.known_status?
|
32
|
+
message = "Response status '#{runtime_response.status}' not found for '#{runtime_response.name}'"
|
34
33
|
Failure.fail!(:response_not_found, message:)
|
35
34
|
end
|
35
|
+
|
36
|
+
content_type = runtime_response.content_type
|
36
37
|
if content_type.nil? || content_type.empty?
|
37
|
-
message = "Content-Type for '#{
|
38
|
+
message = "Content-Type for '#{runtime_response.name}' must not be empty"
|
38
39
|
Failure.fail!(:invalid_response_header, message:)
|
39
40
|
end
|
40
41
|
|
41
|
-
message = "Content-Type '#{content_type}' is not defined for '#{
|
42
|
+
message = "Content-Type '#{content_type}' is not defined for '#{runtime_response.name}'"
|
42
43
|
Failure.fail!(:invalid_response_header, message:)
|
43
44
|
end
|
44
45
|
|
45
|
-
def validate_response_body(schema,
|
46
|
+
def validate_response_body(schema, runtime_response)
|
46
47
|
return unless schema
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
begin
|
50
|
+
parsed_body = runtime_response.body
|
51
|
+
rescue ParseError => e
|
52
|
+
Failure.fail!(:invalid_response_body, message: e.message)
|
53
|
+
end
|
54
|
+
|
55
|
+
validation = schema.validate(parsed_body)
|
52
56
|
Failure.fail!(:invalid_response_body, errors: validation.errors) if validation.error?
|
53
57
|
end
|
54
58
|
|
55
|
-
def validate_response_headers(response_header_definitions,
|
59
|
+
def validate_response_headers(response_header_definitions, unpacked_headers)
|
56
60
|
return unless response_header_definitions
|
57
61
|
|
58
|
-
unpacked_headers = unpack_response_headers(response_header_definitions, response_headers)
|
59
62
|
response_header_definitions.each do |name, definition|
|
60
63
|
next if name == 'Content-Type'
|
61
64
|
|
@@ -84,13 +87,6 @@ module OpenapiFirst
|
|
84
87
|
errors: validation_result.errors)
|
85
88
|
end
|
86
89
|
|
87
|
-
def unpack_response_headers(response_header_definitions, response_headers)
|
88
|
-
headers_as_parameters = response_header_definitions.map do |name, definition|
|
89
|
-
definition.merge('name' => name, 'in' => 'header')
|
90
|
-
end
|
91
|
-
OpenapiParameters::Header.new(headers_as_parameters).unpack(response_headers)
|
92
|
-
end
|
93
|
-
|
94
90
|
def load_json(string)
|
95
91
|
MultiJson.load(string)
|
96
92
|
rescue MultiJson::ParseError
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
require 'openapi_parameters'
|
4
5
|
require_relative 'runtime_response'
|
5
6
|
require_relative 'body_parser'
|
6
7
|
require_relative 'request_validation/validator'
|
@@ -17,8 +18,11 @@ module OpenapiFirst
|
|
17
18
|
@original_path_params = path_params
|
18
19
|
end
|
19
20
|
|
20
|
-
def_delegators :@request, :content_type, :media_type
|
21
|
-
def_delegators :@operation, :operation_id
|
21
|
+
def_delegators :@request, :content_type, :media_type, :path
|
22
|
+
def_delegators :@operation, :operation_id, :request_method
|
23
|
+
def_delegator :@path_item, :path, :path_definition
|
24
|
+
|
25
|
+
attr_reader :path_item, :operation
|
22
26
|
|
23
27
|
def known?
|
24
28
|
known_path? && known_request_method?
|
@@ -38,25 +42,32 @@ module OpenapiFirst
|
|
38
42
|
end
|
39
43
|
|
40
44
|
def path_parameters
|
45
|
+
return {} unless operation.path_parameters
|
46
|
+
|
41
47
|
@path_parameters ||=
|
42
|
-
operation.path_parameters
|
48
|
+
OpenapiParameters::Path.new(operation.path_parameters).unpack(@original_path_params) || {}
|
43
49
|
end
|
44
50
|
|
45
51
|
def query
|
52
|
+
return {} unless operation.query_parameters
|
53
|
+
|
46
54
|
@query ||=
|
47
|
-
operation.query_parameters
|
55
|
+
OpenapiParameters::Query.new(operation.query_parameters).unpack(request.env[Rack::QUERY_STRING]) || {}
|
48
56
|
end
|
49
57
|
|
50
58
|
alias query_parameters query
|
51
59
|
|
52
60
|
def headers
|
53
|
-
|
54
|
-
|
61
|
+
return {} unless operation.header_parameters
|
62
|
+
|
63
|
+
@headers ||= OpenapiParameters::Header.new(operation.header_parameters).unpack_env(request.env) || {}
|
55
64
|
end
|
56
65
|
|
57
66
|
def cookies
|
67
|
+
return {} unless operation.cookie_parameters
|
68
|
+
|
58
69
|
@cookies ||=
|
59
|
-
operation.cookie_parameters
|
70
|
+
OpenapiParameters::Cookie.new(operation.cookie_parameters).unpack(request.env[Rack::HTTP_COOKIE]) || {}
|
60
71
|
end
|
61
72
|
|
62
73
|
def body
|
@@ -79,6 +90,6 @@ module OpenapiFirst
|
|
79
90
|
|
80
91
|
private
|
81
92
|
|
82
|
-
attr_reader :request
|
93
|
+
attr_reader :request
|
83
94
|
end
|
84
95
|
end
|
@@ -1,20 +1,43 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
require_relative 'body_parser'
|
3
5
|
require_relative 'response_validation/validator'
|
4
6
|
|
5
7
|
module OpenapiFirst
|
6
8
|
class RuntimeResponse
|
9
|
+
extend Forwardable
|
10
|
+
|
7
11
|
def initialize(operation, rack_response)
|
8
12
|
@operation = operation
|
9
13
|
@rack_response = rack_response
|
10
14
|
end
|
11
15
|
|
16
|
+
def_delegators :@rack_response, :status, :content_type
|
17
|
+
def_delegators :@operation, :name
|
18
|
+
|
19
|
+
def known?
|
20
|
+
!!response_definition
|
21
|
+
end
|
22
|
+
|
23
|
+
def known_status?
|
24
|
+
@operation.response_status_defined?(status)
|
25
|
+
end
|
26
|
+
|
12
27
|
def description
|
13
28
|
response_definition&.description
|
14
29
|
end
|
15
30
|
|
31
|
+
def body
|
32
|
+
@body ||= content_type =~ /json/i ? load_json(original_body) : original_body
|
33
|
+
end
|
34
|
+
|
35
|
+
def headers
|
36
|
+
@headers ||= unpack_response_headers
|
37
|
+
end
|
38
|
+
|
16
39
|
def validate
|
17
|
-
ResponseValidation::Validator.new(@operation).validate(
|
40
|
+
ResponseValidation::Validator.new(@operation).validate(self)
|
18
41
|
end
|
19
42
|
|
20
43
|
def validate!
|
@@ -22,10 +45,31 @@ module OpenapiFirst
|
|
22
45
|
error&.raise!
|
23
46
|
end
|
24
47
|
|
48
|
+
def response_definition
|
49
|
+
@response_definition ||= @operation.response_for(status, content_type)
|
50
|
+
end
|
51
|
+
|
25
52
|
private
|
26
53
|
|
27
|
-
def
|
28
|
-
|
54
|
+
def original_body
|
55
|
+
buffered_body = String.new
|
56
|
+
@rack_response.body.each { |chunk| buffered_body << chunk }
|
57
|
+
buffered_body
|
58
|
+
end
|
59
|
+
|
60
|
+
def load_json(string)
|
61
|
+
MultiJson.load(string)
|
62
|
+
rescue MultiJson::ParseError
|
63
|
+
raise ParseError, 'Failed to parse response body as JSON'
|
64
|
+
end
|
65
|
+
|
66
|
+
def unpack_response_headers
|
67
|
+
return {} if response_definition&.headers.nil?
|
68
|
+
|
69
|
+
headers_as_parameters = response_definition.headers.map do |name, definition|
|
70
|
+
definition.merge('name' => name, 'in' => 'header')
|
71
|
+
end
|
72
|
+
OpenapiParameters::Header.new(headers_as_parameters).unpack(@rack_response.headers)
|
29
73
|
end
|
30
74
|
end
|
31
75
|
end
|
data/lib/openapi_first/schema.rb
CHANGED
data/lib/openapi_first.rb
CHANGED
@@ -28,10 +28,20 @@ module OpenapiFirst
|
|
28
28
|
# Key in rack to find instance of RuntimeRequest
|
29
29
|
REQUEST = 'openapi.request'
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
# Load and dereference an OpenAPI spec file
|
32
|
+
def self.load(filepath, only: nil)
|
33
|
+
resolved = bundle(filepath)
|
34
|
+
parse(resolved, only:, filepath:)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Parse a dereferenced Hash
|
38
|
+
def self.parse(resolved, only: nil, filepath: nil)
|
33
39
|
resolved['paths'].filter!(&->(key, _) { only.call(key) }) if only
|
34
|
-
Definition.new(resolved,
|
40
|
+
Definition.new(resolved, filepath)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.bundle(filepath)
|
44
|
+
Bundle.resolve(filepath)
|
35
45
|
end
|
36
46
|
|
37
47
|
module Bundle
|
data/openapi_first.gemspec
CHANGED
@@ -41,7 +41,7 @@ Gem::Specification.new do |spec|
|
|
41
41
|
spec.add_runtime_dependency 'json_refs', '~> 0.1', '>= 0.1.7'
|
42
42
|
spec.add_runtime_dependency 'json_schemer', '~> 2.1.0'
|
43
43
|
spec.add_runtime_dependency 'multi_json', '~> 1.15'
|
44
|
-
spec.add_runtime_dependency 'mustermann
|
44
|
+
spec.add_runtime_dependency 'mustermann', '~> 3.0.0'
|
45
45
|
spec.add_runtime_dependency 'openapi_parameters', '>= 0.3.2', '< 2.0'
|
46
46
|
spec.add_runtime_dependency 'rack', '>= 2.2', '< 4.0'
|
47
47
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openapi_first
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Haller
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json_refs
|
@@ -59,7 +59,7 @@ dependencies:
|
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '1.15'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
|
-
name: mustermann
|
62
|
+
name: mustermann
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
@@ -133,13 +133,8 @@ files:
|
|
133
133
|
- lib/openapi_first/body_parser.rb
|
134
134
|
- lib/openapi_first/configuration.rb
|
135
135
|
- lib/openapi_first/definition.rb
|
136
|
-
- lib/openapi_first/definition/cookie_parameters.rb
|
137
|
-
- lib/openapi_first/definition/header_parameters.rb
|
138
136
|
- lib/openapi_first/definition/operation.rb
|
139
|
-
- lib/openapi_first/definition/parameters.rb
|
140
137
|
- lib/openapi_first/definition/path_item.rb
|
141
|
-
- lib/openapi_first/definition/path_parameters.rb
|
142
|
-
- lib/openapi_first/definition/query_parameters.rb
|
143
138
|
- lib/openapi_first/definition/request_body.rb
|
144
139
|
- lib/openapi_first/definition/response.rb
|
145
140
|
- lib/openapi_first/definition/responses.rb
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'openapi_parameters'
|
4
|
-
require_relative 'parameters'
|
5
|
-
|
6
|
-
module OpenapiFirst
|
7
|
-
class CookieParameters < Parameters
|
8
|
-
def unpack(env)
|
9
|
-
OpenapiParameters::Cookie.new(@parameter_definitions).unpack(env['HTTP_COOKIE'])
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'openapi_parameters'
|
4
|
-
require_relative 'parameters'
|
5
|
-
|
6
|
-
module OpenapiFirst
|
7
|
-
class HeaderParameters < Parameters
|
8
|
-
def unpack(env)
|
9
|
-
OpenapiParameters::Header.new(@parameter_definitions).unpack_env(env)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
require_relative '../schema'
|
5
|
-
|
6
|
-
module OpenapiFirst
|
7
|
-
class Parameters
|
8
|
-
extend Forwardable
|
9
|
-
|
10
|
-
def initialize(parameter_definitions, openapi_version:)
|
11
|
-
@parameter_definitions = parameter_definitions
|
12
|
-
@openapi_version = openapi_version
|
13
|
-
end
|
14
|
-
|
15
|
-
def_delegators :parameters, :map
|
16
|
-
|
17
|
-
def empty?
|
18
|
-
@parameter_definitions.empty?
|
19
|
-
end
|
20
|
-
|
21
|
-
def schema
|
22
|
-
@schema ||= build_schema
|
23
|
-
end
|
24
|
-
|
25
|
-
def parameters
|
26
|
-
@parameter_definitions.map do |parameter_object|
|
27
|
-
OpenapiParameters::Parameter.new(parameter_object)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def build_schema
|
34
|
-
init_schema = {
|
35
|
-
'type' => 'object',
|
36
|
-
'properties' => {},
|
37
|
-
'required' => []
|
38
|
-
}
|
39
|
-
schema = @parameter_definitions.each_with_object(init_schema) do |parameter_def, result|
|
40
|
-
parameter = OpenapiParameters::Parameter.new(parameter_def)
|
41
|
-
result['properties'][parameter.name] = parameter.schema if parameter.schema
|
42
|
-
result['required'] << parameter.name if parameter.required?
|
43
|
-
end
|
44
|
-
Schema.new(schema, openapi_version: @openapi_version)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'openapi_parameters'
|
4
|
-
require_relative 'parameters'
|
5
|
-
|
6
|
-
module OpenapiFirst
|
7
|
-
class PathParameters < Parameters
|
8
|
-
def unpack(original_path_params)
|
9
|
-
OpenapiParameters::Path.new(@parameter_definitions).unpack(original_path_params)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'openapi_parameters'
|
4
|
-
require_relative 'parameters'
|
5
|
-
|
6
|
-
module OpenapiFirst
|
7
|
-
class QueryParameters < Parameters
|
8
|
-
def unpack(env)
|
9
|
-
OpenapiParameters::Query.new(@parameter_definitions).unpack(env['QUERY_STRING'])
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|