openapi_first 0.14.1 → 0.16.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3791f0c4bae9c39517b960e99ea3581740eaca9ad10b6189242d7690defd621
4
- data.tar.gz: bfb77aabdba80a0406ff8909e6fcf98ef7c850465fbf562bb6dd8574bd0dd1e2
3
+ metadata.gz: 5f46ad8751619914fa7b387ecb8a0178e67a7ba070e863321463f7ae1097e250
4
+ data.tar.gz: 5961b6bff590f957a8908d62b3c87bd65438dbdfe055020c61739af2862b06cc
5
5
  SHA512:
6
- metadata.gz: b40805c6536ad7bfe932f8d42bea8580493099afdbd12667f3d646c27d2ac8cd9efe4a3dfd2a44e1460322215bfc184d4cbcf69a8b45f3b84efbcc0bfc6c2ad5
7
- data.tar.gz: ed34c78c3b0960aa670876af49124d08175ceb163b503939c0668d4d6104b20931ca823db606503cfe9a95047cf9303a2bee597f5e97ccc3d9625dc1df02c349
6
+ metadata.gz: 0e08b5d1b12f3379ef2f28ab98da925a43c7adc69ea617c305dedb06fbe63e7843cf0d861cd32d7942f7770b4f7ac9bb2762ffb179374fd15deff2b86f64899f
7
+ data.tar.gz: ac406a2a868a44b8cfb8d54a71b5c4fb1d1078f666c6f3ee683a191c83c090a8312f59bd399e089edb4062d309ca98df02ee8b2078a445c1e3b16e5e19cfd462
data/CHANGELOG.md CHANGED
@@ -1,42 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - Use a Hash instead of named arguments for middleware options for better compatibility
6
+
7
+ ## 0.15.0
8
+
9
+ - Populate default parameter values
10
+
11
+ ## 0.14.3
12
+
13
+ - Use json_refs to resolve OpenAPI file. This removes oas_parser and ActiveSupport from list of dependencies
14
+
15
+ ## 0.14.2
16
+
17
+ - Empty query parameters are parsed and request validation returns 400 if an empty string is not allowed. Note that this does not look at `allowEmptyValue` in any way, because allowEmptyValue is deprecated.
3
18
 
4
19
  ## 0.14.1
20
+
5
21
  - Bugfix: Don't mix path- and operation-level parameters for request validation
6
22
 
7
23
  ## 0.14.0
24
+
8
25
  - Handle custom x-handler field in the API description to find a handler method not based on operationId
9
26
  - Add `resolver` option to provide a custom resolver to find a handler method
10
27
 
11
28
  ## 0.13.3
29
+
12
30
  - Better error message if string does not match format
13
31
  - readOnly and writeOnly just works when used inside allOf
14
32
 
15
33
  ## 0.13.2
34
+
16
35
  - Return indicator (`source: { parameter: 'list/1' }`) in error response body when array item in query parameter is invalid
17
36
 
18
37
  ## 0.13.0
38
+
19
39
  - Add support for arrays in query parameters (style: form, explode: false)
20
40
  - Remove warning when handler is not implemented
21
41
 
22
42
  ## 0.12.5
43
+
23
44
  - Add `not_found: :continue` option to Router to make it do nothing if request is unknown
24
45
 
25
46
  ## 0.12.4
47
+
26
48
  - content-type is found while ignoring additional content-type parameters (`application/json` is found when request/response content-type is `application/json; charset=UTF8`)
27
49
  - Support wildcard mime-types when finding the content-type
28
50
 
29
51
  ## 0.12.3
52
+
30
53
  - Add `response_validation:`, `router_raise_error` options to standalone mode.
31
54
 
32
55
  ## 0.12.2
56
+
33
57
  - Allow response to have no media type object specified
34
58
 
35
59
  ## 0.12.1
60
+
36
61
  - Fix response when handler returns 404 or 405
37
62
  - Don't validate the response content if status is 204 (no content)
38
63
 
39
64
  ## 0.12.0
65
+
40
66
  - Change `ResponseValidator` to raise an exception if it found a problem
41
67
  - Params have symbolized keys now
42
68
  - Remove `not_found` option from Router. Return 405 if HTTP verb is not allowed (via Hanami::Router)
@@ -48,6 +74,7 @@
48
74
  - Add `Operation#name` that returns a human readable name for an operation
49
75
 
50
76
  ## 0.11.0
77
+
51
78
  - Raise error if you forgot to add the Router middleware
52
79
  - Make OpenapiFirst.app raise an error in test env when request path is not specified
53
80
  - Rename OperationResolver to Responder
@@ -56,48 +83,60 @@
56
83
  - Move namespace option from Router to OperationResolver
57
84
 
58
85
  ## 0.10.2
