openapi_first 0.12.3 → 0.13.3
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 +3 -26
- data/CHANGELOG.md +18 -1
- data/Gemfile.lock +19 -19
- data/README.md +14 -2
- data/benchmarks/Gemfile +2 -1
- data/benchmarks/Gemfile.lock +24 -21
- data/benchmarks/apps/committee.ru +14 -20
- data/benchmarks/apps/hanami_api.ru +21 -0
- data/benchmarks/apps/hanami_router.ru +1 -1
- data/benchmarks/apps/sinatra.ru +1 -1
- data/benchmarks/apps/syro.ru +3 -3
- data/lib/openapi_first/definition.rb +2 -6
- data/lib/openapi_first/find_handler.rb +2 -4
- data/lib/openapi_first/operation.rb +34 -6
- data/lib/openapi_first/request_validation.rb +27 -27
- data/lib/openapi_first/response_validation.rb +2 -1
- data/lib/openapi_first/router.rb +6 -3
- data/lib/openapi_first/schema_validation.rb +36 -0
- data/lib/openapi_first/utils.rb +7 -14
- data/lib/openapi_first/validation_format.rb +17 -0
- data/lib/openapi_first/version.rb +1 -1
- data/openapi_first.gemspec +3 -1
- metadata +11 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 94cbe97158c8482c40dd1521e1f83cef287ad2e7e32f82746f24adcf45f60c4e
|
|
4
|
+
data.tar.gz: 1aff02e97e51b67334c34bc6ee967559def9218c4e81dc6295204bb5447f6715
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ca9c77f8b70024083b831a02d824c2fd0321ff3598234f085133b161ba46a2177e9279e80e22f2ca56da466b087c0c7ac3a62c6dec9a7f906bf03fd44f2eb47
|
|
7
|
+
data.tar.gz: bcffa9f70194081780a0348f17e695b05f879eb029e4932baa224c3ca1d03e7fb531931f111bfdbcf452f772c0729e725cd3101779aa363408f859e63fd989f8
|
data/.rubocop.yml
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
AllCops:
|
|
2
2
|
TargetRubyVersion: 2.6
|
|
3
|
+
NewCops: enable
|
|
3
4
|
Style/Documentation:
|
|
4
5
|
Enabled: false
|
|
5
6
|
Style/ExponentialNotation:
|
|
@@ -8,29 +9,5 @@ Metrics/BlockLength:
|
|
|
8
9
|
Exclude:
|
|
9
10
|
- 'spec/**/*.rb'
|
|
10
11
|
- '*.gemspec'
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Layout/SpaceAroundMethodCallOperator:
|
|
14
|
-
Enabled: true
|
|
15
|
-
Lint/DeprecatedOpenSSLConstant:
|
|
16
|
-
Enabled: true
|
|
17
|
-
Lint/RaiseException:
|
|
18
|
-
Enabled: true
|
|
19
|
-
Lint/MixedRegexpCaptureTypes:
|
|
20
|
-
Enabled: true
|
|
21
|
-
Style/RedundantRegexpCharacterClass:
|
|
22
|
-
Enabled: true
|
|
23
|
-
Style/RedundantRegexpEscape:
|
|
24
|
-
Enabled: true
|
|
25
|
-
Style/SlicingWithRange:
|
|
26
|
-
Enabled: true
|
|
27
|
-
Lint/StructNewOverride:
|
|
28
|
-
Enabled: true
|
|
29
|
-
Style/HashEachMethods:
|
|
30
|
-
Enabled: false
|
|
31
|
-
Style/HashTransformKeys:
|
|
32
|
-
Enabled: true
|
|
33
|
-
Style/HashTransformValues:
|
|
34
|
-
Enabled: true
|
|
35
|
-
Style/RedundantFetchBlock:
|
|
36
|
-
Enabled: true
|
|
12
|
+
Metrics/MethodLength:
|
|
13
|
+
Max: 20
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.13.3
|
|
4
|
+
- Better error message if string does not match format
|
|
5
|
+
|
|
6
|
+
## 0.13.2
|
|
7
|
+
- Return indicator (`source: { parameter: 'list/1' }`) in error response body when array item in query parameter is invalid
|
|
8
|
+
|
|
9
|
+
## 0.13.0
|
|
10
|
+
- Add support for arrays in query parameters (style: form, explode: false)
|
|
11
|
+
- Remove warning when handler is not implemented
|
|
12
|
+
|
|
13
|
+
## 0.12.5
|
|
14
|
+
- Add `not_found: :continue` option to Router to make it do nothing if request is unknown
|
|
15
|
+
|
|
16
|
+
## 0.12.4
|
|
17
|
+
- content-type is found while ignoring additional content-type parameters (`application/json` is found when request/response content-type is `application/json; charset=UTF8`)
|
|
18
|
+
- Support wildcard mime-types when finding the content-type
|
|
19
|
+
|
|
3
20
|
## 0.12.3
|
|
4
21
|
- Add `response_validation:`, `router_raise_error` options to standalone mode.
|
|
5
22
|
|
|
@@ -8,7 +25,7 @@
|
|
|
8
25
|
|
|
9
26
|
## 0.12.1
|
|
10
27
|
- Fix response when handler returns 404 or 405
|
|
11
|
-
- Don't validate the response content if status is
|
|
28
|
+
- Don't validate the response content if status is 204 (no content)
|
|
12
29
|
|
|
13
30
|
## 0.12.0
|
|
14
31
|
- Change `ResponseValidator` to raise an exception if it found a problem
|
data/Gemfile.lock
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
openapi_first (0.
|
|
4
|
+
openapi_first (0.13.3)
|
|
5
5
|
deep_merge (>= 1.2.1)
|
|
6
6
|
hanami-router (~> 2.0.alpha3)
|
|
7
7
|
hanami-utils (~> 2.0.alpha1)
|
|
8
|
-
json_schemer (~> 0.2)
|
|
8
|
+
json_schemer (~> 0.2.16)
|
|
9
9
|
multi_json (~> 1.14)
|
|
10
10
|
oas_parser (~> 0.25.1)
|
|
11
11
|
rack (~> 2.2)
|
|
@@ -13,7 +13,7 @@ PATH
|
|
|
13
13
|
GEM
|
|
14
14
|
remote: https://rubygems.org/
|
|
15
15
|
specs:
|
|
16
|
-
activesupport (6.0.3.
|
|
16
|
+
activesupport (6.0.3.4)
|
|
17
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
18
18
|
i18n (>= 0.7, < 2)
|
|
19
19
|
minitest (~> 5.1)
|
|
@@ -24,7 +24,7 @@ GEM
|
|
|
24
24
|
ast (2.4.1)
|
|
25
25
|
builder (3.2.4)
|
|
26
26
|
coderay (1.1.3)
|
|
27
|
-
concurrent-ruby (1.1.
|
|
27
|
+
concurrent-ruby (1.1.7)
|
|
28
28
|
deep_merge (1.2.1)
|
|
29
29
|
diff-lcs (1.4.4)
|
|
30
30
|
ecma-re-validator (0.2.1)
|
|
@@ -39,16 +39,16 @@ GEM
|
|
|
39
39
|
transproc (~> 1.0)
|
|
40
40
|
hansi (0.2.0)
|
|
41
41
|
hash-deep-merge (0.1.1)
|
|
42
|
-
i18n (1.8.
|
|
42
|
+
i18n (1.8.5)
|
|
43
43
|
concurrent-ruby (~> 1.0)
|
|
44
|
-
json_schemer (0.2.
|
|
44
|
+
json_schemer (0.2.16)
|
|
45
45
|
ecma-re-validator (~> 0.2)
|
|
46
46
|
hana (~> 1.3)
|
|
47
47
|
regexp_parser (~> 1.5)
|
|
48
48
|
uri_template (~> 0.7)
|
|
49
49
|
method_source (1.0.0)
|
|
50
50
|
mini_portile2 (2.4.0)
|
|
51
|
-
minitest (5.14.
|
|
51
|
+
minitest (5.14.2)
|
|
52
52
|
multi_json (1.15.0)
|
|
53
53
|
mustermann (1.1.1)
|
|
54
54
|
ruby2_keywords (~> 0.0.1)
|
|
@@ -66,24 +66,24 @@ GEM
|
|
|
66
66
|
mustermann-contrib (~> 1.1.1)
|
|
67
67
|
nokogiri
|
|
68
68
|
parallel (1.19.2)
|
|
69
|
-
parser (2.7.
|
|
69
|
+
parser (2.7.2.0)
|
|
70
70
|
ast (~> 2.4.1)
|
|
71
71
|
pry (0.13.1)
|
|
72
72
|
coderay (~> 1.1)
|
|
73
73
|
method_source (~> 1.0)
|
|
74
|
-
public_suffix (4.0.
|
|
74
|
+
public_suffix (4.0.6)
|
|
75
75
|
rack (2.2.3)
|
|
76
76
|
rack-test (1.1.0)
|
|
77
77
|
rack (>= 1.0, < 3)
|
|
78
78
|
rainbow (3.0.0)
|
|
79
79
|
rake (13.0.1)
|
|
80
|
-
regexp_parser (1.
|
|
80
|
+
regexp_parser (1.8.2)
|
|
81
81
|
rexml (3.2.4)
|
|
82
82
|
rspec (3.9.0)
|
|
83
83
|
rspec-core (~> 3.9.0)
|
|
84
84
|
rspec-expectations (~> 3.9.0)
|
|
85
85
|
rspec-mocks (~> 3.9.0)
|
|
86
|
-
rspec-core (3.9.
|
|
86
|
+
rspec-core (3.9.3)
|
|
87
87
|
rspec-support (~> 3.9.3)
|
|
88
88
|
rspec-expectations (3.9.2)
|
|
89
89
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
@@ -92,26 +92,26 @@ GEM
|
|
|
92
92
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
93
93
|
rspec-support (~> 3.9.0)
|
|
94
94
|
rspec-support (3.9.3)
|
|
95
|
-
rubocop (0.
|
|
95
|
+
rubocop (0.93.1)
|
|
96
96
|
parallel (~> 1.10)
|
|
97
|
-
parser (>= 2.7.1.
|
|
97
|
+
parser (>= 2.7.1.5)
|
|
98
98
|
rainbow (>= 2.2.2, < 4.0)
|
|
99
|
-
regexp_parser (>= 1.
|
|
99
|
+
regexp_parser (>= 1.8)
|
|
100
100
|
rexml
|
|
101
|
-
rubocop-ast (>= 0.
|
|
101
|
+
rubocop-ast (>= 0.6.0)
|
|
102
102
|
ruby-progressbar (~> 1.7)
|
|
103
103
|
unicode-display_width (>= 1.4.0, < 2.0)
|
|
104
|
-
rubocop-ast (0.
|
|
105
|
-
parser (>= 2.7.
|
|
104
|
+
rubocop-ast (0.8.0)
|
|
105
|
+
parser (>= 2.7.1.5)
|
|
106
106
|
ruby-progressbar (1.10.1)
|
|
107
107
|
ruby2_keywords (0.0.2)
|
|
108
108
|
thread_safe (0.3.6)
|
|
109
109
|
transproc (1.1.1)
|
|
110
|
-
tzinfo (1.2.
|
|
110
|
+
tzinfo (1.2.8)
|
|
111
111
|
thread_safe (~> 0.1)
|
|
112
112
|
unicode-display_width (1.7.0)
|
|
113
113
|
uri_template (0.7.0)
|
|
114
|
-
zeitwerk (2.
|
|
114
|
+
zeitwerk (2.4.1)
|
|
115
115
|
|
|
116
116
|
PLATFORMS
|
|
117
117
|
ruby
|
data/README.md
CHANGED
|
@@ -19,7 +19,7 @@ OpenapiFirst consists of these Rack middlewares:
|
|
|
19
19
|
|
|
20
20
|
- [`OpenapiFirst::Router`](#OpenapiFirst::Router) – Finds the OpenAPI operation for the current request or returns 404 if no operation was found. This can be customized.
|
|
21
21
|
- [`OpenapiFirst::RequestValidation`](#OpenapiFirst::RequestValidation) – Validates the request against the API description and returns 400 if the request is invalid.
|
|
22
|
-
- [`OpenapiFirst::Responder`](#OpenapiFirst::Responder) calls the [handler](#handlers) found for the operation.
|
|
22
|
+
- [`OpenapiFirst::Responder`](#OpenapiFirst::Responder) calls the [handler](#handlers) found for the operation, sets the correct content-type and serialized the response body to json if needed.
|
|
23
23
|
- [`OpenapiFirst::ResponseValidation`](#OpenapiFirst::ResponseValidation) Validates the response and raises an exception if the response body is invalid.
|
|
24
24
|
|
|
25
25
|
## OpenapiFirst::Router
|
|
@@ -37,6 +37,7 @@ Options and their defaults:
|
|
|
37
37
|
|:---|---|---|---|
|
|
38
38
|
|`spec:`| | The spec loaded via `OpenapiFirst.load` ||
|
|
39
39
|
| `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)
|
|
40
|
+
| `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)
|
|
40
41
|
|
|
41
42
|
## OpenapiFirst::RequestValidation
|
|
42
43
|
|
|
@@ -78,7 +79,12 @@ This middleware adds `env[OpenapiFirst::INBOX]` which holds the (filtered) path
|
|
|
78
79
|
### Parameter validation
|
|
79
80
|
|
|
80
81
|
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.
|
|
81
|
-
|
|
82
|
+
|
|
83
|
+
It just works with a parameter with `name: filter[age]`.
|
|
84
|
+
|
|
85
|
+
OpenapiFirst also supports `type: array` for query parameters and will convert `items` just as described above. [`style`](http://spec.openapis.org/oas/v3.0.3#style-values) and `explode` attributes are not supported for query parameters. It will always act as if `style: form` and `explode: false` were used for query parameters.
|
|
86
|
+
|
|
87
|
+
Conversion is currently done only for path and query parameters, but not for the request body. OpenapiFirst currently does not convert date, date-time or time formats.
|
|
82
88
|
|
|
83
89
|
If you want to forbid _nested_ query parameters you will need to use [`additionalProperties: false`](https://json-schema.org/understanding-json-schema/reference/object.html#properties) in your query parameter JSON schema.
|
|
84
90
|
|
|
@@ -93,6 +99,12 @@ This will also add the parsed request body to `env[OpenapiFirst::REQUEST_BODY]`.
|
|
|
93
99
|
|
|
94
100
|
tbd.
|
|
95
101
|
|
|
102
|
+
### readOnly / writeOnly properties
|
|
103
|
+
|
|
104
|
+
Request validation fails if request includes a property with `readOnly: true`.
|
|
105
|
+
|
|
106
|
+
Response validation fails if response body includes a property with `writeOnly: true`.
|
|
107
|
+
|
|
96
108
|
## OpenapiFirst::Responder
|
|
97
109
|
|
|
98
110
|
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.
|
data/benchmarks/Gemfile
CHANGED
data/benchmarks/Gemfile.lock
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: ..
|
|
3
3
|
specs:
|
|
4
|
-
openapi_first (0.
|
|
4
|
+
openapi_first (0.13.3)
|
|
5
5
|
deep_merge (>= 1.2.1)
|
|
6
6
|
hanami-router (~> 2.0.alpha3)
|
|
7
7
|
hanami-utils (~> 2.0.alpha1)
|
|
8
|
-
json_schemer (~> 0.2)
|
|
8
|
+
json_schemer (~> 0.2.16)
|
|
9
9
|
multi_json (~> 1.14)
|
|
10
10
|
oas_parser (~> 0.25.1)
|
|
11
11
|
rack (~> 2.2)
|
|
@@ -13,7 +13,7 @@ PATH
|
|
|
13
13
|
GEM
|
|
14
14
|
remote: https://rubygems.org/
|
|
15
15
|
specs:
|
|
16
|
-
activesupport (6.0.3.
|
|
16
|
+
activesupport (6.0.3.4)
|
|
17
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
18
18
|
i18n (>= 0.7, < 2)
|
|
19
19
|
minitest (~> 5.1)
|
|
@@ -21,15 +21,15 @@ GEM
|
|
|
21
21
|
zeitwerk (~> 2.2, >= 2.2.2)
|
|
22
22
|
addressable (2.7.0)
|
|
23
23
|
public_suffix (>= 2.0.2, < 5.0)
|
|
24
|
-
benchmark-ips (2.8.
|
|
24
|
+
benchmark-ips (2.8.3)
|
|
25
25
|
benchmark-memory (0.1.2)
|
|
26
26
|
memory_profiler (~> 0.9)
|
|
27
27
|
builder (3.2.4)
|
|
28
|
-
committee (4.
|
|
28
|
+
committee (4.2.0)
|
|
29
29
|
json_schema (~> 0.14, >= 0.14.3)
|
|
30
30
|
openapi_parser (>= 0.11.1)
|
|
31
31
|
rack (>= 1.5)
|
|
32
|
-
concurrent-ruby (1.1.
|
|
32
|
+
concurrent-ruby (1.1.7)
|
|
33
33
|
deep_merge (1.2.1)
|
|
34
34
|
dry-configurable (0.11.6)
|
|
35
35
|
concurrent-ruby (~> 1.0)
|
|
@@ -42,7 +42,7 @@ GEM
|
|
|
42
42
|
concurrent-ruby (~> 1.0)
|
|
43
43
|
dry-equalizer (0.3.0)
|
|
44
44
|
dry-inflector (0.2.0)
|
|
45
|
-
dry-logic (1.0.
|
|
45
|
+
dry-logic (1.0.8)
|
|
46
46
|
concurrent-ruby (~> 1.0)
|
|
47
47
|
dry-core (~> 0.2)
|
|
48
48
|
dry-equalizer (~> 0.2)
|
|
@@ -55,7 +55,7 @@ GEM
|
|
|
55
55
|
dry-logic (~> 1.0, >= 1.0.2)
|
|
56
56
|
ecma-re-validator (0.2.1)
|
|
57
57
|
regexp_parser (~> 1.2)
|
|
58
|
-
grape (1.
|
|
58
|
+
grape (1.5.0)
|
|
59
59
|
activesupport
|
|
60
60
|
builder
|
|
61
61
|
dry-types (>= 1.1)
|
|
@@ -63,6 +63,8 @@ GEM
|
|
|
63
63
|
rack (>= 1.3.0)
|
|
64
64
|
rack-accept
|
|
65
65
|
hana (1.3.6)
|
|
66
|
+
hanami-api (0.1.1)
|
|
67
|
+
hanami-router (~> 2.0.alpha)
|
|
66
68
|
hanami-router (2.0.0.alpha3)
|
|
67
69
|
mustermann (~> 1.0)
|
|
68
70
|
mustermann-contrib (~> 1.0)
|
|
@@ -72,17 +74,17 @@ GEM
|
|
|
72
74
|
transproc (~> 1.0)
|
|
73
75
|
hansi (0.2.0)
|
|
74
76
|
hash-deep-merge (0.1.1)
|
|
75
|
-
i18n (1.8.
|
|
77
|
+
i18n (1.8.5)
|
|
76
78
|
concurrent-ruby (~> 1.0)
|
|
77
79
|
json_schema (0.20.9)
|
|
78
|
-
json_schemer (0.2.
|
|
80
|
+
json_schemer (0.2.16)
|
|
79
81
|
ecma-re-validator (~> 0.2)
|
|
80
82
|
hana (~> 1.3)
|
|
81
83
|
regexp_parser (~> 1.5)
|
|
82
84
|
uri_template (~> 0.7)
|
|
83
85
|
memory_profiler (0.9.14)
|
|
84
86
|
mini_portile2 (2.4.0)
|
|
85
|
-
minitest (5.14.
|
|
87
|
+
minitest (5.14.2)
|
|
86
88
|
multi_json (1.15.0)
|
|
87
89
|
mustermann (1.1.1)
|
|
88
90
|
ruby2_keywords (~> 0.0.1)
|
|
@@ -93,7 +95,7 @@ GEM
|
|
|
93
95
|
mustermann (>= 1.0.0)
|
|
94
96
|
nokogiri (1.10.10)
|
|
95
97
|
mini_portile2 (~> 2.4.0)
|
|
96
|
-
oas_parser (0.25.
|
|
98
|
+
oas_parser (0.25.2)
|
|
97
99
|
activesupport (>= 4.0.0)
|
|
98
100
|
addressable (~> 2.3)
|
|
99
101
|
builder (~> 3.2.3)
|
|
@@ -101,20 +103,20 @@ GEM
|
|
|
101
103
|
hash-deep-merge
|
|
102
104
|
mustermann-contrib (~> 1.1.1)
|
|
103
105
|
nokogiri
|
|
104
|
-
openapi_parser (0.
|
|
105
|
-
public_suffix (4.0.
|
|
106
|
+
openapi_parser (0.12.1)
|
|
107
|
+
public_suffix (4.0.6)
|
|
106
108
|
rack (2.2.3)
|
|
107
109
|
rack-accept (0.4.5)
|
|
108
110
|
rack (>= 0.4)
|
|
109
|
-
rack-protection (2.0
|
|
111
|
+
rack-protection (2.1.0)
|
|
110
112
|
rack
|
|
111
|
-
regexp_parser (1.
|
|
113
|
+
regexp_parser (1.8.2)
|
|
112
114
|
ruby2_keywords (0.0.2)
|
|
113
115
|
seg (1.2.0)
|
|
114
|
-
sinatra (2.0
|
|
116
|
+
sinatra (2.1.0)
|
|
115
117
|
mustermann (~> 1.0)
|
|
116
|
-
rack (~> 2.
|
|
117
|
-
rack-protection (= 2.0
|
|
118
|
+
rack (~> 2.2)
|
|
119
|
+
rack-protection (= 2.1.0)
|
|
118
120
|
tilt (~> 2.0)
|
|
119
121
|
syro (3.2.0)
|
|
120
122
|
rack (>= 1.6.0)
|
|
@@ -125,7 +127,7 @@ GEM
|
|
|
125
127
|
tzinfo (1.2.7)
|
|
126
128
|
thread_safe (~> 0.1)
|
|
127
129
|
uri_template (0.7.0)
|
|
128
|
-
zeitwerk (2.
|
|
130
|
+
zeitwerk (2.4.0)
|
|
129
131
|
|
|
130
132
|
PLATFORMS
|
|
131
133
|
ruby
|
|
@@ -135,7 +137,8 @@ DEPENDENCIES
|
|
|
135
137
|
benchmark-memory
|
|
136
138
|
committee
|
|
137
139
|
grape
|
|
138
|
-
hanami-
|
|
140
|
+
hanami-api
|
|
141
|
+
hanami-router (~> 2.0.0.alpha3)
|
|
139
142
|
multi_json
|
|
140
143
|
openapi_first!
|
|
141
144
|
sinatra
|
|
@@ -1,30 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'committee'
|
|
4
|
-
require 'syro'
|
|
5
3
|
require 'multi_json'
|
|
4
|
+
require 'committee'
|
|
5
|
+
require 'hanami/api'
|
|
6
6
|
|
|
7
|
-
app =
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
res.json MultiJson.dump(hello: 'world', id: inbox[:id])
|
|
12
|
-
end
|
|
13
|
-
end
|
|
7
|
+
app = Class.new(Hanami::API) do
|
|
8
|
+
get '/hello/:id' do
|
|
9
|
+
json(hello: 'world', id: params.fetch(:id))
|
|
10
|
+
end
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
get '/hello' do
|
|
13
|
+
json([{ hello: 'world' }])
|
|
14
|
+
end
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
end
|
|
16
|
+
post '/hello' do
|
|
17
|
+
status 201
|
|
18
|
+
json(hello: 'world')
|
|
23
19
|
end
|
|
24
|
-
end
|
|
20
|
+
end.new
|
|
25
21
|
|
|
26
|
-
use Committee::Middleware::RequestValidation,
|
|
27
|
-
schema_path: './apps/openapi.yaml',
|
|
28
|
-
coerce_date_times: false
|
|
22
|
+
use Committee::Middleware::RequestValidation, schema_path: './apps/openapi.yaml'
|
|
29
23
|
|
|
30
24
|
run app
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'multi_json'
|
|
4
|
+
require 'hanami/api'
|
|
5
|
+
|
|
6
|
+
app = Class.new(Hanami::API) do
|
|
7
|
+
get '/hello/:id' do
|
|
8
|
+
json(hello: 'world', id: params.fetch(:id))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
get '/hello' do
|
|
12
|
+
json([{ hello: 'world' }])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
post '/hello' do
|
|
16
|
+
status 201
|
|
17
|
+
json(hello: 'world')
|
|
18
|
+
end
|
|
19
|
+
end.new
|
|
20
|
+
|
|
21
|
+
run app
|
|
@@ -4,7 +4,7 @@ require 'hanami/router'
|
|
|
4
4
|
require 'multi_json'
|
|
5
5
|
|
|
6
6
|
app = Hanami::Router.new do
|
|
7
|
-
get '/hello', to: ->(_env) { [200, {}, [MultiJson.dump(hello: 'world')]] }
|
|
7
|
+
get '/hello', to: ->(_env) { [200, {}, [MultiJson.dump([{ hello: 'world' }])]] }
|
|
8
8
|
get '/hello/:id', to: lambda { |env|
|
|
9
9
|
[200, {}, [MultiJson.dump(hello: 'world', id: env['router.params'][:id])]]
|
|
10
10
|
}
|
data/benchmarks/apps/sinatra.ru
CHANGED
data/benchmarks/apps/syro.ru
CHANGED
|
@@ -7,17 +7,17 @@ app = Syro.new do
|
|
|
7
7
|
on 'hello' do
|
|
8
8
|
on :id do
|
|
9
9
|
get do
|
|
10
|
-
res.json
|
|
10
|
+
res.json({ hello: 'world', id: inbox[:id] })
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
get do
|
|
15
|
-
res.json
|
|
15
|
+
res.json([{ hello: 'world' }])
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
post do
|
|
19
19
|
res.status = 201
|
|
20
|
-
res.json
|
|
20
|
+
res.json({ hello: 'world' })
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -4,15 +4,11 @@ require_relative 'operation'
|
|
|
4
4
|
|
|
5
5
|
module OpenapiFirst
|
|
6
6
|
class Definition
|
|
7
|
-
attr_reader :filepath
|
|
7
|
+
attr_reader :filepath, :operations
|
|
8
8
|
|
|
9
9
|
def initialize(parsed)
|
|
10
10
|
@filepath = parsed.path
|
|
11
|
-
@
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def operations
|
|
15
|
-
@spec.endpoints.map { |e| Operation.new(e) }
|
|
11
|
+
@operations = parsed.endpoints.map { |e| Operation.new(e) }
|
|
16
12
|
end
|
|
17
13
|
end
|
|
18
14
|
end
|
|
@@ -9,10 +9,8 @@ module OpenapiFirst
|
|
|
9
9
|
@handlers = spec.operations.each_with_object({}) do |operation, hash|
|
|
10
10
|
operation_id = operation.operation_id
|
|
11
11
|
handler = find_handler(operation_id)
|
|
12
|
-
if handler.nil?
|
|
13
|
-
|
|
14
|
-
next
|
|
15
|
-
end
|
|
12
|
+
next if handler.nil?
|
|
13
|
+
|
|
16
14
|
hash[operation_id] = handler
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'forwardable'
|
|
4
4
|
require 'json_schemer'
|
|
5
|
+
require_relative 'schema_validation'
|
|
5
6
|
require_relative 'utils'
|
|
6
7
|
require_relative 'response_object'
|
|
7
8
|
|
|
@@ -14,6 +15,9 @@ module OpenapiFirst
|
|
|
14
15
|
:request_body,
|
|
15
16
|
:operation_id
|
|
16
17
|
|
|
18
|
+
WRITE_METHODS = Set.new(%w[post put patch delete]).freeze
|
|
19
|
+
private_constant :WRITE_METHODS
|
|
20
|
+
|
|
17
21
|
def initialize(parsed)
|
|
18
22
|
@operation = parsed
|
|
19
23
|
end
|
|
@@ -22,12 +26,19 @@ module OpenapiFirst
|
|
|
22
26
|
@operation.path.path
|
|
23
27
|
end
|
|
24
28
|
|
|
25
|
-
def
|
|
26
|
-
|
|
29
|
+
def read?
|
|
30
|
+
!write?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def write?
|
|
34
|
+
WRITE_METHODS.include?(method)
|
|
27
35
|
end
|
|
28
36
|
|
|
29
37
|
def parameters_schema
|
|
30
|
-
@parameters_schema ||=
|
|
38
|
+
@parameters_schema ||= begin
|
|
39
|
+
parameters_json_schema = build_parameters_json_schema
|
|
40
|
+
parameters_json_schema && SchemaValidation.new(parameters_json_schema)
|
|
41
|
+
end
|
|
31
42
|
end
|
|
32
43
|
|
|
33
44
|
def content_type_for(status)
|
|
@@ -41,12 +52,22 @@ module OpenapiFirst
|
|
|
41
52
|
|
|
42
53
|
raise ResponseInvalid, "Response has no content-type for '#{name}'" unless content_type
|
|
43
54
|
|
|
44
|
-
media_type = content
|
|
55
|
+
media_type = find_content_for_content_type(content, content_type)
|
|
45
56
|
unless media_type
|
|
46
57
|
message = "Response content type not found '#{content_type}' for '#{name}'"
|
|
47
58
|
raise ResponseContentTypeNotFoundError, message
|
|
48
59
|
end
|
|
49
|
-
media_type['schema']
|
|
60
|
+
schema = media_type['schema']
|
|
61
|
+
SchemaValidation.new(schema, write: false) if schema
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def request_body_schema(request_content_type)
|
|
65
|
+
content = @operation.request_body.content
|
|
66
|
+
media_type = find_content_for_content_type(content, request_content_type)
|
|
67
|
+
schema = media_type&.fetch('schema', nil)
|
|
68
|
+
return unless schema
|
|
69
|
+
|
|
70
|
+
SchemaValidation.new(schema, write: write?)
|
|
50
71
|
end
|
|
51
72
|
|
|
52
73
|
def response_for(status)
|
|
@@ -62,6 +83,13 @@ module OpenapiFirst
|
|
|
62
83
|
|
|
63
84
|
private
|
|
64
85
|
|
|
86
|
+
def find_content_for_content_type(content, request_content_type)
|
|
87
|
+
content.fetch(request_content_type) do |_|
|
|
88
|
+
type = request_content_type.split(';')[0]
|
|
89
|
+
content[type] || content["#{type.split('/')[0]}/*"] || content['*/*']
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
65
93
|
def build_parameters_json_schema
|
|
66
94
|
return unless @operation.parameters&.any?
|
|
67
95
|
|
|
@@ -71,7 +99,7 @@ module OpenapiFirst
|
|
|
71
99
|
end
|
|
72
100
|
end
|
|
73
101
|
|
|
74
|
-
def generate_schema(schema, params, parameter)
|
|
102
|
+
def generate_schema(schema, params, parameter)
|
|
75
103
|
required = Set.new(schema['required'])
|
|
76
104
|
params.each do |key, value|
|
|
77
105
|
required << key if parameter.required
|
|
@@ -16,7 +16,7 @@ module OpenapiFirst
|
|
|
16
16
|
@raise = raise_error
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def call(env) # rubocop:disable Metrics/AbcSize
|
|
19
|
+
def call(env) # rubocop:disable Metrics/AbcSize
|
|
20
20
|
operation = env[OpenapiFirst::OPERATION]
|
|
21
21
|
return @app.call(env) unless operation
|
|
22
22
|
|
|
@@ -45,17 +45,17 @@ module OpenapiFirst
|
|
|
45
45
|
validate_request_body_presence!(body, operation)
|
|
46
46
|
return if body.empty?
|
|
47
47
|
|
|
48
|
-
schema = request_body_schema(content_type
|
|
48
|
+
schema = operation&.request_body_schema(content_type)
|
|
49
49
|
return unless schema
|
|
50
50
|
|
|
51
51
|
parsed_request_body = parse_request_body!(body)
|
|
52
|
-
errors =
|
|
52
|
+
errors = schema.validate(parsed_request_body)
|
|
53
53
|
halt_with_error(400, serialize_request_body_errors(errors)) if errors.any?
|
|
54
|
-
env[INBOX].merge! env[REQUEST_BODY] = parsed_request_body
|
|
54
|
+
env[INBOX].merge! env[REQUEST_BODY] = Utils.deep_symbolize(parsed_request_body)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def parse_request_body!(body)
|
|
58
|
-
MultiJson.load(body
|
|
58
|
+
MultiJson.load(body)
|
|
59
59
|
rescue MultiJson::ParseError => e
|
|
60
60
|
err = { title: 'Failed to parse body as JSON' }
|
|
61
61
|
err[:detail] = e.cause unless ENV['RACK_ENV'] == 'production'
|
|
@@ -74,10 +74,6 @@ module OpenapiFirst
|
|
|
74
74
|
halt_with_error(415, 'Request body is required')
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
def validate_json_schema(schema, object)
|
|
78
|
-
schema.validate(Utils.deep_stringify(object))
|
|
79
|
-
end
|
|
80
|
-
|
|
81
77
|
def default_error(status, title = Rack::Utils::HTTP_STATUS_CODES[status])
|
|
82
78
|
{
|
|
83
79
|
status: status.to_s,
|
|
@@ -95,13 +91,6 @@ module OpenapiFirst
|
|
|
95
91
|
).finish
|
|
96
92
|
end
|
|
97
93
|
|
|
98
|
-
def request_body_schema(content_type, operation)
|
|
99
|
-
return unless operation
|
|
100
|
-
|
|
101
|
-
schema = operation.request_body.content[content_type]&.fetch('schema')
|
|
102
|
-
JSONSchemer.schema(schema) if schema
|
|
103
|
-
end
|
|
104
|
-
|
|
105
94
|
def serialize_request_body_errors(validation_errors)
|
|
106
95
|
validation_errors.map do |error|
|
|
107
96
|
{
|
|
@@ -113,14 +102,11 @@ module OpenapiFirst
|
|
|
113
102
|
end
|
|
114
103
|
|
|
115
104
|
def validate_query_parameters!(env, operation, params)
|
|
116
|
-
|
|
117
|
-
return unless
|
|
118
|
-
|
|
119
|
-
params = filtered_params(
|
|
120
|
-
errors =
|
|
121
|
-
operation.parameters_schema,
|
|
122
|
-
params
|
|
123
|
-
)
|
|
105
|
+
schema = operation.parameters_schema
|
|
106
|
+
return unless schema
|
|
107
|
+
|
|
108
|
+
params = filtered_params(schema.raw_schema, params)
|
|
109
|
+
errors = schema.validate(Utils.deep_stringify(params))
|
|
124
110
|
halt_with_error(400, serialize_query_parameter_errors(errors)) if errors.any?
|
|
125
111
|
env[PARAMETERS] = params
|
|
126
112
|
env[INBOX].merge! params
|
|
@@ -140,8 +126,9 @@ module OpenapiFirst
|
|
|
140
126
|
|
|
141
127
|
def serialize_query_parameter_errors(validation_errors)
|
|
142
128
|
validation_errors.map do |error|
|
|
129
|
+
pointer = error['data_pointer'][1..].to_s
|
|
143
130
|
{
|
|
144
|
-
source: { parameter:
|
|
131
|
+
source: { parameter: pointer }
|
|
145
132
|
}.update(ValidationFormat.error_details(error))
|
|
146
133
|
end
|
|
147
134
|
end
|
|
@@ -149,14 +136,27 @@ module OpenapiFirst
|
|
|
149
136
|
def parse_parameter(value, schema)
|
|
150
137
|
return filtered_params(schema, value) if schema['properties']
|
|
151
138
|
|
|
139
|
+
return parse_array_parameter(value, schema) if schema['type'] == 'array'
|
|
140
|
+
|
|
141
|
+
parse_simple_value(value, schema)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def parse_array_parameter(value, schema)
|
|
145
|
+
array = value.is_a?(Array) ? value : value.split(',')
|
|
146
|
+
return array unless schema['items']
|
|
147
|
+
|
|
148
|
+
array.map! { |e| parse_simple_value(e, schema['items']) }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def parse_simple_value(value, schema)
|
|
152
|
+
return to_boolean(value) if schema['type'] == 'boolean'
|
|
153
|
+
|
|
152
154
|
begin
|
|
153
155
|
return Integer(value, 10) if schema['type'] == 'integer'
|
|
154
156
|
return Float(value) if schema['type'] == 'number'
|
|
155
157
|
rescue ArgumentError
|
|
156
158
|
value
|
|
157
159
|
end
|
|
158
|
-
return to_boolean(value) if schema['type'] == 'boolean'
|
|
159
|
-
|
|
160
160
|
value
|
|
161
161
|
end
|
|
162
162
|
|
|
@@ -41,7 +41,8 @@ module OpenapiFirst
|
|
|
41
41
|
full_body = +''
|
|
42
42
|
response.each { |chunk| full_body << chunk }
|
|
43
43
|
data = full_body.empty? ? {} : load_json(full_body)
|
|
44
|
-
errors =
|
|
44
|
+
errors = schema.validate(data)
|
|
45
|
+
errors = errors.to_a.map! do |error|
|
|
45
46
|
error_message_for(error)
|
|
46
47
|
end
|
|
47
48
|
raise ResponseBodyInvalidError, errors.join(', ') if errors.any?
|
data/lib/openapi_first/router.rb
CHANGED
|
@@ -10,11 +10,13 @@ module OpenapiFirst
|
|
|
10
10
|
app,
|
|
11
11
|
spec:,
|
|
12
12
|
raise_error: false,
|
|
13
|
+
not_found: :halt,
|
|
13
14
|
parent_app: nil
|
|
14
15
|
)
|
|
15
16
|
@app = app
|
|
16
17
|
@parent_app = parent_app
|
|
17
18
|
@raise = raise_error
|
|
19
|
+
@not_found = not_found
|
|
18
20
|
@filepath = spec.filepath
|
|
19
21
|
@router = build_router(spec.operations)
|
|
20
22
|
end
|
|
@@ -23,9 +25,11 @@ module OpenapiFirst
|
|
|
23
25
|
env[OPERATION] = nil
|
|
24
26
|
response = call_router(env)
|
|
25
27
|
if env[OPERATION].nil?
|
|
26
|
-
return @parent_app.call(env) if @parent_app # This should only happen if used via OpenapiFirst.
|
|
28
|
+
return @parent_app.call(env) if @parent_app # This should only happen if used via OpenapiFirst.middleware
|
|
27
29
|
|
|
28
30
|
raise_error(env) if @raise
|
|
31
|
+
|
|
32
|
+
return @app.call(env) if @not_found == :continue
|
|
29
33
|
end
|
|
30
34
|
response
|
|
31
35
|
end
|
|
@@ -49,13 +53,12 @@ module OpenapiFirst
|
|
|
49
53
|
env[Rack::PATH_INFO] = env.delete(ORIGINAL_PATH) if env[ORIGINAL_PATH]
|
|
50
54
|
end
|
|
51
55
|
|
|
52
|
-
def build_router(operations) # rubocop:disable Metrics/AbcSize
|
|
56
|
+
def build_router(operations) # rubocop:disable Metrics/AbcSize
|
|
53
57
|
router = Hanami::Router.new {}
|
|
54
58
|
operations.each do |operation|
|
|
55
59
|
normalized_path = operation.path.gsub('{', ':').gsub('}', '')
|
|
56
60
|
if operation.operation_id.nil?
|
|
57
61
|
warn "operationId is missing in '#{operation.method} #{operation.path}'. I am ignoring this operation."
|
|
58
|
-
next
|
|
59
62
|
end
|
|
60
63
|
router.public_send(
|
|
61
64
|
operation.method,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json_schemer'
|
|
4
|
+
|
|
5
|
+
module OpenapiFirst
|
|
6
|
+
class SchemaValidation
|
|
7
|
+
attr_reader :raw_schema
|
|
8
|
+
|
|
9
|
+
def initialize(schema, write: true)
|
|
10
|
+
@raw_schema = schema
|
|
11
|
+
custom_keywords = {}
|
|
12
|
+
custom_keywords['writeOnly'] = proc { |data| !data } unless write
|
|
13
|
+
custom_keywords['readOnly'] = proc { |data| !data } if write
|
|
14
|
+
@schemer = JSONSchemer.schema(
|
|
15
|
+
schema,
|
|
16
|
+
keywords: custom_keywords,
|
|
17
|
+
before_property_validation: proc do |data, property, property_schema, parent|
|
|
18
|
+
convert_nullable(data, property, property_schema, parent)
|
|
19
|
+
end
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def validate(input)
|
|
24
|
+
@schemer.validate(input)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def convert_nullable(_data, _property, property_schema, _parent)
|
|
30
|
+
return unless property_schema.is_a?(Hash) && property_schema['nullable'] && property_schema['type']
|
|
31
|
+
|
|
32
|
+
property_schema['type'] = [*property_schema['type'], 'null']
|
|
33
|
+
property_schema.delete('nullable')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/openapi_first/utils.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'hanami/utils/string'
|
|
4
|
+
require 'hanami/utils/hash'
|
|
4
5
|
require 'deep_merge/core'
|
|
5
6
|
|
|
6
7
|
module OpenapiFirst
|
|
@@ -17,20 +18,12 @@ module OpenapiFirst
|
|
|
17
18
|
Hanami::Utils::String.classify(string)
|
|
18
19
|
end
|
|
19
20
|
|
|
20
|
-
def self.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
when Array
|
|
27
|
-
value.map do |item|
|
|
28
|
-
item.is_a?(::Hash) ? deep_stringify(item) : item
|
|
29
|
-
end
|
|
30
|
-
else
|
|
31
|
-
value
|
|
32
|
-
end
|
|
33
|
-
end
|
|
21
|
+
def self.deep_symbolize(hash)
|
|
22
|
+
Hanami::Utils::Hash.deep_symbolize(hash)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.deep_stringify(hash)
|
|
26
|
+
Hanami::Utils::Hash.deep_stringify(hash)
|
|
34
27
|
end
|
|
35
28
|
end
|
|
36
29
|
end
|
|
@@ -6,17 +6,32 @@ module OpenapiFirst
|
|
|
6
6
|
|
|
7
7
|
# rubocop:disable Metrics/MethodLength
|
|
8
8
|
# rubocop:disable Metrics/AbcSize
|
|
9
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
10
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
9
11
|
def self.error_details(error)
|
|
10
12
|
if error['type'] == 'pattern'
|
|
11
13
|
{
|
|
12
14
|
title: 'is not valid',
|
|
13
15
|
detail: "does not match pattern '#{error['schema']['pattern']}'"
|
|
14
16
|
}
|
|
17
|
+
elsif error['type'] == 'format'
|
|
18
|
+
{
|
|
19
|
+
title: "has not a valid #{error.dig('schema', 'format')} format",
|
|
20
|
+
detail: "#{error['data'].inspect} is not a valid #{error.dig('schema', 'format')} format"
|
|
21
|
+
}
|
|
15
22
|
elsif error['type'] == 'required'
|
|
16
23
|
missing_keys = error['details']['missing_keys']
|
|
17
24
|
{
|
|
18
25
|
title: "is missing required properties: #{missing_keys.join(', ')}"
|
|
19
26
|
}
|
|
27
|
+
elsif error['type'] == 'readOnly'
|
|
28
|
+
{
|
|
29
|
+
title: 'appears in request, but is read-only'
|
|
30
|
+
}
|
|
31
|
+
elsif error['type'] == 'writeOnly'
|
|
32
|
+
{
|
|
33
|
+
title: 'write-only field appears in response:'
|
|
34
|
+
}
|
|
20
35
|
elsif SIMPLE_TYPES.include?(error['type'])
|
|
21
36
|
{
|
|
22
37
|
title: "should be a #{error['type']}"
|
|
@@ -29,5 +44,7 @@ module OpenapiFirst
|
|
|
29
44
|
end
|
|
30
45
|
# rubocop:enable Metrics/MethodLength
|
|
31
46
|
# rubocop:enable Metrics/AbcSize
|
|
47
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
48
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
32
49
|
end
|
|
33
50
|
end
|
data/openapi_first.gemspec
CHANGED
|
@@ -32,10 +32,12 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.bindir = 'exe'
|
|
33
33
|
spec.require_paths = ['lib']
|
|
34
34
|
|
|
35
|
+
spec.required_ruby_version = '>= 2.6.0'
|
|
36
|
+
|
|
35
37
|
spec.add_runtime_dependency 'deep_merge', '>= 1.2.1'
|
|
36
38
|
spec.add_runtime_dependency 'hanami-router', '~> 2.0.alpha3'
|
|
37
39
|
spec.add_runtime_dependency 'hanami-utils', '~> 2.0.alpha1'
|
|
38
|
-
spec.add_runtime_dependency 'json_schemer', '~> 0.2'
|
|
40
|
+
spec.add_runtime_dependency 'json_schemer', '~> 0.2.16'
|
|
39
41
|
spec.add_runtime_dependency 'multi_json', '~> 1.14'
|
|
40
42
|
spec.add_runtime_dependency 'oas_parser', '~> 0.25.1'
|
|
41
43
|
spec.add_runtime_dependency 'rack', '~> 2.2'
|
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: 0.
|
|
4
|
+
version: 0.13.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andreas Haller
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-11-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: deep_merge
|
|
@@ -58,14 +58,14 @@ dependencies:
|
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version:
|
|
61
|
+
version: 0.2.16
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version:
|
|
68
|
+
version: 0.2.16
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: multi_json
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -164,7 +164,7 @@ dependencies:
|
|
|
164
164
|
- - "~>"
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
166
|
version: '3'
|
|
167
|
-
description:
|
|
167
|
+
description:
|
|
168
168
|
email:
|
|
169
169
|
- andreas.haller@posteo.de
|
|
170
170
|
executables: []
|
|
@@ -187,6 +187,7 @@ files:
|
|
|
187
187
|
- benchmarks/Gemfile.lock
|
|
188
188
|
- benchmarks/apps/committee.ru
|
|
189
189
|
- benchmarks/apps/grape.ru
|
|
190
|
+
- benchmarks/apps/hanami_api.ru
|
|
190
191
|
- benchmarks/apps/hanami_router.ru
|
|
191
192
|
- benchmarks/apps/openapi.yaml
|
|
192
193
|
- benchmarks/apps/openapi_first.ru
|
|
@@ -213,6 +214,7 @@ files:
|
|
|
213
214
|
- lib/openapi_first/response_validator.rb
|
|
214
215
|
- lib/openapi_first/router.rb
|
|
215
216
|
- lib/openapi_first/router_required.rb
|
|
217
|
+
- lib/openapi_first/schema_validation.rb
|
|
216
218
|
- lib/openapi_first/utils.rb
|
|
217
219
|
- lib/openapi_first/validation.rb
|
|
218
220
|
- lib/openapi_first/validation_format.rb
|
|
@@ -225,7 +227,7 @@ metadata:
|
|
|
225
227
|
https://github.com/ahx/openapi_first: https://github.com/ahx/openapi_first
|
|
226
228
|
source_code_uri: https://github.com/ahx/openapi_first
|
|
227
229
|
changelog_uri: https://github.com/ahx/openapi_first/blob/master/CHANGELOG.md
|
|
228
|
-
post_install_message:
|
|
230
|
+
post_install_message:
|
|
229
231
|
rdoc_options: []
|
|
230
232
|
require_paths:
|
|
231
233
|
- lib
|
|
@@ -233,7 +235,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
233
235
|
requirements:
|
|
234
236
|
- - ">="
|
|
235
237
|
- !ruby/object:Gem::Version
|
|
236
|
-
version:
|
|
238
|
+
version: 2.6.0
|
|
237
239
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
238
240
|
requirements:
|
|
239
241
|
- - ">="
|
|
@@ -241,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
241
243
|
version: '0'
|
|
242
244
|
requirements: []
|
|
243
245
|
rubygems_version: 3.1.2
|
|
244
|
-
signing_key:
|
|
246
|
+
signing_key:
|
|
245
247
|
specification_version: 4
|
|
246
248
|
summary: Implement REST APIs based on OpenApi.
|
|
247
249
|
test_files: []
|