86
+
59
87
  - Return 400 if request body has invalid JSON ([issue](https://github.com/ahx/openapi_first/issues/73)) thanks Thomas Frütel
60
88
 
61
89
  ## 0.10.1
90
+
62
91
  - Fix duplicated key in `required` when generating JSON schema for `some[thing]` parameters
63
92
 
64
93
  ## 0.10.0
94
+
65
95
  - Add support for query parameters named `"some[thing]"` ([issue](https://github.com/ahx/openapi_first/issues/40))
66
96
 
67
97
  ## 0.9.0
98
+
68
99
  - Make request validation usable standalone
69
100
 
70
101
  ## 0.8.0
102
+
71
103
  - Add merged parameter and request body available to env at `env[OpenapiFirst::INBOX]` in request validation
72
104
  - Path and query parameters with `type: boolean` now get converted to `true`/`false`
73
105
  - Rename `OpenapiFirst::PARAMS` to `OpenapiFirst::PARAMETERS`
74
106
 
75
107
  ## 0.7.1
108
+
76
109
  - Add missing `require` to work with new version of `oas_parser`
77
110
 
78
111
  ## 0.7.0
112
+
79
113
  - Make use of hanami-router, because it's fast
80
114
  - Remove option `allow_unknown_query_paramerters`
81
115
  - Move the namespace option to Router
82
- - Convert numeric path and query parameters to `Integer` or `Float`
116
+ - Convert numeric path and query parameters to `Integer` or `Float`
83
117
  - Pass the Rack env if your action class' initializers accepts an argument
84
118
  - Respec rack's `env['SCRIPT_NAME']` in router
85
119
  - Add MIT license
86
120
 
87
121
  ## 0.6.10
122
+
88
123
  - Bugfix: params.env['unknown'] now returns `nil` as expected. Thanks @tristandruyen.
89
124
 
90
125
  ## 0.6.9
126
+
91
127
  - Removed radix tree, because of a bug (https://github.com/namusyaka/r2ree-ruby/issues/2)
92
128
 
93
129
  ## 0.6.8
130
+
94
131
  - Performance: About 25% performance increase (i/s) with help of c++ based radix-tree and some optimizations
95
132
  - Update dependencies
96
133
 
97
134
  ## 0.6.7
135
+
98
136
  - Fix: version number of oas_parser
99
137
 
100
138
  ## 0.6.6
139
+
101
140
  - Remove warnings for Ruby 2.7
102
141
 
103
142
  ## 0.6.5
data/Gemfile.lock CHANGED
@@ -1,121 +1,93 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openapi_first (0.14.1)
4
+ openapi_first (0.16.0.beta1)
5
5
  deep_merge (>= 1.2.1)
6
- hanami-router (~> 2.0.alpha3)
7
- hanami-utils (~> 2.0.alpha1)
6
+ hanami-router (~> 2.0.alpha5)
7
+ hanami-utils (~> 2.0.alpha3)
8
+ json_refs (>= 0.1.7)
8
9
  json_schemer (~> 0.2.16)
9
10
  multi_json (~> 1.14)
10
- oas_parser (~> 0.25.1)
11
11
  rack (~> 2.2)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (6.1.4.1)
17
- concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 1.6, < 2)
19
- minitest (>= 5.1)
20
- tzinfo (~> 2.0)
21
- zeitwerk (~> 2.3)
22
- addressable (2.8.0)
23
- public_suffix (>= 2.0.2, < 5.0)
24
16
  ast (2.4.2)
25
- builder (3.2.4)
26
17
  coderay (1.1.3)
27
18
  concurrent-ruby (1.1.9)
28
- deep_merge (1.2.1)
29
- diff-lcs (1.4.4)
19
+ deep_merge (1.2.2)
20
+ diff-lcs (1.5.0)
30
21
  dry-transformer (0.1.1)
31
- ecma-re-validator (0.3.0)
32
- regexp_parser (~> 2.0)
22
+ ecma-re-validator (0.4.0)
23
+ regexp_parser (~> 2.2)
33
24
  hana (1.3.7)
34
25
  hanami-router (2.0.0.alpha5)
35
26
  mustermann (~> 1.0)
36
27
  mustermann-contrib (~> 1.0)
37
28
  rack (~> 2.0)
38
- hanami-utils (2.0.0.alpha2)
29
+ hanami-utils (2.0.0.alpha3)
39
30
  concurrent-ruby (~> 1.0)
40
31
  dry-transformer (~> 0.1)
41
32
  hansi (0.2.0)
42
- hash-deep-merge (0.1.1)
43
- i18n (1.8.10)
44
- concurrent-ruby (~> 1.0)
33
+ json_refs (0.1.7)
34
+ hana
45
35
  json_schemer (0.2.18)
46
36
  ecma-re-validator (~> 0.3)
47
37
  hana (~> 1.3)
48
38
  regexp_parser (~> 2.0)
49
39
  uri_template (~> 0.7)
50
40
  method_source (1.0.0)
51
- mini_portile2 (2.6.1)
52
- minitest (5.14.4)
53
41
  multi_json (1.15.0)
54
42
  mustermann (1.1.1)
55
43
  ruby2_keywords (~> 0.0.1)
56
44
  mustermann-contrib (1.1.1)
57
45
  hansi (~> 0.2.0)
58
46
  mustermann (= 1.1.1)
59
- nokogiri (1.12.5)
60
- mini_portile2 (~> 2.6.1)
61
- racc (~> 1.4)
62
- oas_parser (0.25.4)
63
- activesupport (>= 4.0.0)
64
- addressable (~> 2.3)
65
- builder (~> 3.2.3)
66
- deep_merge (~> 1.2.1)
67
- hash-deep-merge
68
- mustermann-contrib (~> 1.1.1)
69
- nokogiri
70
47
  parallel (1.21.0)
71
- parser (3.0.2.0)
48
+ parser (3.1.0.0)
72
49
  ast (~> 2.4.1)
73
50
  pry (0.14.1)
74
51
  coderay (~> 1.1)
75
52
  method_source (~> 1.0)
76
- public_suffix (4.0.6)
77
- racc (1.5.2)
78
53
  rack (2.2.3)
79
54
  rack-test (1.1.0)
80
55
  rack (>= 1.0, < 3)
81
- rainbow (3.0.0)
56
+ rainbow (3.1.1)
82
57
  rake (13.0.6)
83
- regexp_parser (2.1.1)
58
+ regexp_parser (2.2.1)
84
59
  rexml (3.2.5)
85
- rspec (3.10.0)
86
- rspec-core (~> 3.10.0)
87
- rspec-expectations (~> 3.10.0)
88
- rspec-mocks (~> 3.10.0)
89
- rspec-core (3.10.1)
90
- rspec-support (~> 3.10.0)
91
- rspec-expectations (3.10.1)
60
+ rspec (3.11.0)
61
+ rspec-core (~> 3.11.0)
62
+ rspec-expectations (~> 3.11.0)
63
+ rspec-mocks (~> 3.11.0)
64
+ rspec-core (3.11.0)
65
+ rspec-support (~> 3.11.0)
66
+ rspec-expectations (3.11.0)
92
67
  diff-lcs (>= 1.2.0, < 2.0)
93
- rspec-support (~> 3.10.0)
94
- rspec-mocks (3.10.2)
68
+ rspec-support (~> 3.11.0)
69
+ rspec-mocks (3.11.0)
95
70
  diff-lcs (>= 1.2.0, < 2.0)
96
- rspec-support (~> 3.10.0)
97
- rspec-support (3.10.2)
98
- rubocop (1.22.1)
71
+ rspec-support (~> 3.11.0)
72
+ rspec-support (3.11.0)
73
+ rubocop (1.25.1)
99
74
  parallel (~> 1.10)
100
- parser (>= 3.0.0.0)
75
+ parser (>= 3.1.0.0)
101
76
  rainbow (>= 2.2.2, < 4.0)
102
77
  regexp_parser (>= 1.8, < 3.0)
103
78
  rexml
104
- rubocop-ast (>= 1.12.0, < 2.0)
79
+ rubocop-ast (>= 1.15.1, < 2.0)
105
80
  ruby-progressbar (~> 1.7)
106
81
  unicode-display_width (>= 1.4.0, < 3.0)
107
- rubocop-ast (1.12.0)
82
+ rubocop-ast (1.15.2)
108
83
  parser (>= 3.0.1.1)
109
84
  ruby-progressbar (1.11.0)
110
85
  ruby2_keywords (0.0.5)
111
- tzinfo (2.0.4)
112
- concurrent-ruby (~> 1.0)
113
86
  unicode-display_width (2.1.0)
114
87
  uri_template (0.7.0)
115
- zeitwerk (2.4.2)
116
88
 
117
89
  PLATFORMS
118
- ruby
90
+ x86_64-darwin-20
119
91
 
120
92
  DEPENDENCIES
121
93
  bundler (~> 2)
@@ -127,4 +99,4 @@ DEPENDENCIES
127
99
  rubocop
128
100
 
129
101
  BUNDLED WITH
130
- 2.2.28
102
+ 2.3.7
data/README.md CHANGED
@@ -15,6 +15,7 @@ This gem is inspired by [committee](https://github.com/interagent/committee) (Ru
15
15
  Here's a [comparison between committee and openapi_first](https://gist.github.com/ahx/1538c31f0652f459861713b5259e366a).
16
16
 
17
17
  ## Rack middlewares
18
+
18
19
  OpenapiFirst consists of these Rack middlewares:
19
20
 
20
21
  - [`OpenapiFirst::Router`](#OpenapiFirst::Router) – Finds the OpenAPI operation for the current request or returns 404 if no operation was found. This can be customized.
@@ -23,6 +24,7 @@ OpenapiFirst consists of these Rack middlewares:
23
24
  - [`OpenapiFirst::ResponseValidation`](#OpenapiFirst::ResponseValidation) Validates the response and raises an exception if the response body is invalid.
24
25
 
25
26
  ## OpenapiFirst::Router
27
+
26
28
  You always have to add this middleware first in order to make the other middlewares work.
27
29
 
28
30
  ```ruby
@@ -33,11 +35,11 @@ This middleware adds `env[OpenapiFirst::OPERATION]` which holds an Operation obj
33
35
 
34
36
  ### Options and defaults
35
37
 
36
- | Name | Possible values | Description | Default
37
- |:---|---|---|---|
38
- |`spec:`| | The spec loaded via `OpenapiFirst.load` ||
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)
38
+ | Name | Possible values | Description | Default |
39
+ | :------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
40
+ | `spec:` | | The spec loaded via `OpenapiFirst.load` | |
41
+ | `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) |
42
+ | `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) |
41
43
 
42
44
  ## OpenapiFirst::RequestValidation
43
45
 
@@ -47,12 +49,11 @@ This middleware returns a 400 status code with a body that describes the error i
47
49
  use OpenapiFirst::RequestValidation
48
50
  ```
49
51
 
50
-
51
52
  ### Options and defaults
52
53
 
53
- | Name | Possible values | Description | Default
54
- |:---|---|---|---|
55
- | `raise_error:` |`false`, `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` instead of returning 4xx. | `false` (don't raise an exception)
54
+ | Name | Possible values | Description | Default |
55
+ | :------------- | --------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------- |
56
+ | `raise_error:` | `false`, `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` instead of returning 4xx. | `false` (don't raise an exception) |
56
57
 
57
58
  The error responses conform with [JSON:API](https://jsonapi.org).
58
59
 
@@ -110,17 +111,18 @@ Response validation fails if response body includes a property with `writeOnly:
110
111
  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.
111
112
 
112
113
  ```ruby
113
- run OpenapiFirst::Responder, spec: OpenapiFirst.load('./openapi/openapi.yaml')
114
+ run OpenapiFirst::Responder
114
115
  ```
115
116
 
116
117
  ### Options
117
- | Name | Description
118
- |:---|---|
119
- | `namespace:` | Optional. A class or module where to find the handler method. |
120
- | `resolver:` | Optional. An object that responds to `#call(operation)` and returns a [handler](#handlers). By default this is an instance of [DefaultOperationResolver](#OpenapiFirst::DefaultOperationResolver) |
121
118
 
119
+ | Name | Description |
120
+ | :----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | `namespace:` | Optional. A class or module where to find the handler method. |
122
+ | `resolver:` | Optional. An object that responds to `#call(operation)` and returns a [handler](#handlers). By default this is an instance of [DefaultOperationResolver](#OpenapiFirst::DefaultOperationResolver) |
122
123
 
123
124
  ### OpenapiFirst::DefaultOperationResolver
125
+
124
126
  This is the default way to look up a handler method for an operation. Handlers are always looked up in a namespace module that needs to be specified.
125
127
 
126
128
  It works like this:
@@ -144,13 +146,15 @@ There are two ways to set the response body:
144
146
  - Returning a value which will get converted to JSON
145
147
 
146
148
  ## OpenapiFirst::ResponseValidation
147
- This middleware is especially useful when testing. It *always* raises an error if the response is not valid.
149
+
150
+ This middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
148
151
 
149
152
  ```ruby
150
153
  use OpenapiFirst::ResponseValidation if ENV['RACK_ENV'] == 'test'
151
154
  ```
152
155
 
153
156
  ## Standalone usage
157
+
154
158
  Instead of composing these middlewares yourself you can use `OpenapiFirst.app`.
155
159
 
156
160
  ```ruby
@@ -181,15 +185,14 @@ The above will use the mentioned Rack middlewares to:
181
185
 
182
186
  ### Options and defaults
183
187
 
184
- | Name | Possible values | Description | Default
185
- |:---|---|---|---|
186
- | `spec_path` || A filepath to an OpenAPI definition file. |
187
- | `namespace:` || A class or module where to find the handler methods.|
188
- | `response_validation:` | `true`, `false` | If set to true it raises an exception if the response is invalid. This is useful during testing. | `false`
189
- | `router_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request path/method is not specified. This is useful during testing. | `false`
190
- | `request_validation_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request is not valid. | `false`
191
- | `resolver:` | | Option to customize finding the [handler](#handlers) method for an operation. See [OpenapiFirst::Responder](#OpenapiFirst::Responder) for details.
192
-
188
+ | Name | Possible values | Description | Default |
189
+ | :-------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
190
+ | `spec_path` | | A filepath to an OpenAPI definition file. |
191
+ | `namespace:` | | A class or module where to find the handler methods. |
192
+ | `response_validation:` | `true`, `false` | If set to true it raises an exception if the response is invalid. This is useful during testing. | `false` |
193
+ | `router_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request path/method is not specified. This is useful during testing. | `false` |
194
+ | `request_validation_raise_error:` | `true`, `false` | If set to true it raises an exception (subclass of `OpenapiFirst::Error` when a request is not valid. | `false` |
195
+ | `resolver:` | | Option to customize finding the [handler](#handlers) method for an operation. See [OpenapiFirst::Responder](#OpenapiFirst::Responder) for details. |
193
196
 
194
197
  Handler functions (`find_pet`) are called with two arguments:
195
198
 
@@ -289,6 +292,10 @@ Run `bundle exec rspec` to run the tests.
289
292
 
290
293
  See `bundle exec rake -T` for rubygems related tasks.
291
294
 
295
+ ## Benchmarks
296
+
297
+ [Results](https://gist.github.com/ahx/e6ffced58bd2e8d5baffb2f4d2c1f823)
298
+
292
299
  ### Run benchmarks
293
300
 
294
301
  ```sh
data/benchmarks/Gemfile CHANGED
@@ -7,7 +7,7 @@ gem 'benchmark-memory'
7
7
  gem 'committee'
8
8
  gem 'grape'
9
9
  gem 'hanami-api'
10
- gem 'hanami-router', '~> 2.0.0.alpha3'
10
+ gem 'hanami-router', '~> 2.0.0.alpha5'
11
11
  gem 'multi_json'
12
12
  gem 'openapi_first', path: '../'
13
13
  gem 'sinatra'
@@ -1,37 +1,34 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- openapi_first (0.14.1)
4
+ openapi_first (0.16.0.beta1)
5
5
  deep_merge (>= 1.2.1)
6
- hanami-router (~> 2.0.alpha3)
7
- hanami-utils (~> 2.0.alpha1)
6
+ hanami-router (~> 2.0.alpha5)
7
+ hanami-utils (~> 2.0.alpha3)
8
+ json_refs (>= 0.1.7)
8
9
  json_schemer (~> 0.2.16)
9
10
  multi_json (~> 1.14)
10
- oas_parser (~> 0.25.1)
11
11
  rack (~> 2.2)
12
12
 
13
13
  GEM
14
14
  remote: https://rubygems.org/
15
15
  specs:
16
- activesupport (6.1.4.1)
16
+ activesupport (7.0.2.2)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
18
  i18n (>= 1.6, < 2)
19
19
  minitest (>= 5.1)
20
20
  tzinfo (~> 2.0)
21
- zeitwerk (~> 2.3)
22
- addressable (2.8.0)
23
- public_suffix (>= 2.0.2, < 5.0)
24
- benchmark-ips (2.9.1)
25
- benchmark-memory (0.1.2)
26
- memory_profiler (~> 0.9)
21
+ benchmark-ips (2.9.3)
22
+ benchmark-memory (0.2.0)
23
+ memory_profiler (~> 1)
27
24
  builder (3.2.4)
28
25
  committee (4.4.0)
29
26
  json_schema (~> 0.14, >= 0.14.3)
30
27
  openapi_parser (>= 0.11.1, < 1.0)
31
28
  rack (>= 1.5)
32
29
  concurrent-ruby (1.1.9)
33
- deep_merge (1.2.1)
34
- dry-configurable (0.13.0)
30
+ deep_merge (1.2.2)
31
+ dry-configurable (0.14.0)
35
32
  concurrent-ruby (~> 1.0)
36
33
  dry-core (~> 0.6)
37
34
  dry-container (0.9.0)
@@ -50,9 +47,9 @@ GEM
50
47
  dry-core (~> 0.5, >= 0.5)
51
48
  dry-inflector (~> 0.1, >= 0.1.2)
52
49
  dry-logic (~> 1.0, >= 1.0.2)
53
- ecma-re-validator (0.3.0)
54
- regexp_parser (~> 2.0)
55
- grape (1.6.0)
50
+ ecma-re-validator (0.4.0)
51
+ regexp_parser (~> 2.2)
52
+ grape (1.6.2)
56
53
  activesupport
57
54
  builder
58
55
  dry-types (>= 1.1)
@@ -66,22 +63,22 @@ GEM
66
63
  mustermann (~> 1.0)
67
64
  mustermann-contrib (~> 1.0)
68
65
  rack (~> 2.0)
69
- hanami-utils (2.0.0.alpha2)
66
+ hanami-utils (2.0.0.alpha6)
70
67
  concurrent-ruby (~> 1.0)
71
68
  dry-transformer (~> 0.1)
72
69
  hansi (0.2.0)
73
- hash-deep-merge (0.1.1)
74
- i18n (1.8.10)
70
+ i18n (1.9.1)
75
71
  concurrent-ruby (~> 1.0)
72
+ json_refs (0.1.7)
73
+ hana
76
74
  json_schema (0.21.0)
77
75
  json_schemer (0.2.18)
78
76
  ecma-re-validator (~> 0.3)
79
77
  hana (~> 1.3)
80
78
  regexp_parser (~> 2.0)
81
79
  uri_template (~> 0.7)
82
- memory_profiler (0.9.14)
83
- mini_portile2 (2.6.1)
84
- minitest (5.14.4)
80
+ memory_profiler (1.0.0)
81
+ minitest (5.15.0)
85
82
  multi_json (1.15.0)
86
83
  mustermann (1.1.1)
87
84
  ruby2_keywords (~> 0.0.1)
@@ -90,26 +87,13 @@ GEM
90
87
  mustermann (= 1.1.1)
91
88
  mustermann-grape (1.0.1)
92
89
  mustermann (>= 1.0.0)
93
- nokogiri (1.12.5)
94
- mini_portile2 (~> 2.6.1)
95
- racc (~> 1.4)
96
- oas_parser (0.25.4)
97
- activesupport (>= 4.0.0)
98
- addressable (~> 2.3)
99
- builder (~> 3.2.3)
100
- deep_merge (~> 1.2.1)
101
- hash-deep-merge
102
- mustermann-contrib (~> 1.1.1)
103
- nokogiri
104
90
  openapi_parser (0.15.0)
105
- public_suffix (4.0.6)
106
- racc (1.5.2)
107
91
  rack (2.2.3)
108
92
  rack-accept (0.4.5)
109
93
  rack (>= 0.4)
110
94
  rack-protection (2.1.0)
111
95
  rack
112
- regexp_parser (2.1.1)
96
+ regexp_parser (2.2.1)
113
97
  ruby2_keywords (0.0.5)
114
98
  seg (1.2.0)
115
99
  sinatra (2.1.0)
@@ -124,10 +108,9 @@ GEM
124
108
  tzinfo (2.0.4)
125
109
  concurrent-ruby (~> 1.0)
126
110
  uri_template (0.7.0)
127
- zeitwerk (2.4.2)
128
111
 
129
112
  PLATFORMS
130
- ruby
113
+ x86_64-darwin-20
131
114
 
132
115
  DEPENDENCIES
133
116
  benchmark-ips
@@ -135,11 +118,11 @@ DEPENDENCIES
135
118
  committee
136
119
  grape
137
120
  hanami-api
138
- hanami-router (~> 2.0.0.alpha3)
121
+ hanami-router (~> 2.0.0.alpha5)
139
122
  multi_json
140
123
  openapi_first!
141
124
  sinatra
142
125
  syro
143
126
 
144
127
  BUNDLED WITH
145
- 2.2.3
128
+ 2.2.28
@@ -20,7 +20,7 @@ app = Class.new(Hanami::API) do
20
20
  end.new
21
21
 
22
22
  use Committee::Middleware::RequestValidation,
23
- schema_path: './apps/openapi.yaml',
23
+ schema_path: File.absolute_path('./openapi.yaml', __dir__),
24
24
  parse_response_by_content_type: true
25
25
 
26
26
  run app
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'committee'
5
+ require 'hanami/api'
6
+
7
+ app = Class.new(Hanami::API) do
8
+ get '/hello/:id' do
9
+ json(hello: 'world', id: params.fetch(:id))
10
+ end
11
+
12
+ get '/hello' do
13
+ json([{ hello: 'world' }])
14
+ end
15
+
16
+ post '/hello' do
17
+ status 201
18
+ json(hello: 'world')
19
+ end
20
+ end.new
21
+
22
+ use Committee::Middleware::RequestValidation,
23
+ schema_path: File.absolute_path('./openapi.yaml', __dir__),
24
+ parse_response_by_content_type: true
25
+
26
+ use Committee::Middleware::ResponseValidation,
27
+ schema_path: File.absolute_path('./openapi.yaml', __dir__)
28
+
29
+ run app
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'committee'
5
+ require 'sinatra'
6
+
7
+ class SinatraWithCommiteeExample < Sinatra::Base
8
+ set :environment, :production
9
+
10
+ get '/hello/:id' do
11
+ content_type :json
12
+ MultiJson.dump(hello: 'world', id: params.fetch('id'))
13
+ end
14
+
15
+ get '/hello' do
16
+ content_type :json
17
+ MultiJson.dump([{ hello: 'world' }])
18
+ end
19
+
20
+ post '/hello' do
21
+ content_type :json
22
+ status 201
23
+ MultiJson.dump(hello: 'world')
24
+ end
25
+ end
26
+
27
+ use Committee::Middleware::RequestValidation,
28
+ schema_path: File.absolute_path('./openapi.yaml', __dir__),
29
+ parse_response_by_content_type: true
30
+
31
+ run SinatraWithCommiteeExample
@@ -28,15 +28,13 @@ paths:
28
28
  content:
29
29
  application/json:
30
30
  schema:
31
- type: array
32
- items:
33
- type: object
34
- required: [hello, id]
35
- properties:
36
- hello:
37
- type: string
38
- id:
39
- type: string
31
+ type: object
32
+ required: [hello, id]
33
+ properties:
34
+ hello:
35
+ type: string
36
+ id:
37
+ type: string
40
38
  /hello:
41
39
  get:
42
40
  operationId: find_things
@@ -61,11 +59,13 @@ paths:
61
59
  content:
62
60
  application/json:
63
61
  schema:
64
- type: object
65
- required: [hello]
66
- properties:
67
- hello:
68
- type: string
62
+ type: array
63
+ items:
64
+ type: object
65
+ required: [hello]
66
+ properties:
67
+ hello:
68
+ type: string
69
69
  default:
70
70
  description: Error response
71
71
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'openapi_first'
5
+ require 'hanami/api'
6
+
7
+ app = Class.new(Hanami::API) do
8
+ get '/hello/:id' do
9
+ json(hello: 'world', id: params.fetch(:id))
10
+ end
11
+
12
+ get '/hello' do
13
+ json([{ hello: 'world' }])
14
+ end
15
+
16
+ post '/hello' do
17
+ status 201
18
+ json(hello: 'world')
19
+ end
20
+ end.new
21
+
22
+ oas_path = File.absolute_path('./openapi.yaml', __dir__)
23
+ use OpenapiFirst::Router, spec: OpenapiFirst.load(oas_path)
24
+ use OpenapiFirst::RequestValidation
25
+
26
+ run app
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'openapi_first'
5
+
6
+ namespace = Module.new do
7
+ def self.find_thing(params, _res)
8
+ { hello: 'world', id: params.fetch(:id) }
9
+ end
10
+
11
+ def self.find_things(_params, _res)
12
+ [{ hello: 'world' }]
13
+ end
14
+
15
+ def self.create_thing(_params, res)
16
+ res.status = 201
17
+ { hello: 'world' }
18
+ end
19
+ end
20
+
21
+ oas_path = File.absolute_path('./openapi.yaml', __dir__)
22
+ run OpenapiFirst.app(oas_path, namespace: namespace, response_validation: true)
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rack'
4
- require 'logger'
5
4
 
6
5
  module OpenapiFirst
7
6
  class App
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require 'json_schemer'
4
+ require 'set'
5
5
  require_relative 'schema_validation'
6
6
  require_relative 'utils'
7
7
  require_relative 'response_object'
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rack'
4
- require 'json_schemer'
5
4
  require 'multi_json'
6
5
  require_relative 'inbox'
7
6
  require_relative 'router_required'
@@ -11,9 +10,9 @@ module OpenapiFirst
11
10
  class RequestValidation # rubocop:disable Metrics/ClassLength
12
11
  prepend RouterRequired
13
12
 
14
- def initialize(app, raise_error: false)
13
+ def initialize(app, options = {})
15
14
  @app = app
16
- @raise = raise_error
15
+ @raise = options.fetch(:raise_error, false)
17
16
  end
18
17
 
19
18
  def call(env) # rubocop:disable Metrics/AbcSize
@@ -106,8 +105,10 @@ module OpenapiFirst
106
105
  return unless schema
107
106
 
108
107
  params = filtered_params(schema.raw_schema, params)
109
- errors = schema.validate(Utils.deep_stringify(params))
108
+ params = Utils.deep_stringify(params)
109
+ errors = schema.validate(params)
110
110
  halt_with_error(400, serialize_query_parameter_errors(errors)) if errors.any?
111
+ params = Utils.deep_symbolize(params)
111
112
  env[PARAMETERS] = params
112
113
  env[INBOX].merge! params
113
114
  end
@@ -142,6 +143,8 @@ module OpenapiFirst
142
143
  end
143
144
 
144
145
  def parse_array_parameter(value, schema)
146
+ return value if value.nil? || value.empty?
147
+
145
148
  array = value.is_a?(Array) ? value : value.split(',')
146
149
  return array unless schema['items']
147
150
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rack'
4
+ require 'multi_json'
4
5
  require_relative 'inbox'
5
6
  require_relative 'default_operation_resolver'
6
7
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
- require_relative 'utils'
5
4
 
6
5
  module OpenapiFirst
7
6
  # Represents an OpenAPI Response Object
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json_schemer'
4
3
  require 'multi_json'
5
4
  require_relative 'router_required'
6
- require_relative 'validation'
5
+ require_relative 'validation_format'
7
6
 
8
7
  module OpenapiFirst
9
8
  class ResponseValidation
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'json_schemer'
4
- require 'multi_json'
5
- require_relative 'validation'
3
+ require_relative 'response_validation'
6
4
  require_relative 'router'
7
5
 
8
6
  module OpenapiFirst
@@ -2,21 +2,18 @@
2
2
 
3
3
  require 'rack'
4
4
  require 'hanami/router'
5
- require_relative 'utils'
6
5
 
7
6
  module OpenapiFirst
8
7
  class Router
9
8
  def initialize(
10
9
  app,
11
- spec:,
12
- raise_error: false,
13
- not_found: :halt,
14
- parent_app: nil
10
+ options
15
11
  )
16
12
  @app = app
17
- @parent_app = parent_app
18
- @raise = raise_error
19
- @not_found = not_found
13
+ @parent_app = options.fetch(:parent_app, nil)
14
+ @raise = options.fetch(:raise_error, false)
15
+ @not_found = options.fetch(:not_found, :halt)
16
+ spec = options.fetch(:spec)
20
17
  @filepath = spec.filepath
21
18
  @router = build_router(spec.operations)
22
19
  end
@@ -14,6 +14,7 @@ module OpenapiFirst
14
14
  @schemer = JSONSchemer.schema(
15
15
  schema,
16
16
  keywords: custom_keywords,
17
+ insert_property_defaults: true,
17
18
  before_property_validation: proc do |data, property, property_schema, parent|
18
19
  convert_nullable(data, property, property_schema, parent)
19
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '0.14.1'
4
+ VERSION = '0.16.0.beta1'
5
5
  end
data/lib/openapi_first.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
- require 'oas_parser'
5
- require 'openapi_first/definition'
6
- require 'openapi_first/version'
7
- require 'openapi_first/inbox'
8
- require 'openapi_first/router'
9
- require 'openapi_first/request_validation'
10
- require 'openapi_first/response_validator'
11
- require 'openapi_first/response_validation'
12
- require 'openapi_first/responder'
13
- require 'openapi_first/app'
4
+ require 'json_refs'
5
+ require_relative 'openapi_first/definition'
6
+ require_relative 'openapi_first/version'
7
+ require_relative 'openapi_first/inbox'
8
+ require_relative 'openapi_first/router'
9
+ require_relative 'openapi_first/request_validation'
10
+ require_relative 'openapi_first/response_validator'
11
+ require_relative 'openapi_first/response_validation'
12
+ require_relative 'openapi_first/responder'
13
+ require_relative 'openapi_first/app'
14
14
 
15
15
  module OpenapiFirst
16
16
  OPERATION = 'openapi_first.operation'
@@ -24,8 +24,10 @@ module OpenapiFirst
24
24
  end
25
25
 
26
26
  def self.load(spec_path, only: nil)
27
- content = YAML.load_file(spec_path)
28
- resolved = OasParser::Parser.new(spec_path, content).resolve
27
+ resolved = Dir.chdir(File.dirname(spec_path)) do
28
+ content = YAML.load_file(File.basename(spec_path))
29
+ JsonRefs.call(content, resolve_local_ref: true, resolve_file_ref: true)
30
+ end
29
31
  resolved['paths'].filter!(&->(key, _) { only.call(key) }) if only
30
32
  Definition.new(resolved, spec_path)
31
33
  end
@@ -35,15 +35,18 @@ Gem::Specification.new do |spec|
35
35
  spec.required_ruby_version = '>= 2.6.0'
36
36
 
37
37
  spec.add_runtime_dependency 'deep_merge', '>= 1.2.1'
38
- spec.add_runtime_dependency 'hanami-router', '~> 2.0.alpha3'
39
- spec.add_runtime_dependency 'hanami-utils', '~> 2.0.alpha1'
38
+ spec.add_runtime_dependency 'hanami-router', '~> 2.0.alpha5'
39
+ spec.add_runtime_dependency 'hanami-utils', '~> 2.0.alpha3'
40
+ spec.add_runtime_dependency 'json_refs', '>= 0.1.7'
40
41
  spec.add_runtime_dependency 'json_schemer', '~> 0.2.16'
41
42
  spec.add_runtime_dependency 'multi_json', '~> 1.14'
42
- spec.add_runtime_dependency 'oas_parser', '~> 0.25.1'
43
43
  spec.add_runtime_dependency 'rack', '~> 2.2'
44
44
 
45
45
  spec.add_development_dependency 'bundler', '~> 2'
46
46
  spec.add_development_dependency 'rack-test', '~> 1'
47
47
  spec.add_development_dependency 'rake', '~> 13'
48
48
  spec.add_development_dependency 'rspec', '~> 3'
49
+ spec.metadata = {
50
+ 'rubygems_mfa_required' => 'true'
51
+ }
49
52
  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: 0.14.1
4
+ version: 0.16.0.beta1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-10-05 00:00:00.000000000 Z
11
+ date: 2022-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge
@@ -30,70 +30,70 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.0.alpha3
33
+ version: 2.0.alpha5
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.0.alpha3
40
+ version: 2.0.alpha5
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: hanami-utils
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 2.0.alpha1
47
+ version: 2.0.alpha3
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 2.0.alpha1
54
+ version: 2.0.alpha3
55
55
  - !ruby/object:Gem::Dependency
56
- name: json_schemer
56
+ name: json_refs
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.2.16
61
+ version: 0.1.7
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: 0.2.16
68
+ version: 0.1.7
69
69
  - !ruby/object:Gem::Dependency
70
- name: multi_json
70
+ name: json_schemer
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1.14'
75
+ version: 0.2.16
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1.14'
82
+ version: 0.2.16
83
83
  - !ruby/object:Gem::Dependency
84
- name: oas_parser
84
+ name: multi_json
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 0.25.1
89
+ version: '1.14'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 0.25.1
96
+ version: '1.14'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rack
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -185,11 +185,15 @@ files:
185
185
  - benchmarks/Gemfile
186
186
  - benchmarks/Gemfile.lock
187
187
  - benchmarks/apps/committee.ru
188
+ - benchmarks/apps/committee_with_response_validation.ru
189
+ - benchmarks/apps/committee_with_sinatra.ru
188
190
  - benchmarks/apps/grape.ru
189
191
  - benchmarks/apps/hanami_api.ru
190
192
  - benchmarks/apps/hanami_router.ru
191
193
  - benchmarks/apps/openapi.yaml
192
194
  - benchmarks/apps/openapi_first.ru
195
+ - benchmarks/apps/openapi_first_with_hanami_api.ru
196
+ - benchmarks/apps/openapi_first_with_response_validation.ru
193
197
  - benchmarks/apps/sinatra.ru
194
198
  - benchmarks/apps/syro.ru
195
199
  - benchmarks/benchmarks.rb
@@ -223,9 +227,7 @@ homepage: https://github.com/ahx/openapi_first
223
227
  licenses:
224
228
  - MIT
225
229
  metadata:
226
- https://github.com/ahx/openapi_first: https://github.com/ahx/openapi_first
227
- source_code_uri: https://github.com/ahx/openapi_first
228
- changelog_uri: https://github.com/ahx/openapi_first/blob/master/CHANGELOG.md
230
+ rubygems_mfa_required: 'true'
229
231
  post_install_message:
230
232
  rdoc_options: []
231
233
  require_paths:
@@ -237,11 +239,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
237
239
  version: 2.6.0
238
240
  required_rubygems_version: !ruby/object:Gem::Requirement
239
241
  requirements:
240
- - - ">="
242
+ - - ">"
241
243
  - !ruby/object:Gem::Version
242
- version: '0'
244
+ version: 1.3.1
243
245
  requirements: []
244
- rubygems_version: 3.2.22
246
+ rubygems_version: 3.3.3
245
247
  signing_key:
246
248
  specification_version: 4
247
249
  summary: Implement REST APIs based on OpenApi.