openapi_first 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50a431b59d4568cede6b8a110ada173691c698517231299795ee22fc8f54115a
4
- data.tar.gz: be49aa45dd7b95ee0ec2149b4ec6476c78a42a0e46204c321e5350368bee2d54
3
+ metadata.gz: e7c0190540a81a93de98accc25eac31681aa77997282744083639e75e032a65b
4
+ data.tar.gz: '08c6b1f8bee0682a75672e77e8831d30fe15a466ad72cdd6ea901c93b4c0361d'
5
5
  SHA512:
6
- metadata.gz: b6b29dab24a951716e9fb73a7f852a4acd3de577cce82008cd329906ef77d73c4cff182face7adb1d36e9ecb9ea170d1655accb9cdc3bba682a818c3c81000b0
7
- data.tar.gz: d0ad9c915cb4e637dce9ff4f0e6a8bc35cf705c684f69f38d9abbc8edada4b8aef0d2ba226cfae187f9d80da3a229e0f9493ac78bddb0045206ed816cbfe022e
6
+ metadata.gz: cb1e60e214237e73304f5e4abc7611b6516072b7610e2bacfe9040d9fc91299e786fbfc64c695702aca891981a3eb993d0f8a2f75007fa2637f659e5ab4a635c
7
+ data.tar.gz: ab281285bda7e922497885c3751eaa9e34419ba2a8f9a20b2b9dc9bf693d9f2248ed998347527430485edf123d715dbb7932d516f5f2af22aae95910f05e7d58
@@ -1,5 +1,8 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.10.0
4
+ - Add support for query parameters named `"some[thing]"` ([issue](https://github.com/ahx/openapi_first/issues/40))
5
+
3
6
  ## 0.9.0
4
7
  - Make request validation usable standalone
5
8
 
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openapi_first (0.9.0)
4
+ openapi_first (0.10.0)
5
+ deep_merge (>= 1.2.1)
5
6
  hanami-router (~> 2.0.alpha2)
6
7
  hanami-utils (~> 2.0.alpha1)
7
8
  json_schemer (~> 0.2)
@@ -12,12 +13,12 @@ PATH
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
15
- activesupport (6.0.2.2)
16
+ activesupport (6.0.3)
16
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
17
18
  i18n (>= 0.7, < 2)
18
19
  minitest (~> 5.1)
19
20
  tzinfo (~> 1.1)
20
- zeitwerk (~> 2.2)
21
+ zeitwerk (~> 2.2, >= 2.2.2)
21
22
  addressable (2.7.0)
22
23
  public_suffix (>= 2.0.2, < 5.0)
23
24
  ast (2.4.0)
@@ -28,7 +29,7 @@ GEM
28
29
  diff-lcs (1.3)
29
30
  ecma-re-validator (0.2.1)
30
31
  regexp_parser (~> 1.2)
31
- hana (1.3.5)
32
+ hana (1.3.6)
32
33
  hanami-router (2.0.0.alpha2)
33
34
  mustermann (~> 1.0)
34
35
  mustermann-contrib (~> 1.0)
@@ -66,7 +67,7 @@ GEM
66
67
  mustermann-contrib (~> 1.1.1)
67
68
  nokogiri
68
69
  parallel (1.19.1)
69
- parser (2.7.1.1)
70
+ parser (2.7.1.2)
70
71
  ast (~> 2.4.0)
71
72
  pry (0.13.1)
72
73
  coderay (~> 1.1)
@@ -83,15 +84,15 @@ GEM
83
84
  rspec-core (~> 3.9.0)
84
85
  rspec-expectations (~> 3.9.0)
85
86
  rspec-mocks (~> 3.9.0)
86
- rspec-core (3.9.1)
87
- rspec-support (~> 3.9.1)
87
+ rspec-core (3.9.2)
88
+ rspec-support (~> 3.9.3)
88
89
  rspec-expectations (3.9.1)
89
90
  diff-lcs (>= 1.2.0, < 2.0)
90
91
  rspec-support (~> 3.9.0)
91
92
  rspec-mocks (3.9.1)
92
93
  diff-lcs (>= 1.2.0, < 2.0)
93
94
  rspec-support (~> 3.9.0)
94
- rspec-support (3.9.2)
95
+ rspec-support (3.9.3)
95
96
  rubocop (0.82.0)
96
97
  jaro_winkler (~> 1.5.1)
97
98
  parallel (~> 1.10)
data/README.md CHANGED
@@ -1,13 +1,32 @@
1
1
  # OpenapiFirst
2
2
 
3
- OpenapiFirst helps to implement HTTP APIs based on an [OpenApi](https://www.openapis.org/) API description. The idea is that you create an API description first, then add a method that returns data and implements your business logic and be done.
4
-
5
- ## TL;DR
3
+ OpenapiFirst helps to implement HTTP APIs based on an [OpenApi](https://www.openapis.org/) API description. The idea is that you create an API description first, then add code that returns data and implements your business logic and be done.
6
4
 
7
5
  Start with writing an OpenAPI file that describes the API, which you are about to write. Use a [validator](https://github.com/stoplightio/spectral/) to make sure the file is valid.
8
- In the following examples, the OpenAPI file is named `openapi/openapi.yaml`.
9
6
 
10
- Now implement your API:
7
+ ## Rack middlewares
8
+ OpenapiFirst consists of these Rack middlewares:
9
+
10
+ - `OpenapiFirst::Router` finds the operation for the current request or returns 404 if no operation was found.
11
+ - `OpenapiFirst::RequestValidation` validates the request against the found operation and returns 400 if the request is invalid.
12
+ - `OpenapiFirst::OperationResolver` calls the [handler](#handlers) found for the operation.
13
+
14
+ ## Usage within your Rack webframework
15
+ If you just want to use the request validation part without any handlers you can use the rack middlewares standalone:
16
+
17
+ ```ruby
18
+ use OpenapiFirst::Router, spec: OpenapiFirst.load('./openapi/openapi.yaml')
19
+ use OpenapiFirst::RequestValidation
20
+ ```
21
+
22
+ ### Rack env variables
23
+ These variables will available in your rack env:
24
+
25
+ - `env[OpenapiFirst::OPERATION]` - Holds an Operation object that responsed about `operation_id` and `path`. This is useful for introspection.
26
+ - `env[OpenapiFirst::INBOX]`. Holds the (filtered) path and query parameters and the parsed request body.
27
+
28
+ ## Standalone usage
29
+ You can implement your API in conveniently with just OpenapiFirst.
11
30
 
12
31
  ```ruby
13
32
  module Pets
@@ -24,7 +43,7 @@ require 'openapi_first'
24
43
  run OpenapiFirst.app('./openapi/openapi.yaml', namespace: Pets)
25
44
  ```
26
45
 
27
- The above will:
46
+ The above will use the mentioned Rack middlewares to:
28
47
 
29
48
  - Validate the request and respond with 400 if the request does not match with your API description
30
49
  - Map the request to a method call `Pets.find_pet` based on the `operationId` in the API description
@@ -35,48 +54,10 @@ Handler functions (`find_pet`) are called with two arguments:
35
54
  - `params` - Holds the parsed request body, filtered query params and path parameters
36
55
  - `res` - Holds a Rack::Response that you can modify if needed
37
56
  If you want to access to plain Rack env you can call `params.env`.
38
-
39
- ## Rack middlewares
40
- OpenapiFirst consists of these Rack middlewares:
41
57
 
42
- - `OpenapiFirst::Router` finds the operation for the current request and finds a handler (if namespace option is given)
43
- - `OpenapiFirst::RequestValidation` validates the request and returns 400 if it's invalid
44
- - `OpenapiFirst::OperationResolver` calls the handler
58
+ ### Handlers
45
59
 
46
- Instead of using `OpenapiFirst.app` you can use these middlwares by itself.
47
-
48
- ## Usage within your Rack webframework
49
- If you just want to use the request validation part without any handlers you can use the rack middlewares standalone and don't need to pass a `namespace` option:
50
-
51
- ```ruby
52
- use OpenapiFirst::Router, spec: OpenapiFirst.load('./openapi/openapi.yaml')
53
- use OpenapiFirst::RequestValidation
54
- ```
55
-
56
- ### Rack env variables
57
- These variables will available in your rack env:
58
-
59
- - `env[OpenapiFirst::OPERATION]` - Holds an Operation object that responsed about `operation_id` and `path`. This is useful for introspection.
60
- - `env[OpenapiFirst::INBOX]`. Holds the (filtered) path and query parameters and the response body.
61
-
62
- ## Try it out
63
-
64
- See [examples](examples).
65
-
66
-
67
- ## Installation
68
-
69
- Add this line to your application's Gemfile:
70
-
71
- ```ruby
72
- gem 'openapi_first'
73
- ```
74
-
75
- OpenapiFirst uses [`multi_json`](https://rubygems.org/gems/multi_json).
76
-
77
- ## Handlers
78
-
79
- OpenapiFirst 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.
60
+ OpenapiFirst 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 via the `OperationResolver` middleware.
80
61
 
81
62
  It works like this:
82
63
 
@@ -96,6 +77,28 @@ There are two ways to set the response body:
96
77
  - Calling `res.write "things"` (see [Rack::Response](https://www.rubydoc.info/github/rack/rack/Rack/Response))
97
78
  - Returning a value from the function (see example above) (this will always converted to JSON)
98
79
 
80
+ ### If your API description does not contain all endpoints
81
+
82
+ ```ruby
83
+ run OpenapiFirst.middleware('./openapi/openapi.yaml', namespace: Pets)
84
+ ```
85
+
86
+ Here all requests that are not part of the API description will be passed to the next app.
87
+
88
+ ### Try it out
89
+
90
+ See [examples](examples).
91
+
92
+ ## Installation
93
+
94
+ Add this line to your application's Gemfile:
95
+
96
+ ```ruby
97
+ gem 'openapi_first'
98
+ ```
99
+
100
+ OpenapiFirst uses [`multi_json`](https://rubygems.org/gems/multi_json).
101
+
99
102
  ## Request validation
100
103
 
101
104
  If the request is not valid, these middlewares return a 400 status code with a body that describes the error.
@@ -151,14 +154,6 @@ validator = OpenapiFirst::ResponseValidator.new(spec)
151
154
  expect(validator.validate(last_request, last_response).errors).to be_empty
152
155
  ```
153
156
 
154
- ## If your API description does not contain all endpoints
155
-
156
- ```ruby
157
- run OpenapiFirst.middleware('./openapi/openapi.yaml', namespace: Pets)
158
- ```
159
-
160
- Here all requests that are not part of the API description will be passed to the next app.
161
-
162
157
  ## Handling only certain paths
163
158
 
164
159
  You can filter the URIs that should be handled by passing `only` to `OpenapiFirst.load`:
@@ -168,7 +163,6 @@ spec = OpenapiFirst.load './openapi/openapi.yaml', only: '/pets'.method(:==)
168
163
  run OpenapiFirst.app(spec, namespace: Pets)
169
164
  ```
170
165
 
171
-
172
166
  ## Coverage
173
167
 
174
168
  (This is a bit experimental. Please try it out and give feedback.)
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- openapi_first (0.9.0)
4
+ openapi_first (0.10.0)
5
+ deep_merge (>= 1.2.1)
5
6
  hanami-router (~> 2.0.alpha2)
6
7
  hanami-utils (~> 2.0.alpha1)
7
8
  json_schemer (~> 0.2)
@@ -12,15 +13,15 @@ PATH
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
15
- activesupport (6.0.2.2)
16
+ activesupport (6.0.3)
16
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
17
18
  i18n (>= 0.7, < 2)
18
19
  minitest (~> 5.1)
19
20
  tzinfo (~> 1.1)
20
- zeitwerk (~> 2.2)
21
+ zeitwerk (~> 2.2, >= 2.2.2)
21
22
  addressable (2.7.0)
22
23
  public_suffix (>= 2.0.2, < 5.0)
23
- benchmark-ips (2.7.2)
24
+ benchmark-ips (2.8.2)
24
25
  benchmark-memory (0.1.2)
25
26
  memory_profiler (~> 0.9)
26
27
  builder (3.2.4)
@@ -61,7 +62,7 @@ GEM
61
62
  mustermann-grape (~> 1.0.0)
62
63
  rack (>= 1.3.0)
63
64
  rack-accept
64
- hana (1.3.5)
65
+ hana (1.3.6)
65
66
  hanami-router (2.0.0.alpha2)
66
67
  mustermann (~> 1.0)
67
68
  mustermann-contrib (~> 1.0)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'forwardable'
4
+ require_relative 'utils'
4
5
 
5
6
  module OpenapiFirst
6
7
  class Operation
@@ -39,14 +40,31 @@ module OpenapiFirst
39
40
  def build_parameters_json_schema
40
41
  return unless @operation.parameters&.any?
41
42
 
42
- @operation.parameters.each_with_object(
43
+ @operation.parameters.each_with_object(new_node) do |parameter, schema|
44
+ params = Rack::Utils.parse_nested_query(parameter.name)
45
+ generate_schema(schema, params, parameter)
46
+ end
47
+ end
48
+
49
+ def generate_schema(schema, params, parameter)
50
+ params.each do |key, value|
51
+ schema['required'] << key if parameter.required
52
+ if value.is_a? Hash
53
+ property_schema = new_node
54
+ generate_schema(property_schema, value, parameter)
55
+ Utils.deep_merge!(schema['properties'], { key => property_schema })
56
+ else
57
+ schema['properties'][key] = parameter.schema
58
+ end
59
+ end
60
+ end
61
+
62
+ def new_node
63
+ {
43
64
  'type' => 'object',
44
65
  'required' => [],
45
66
  'properties' => {}
46
- ) do |parameter, schema|
47
- schema['required'] << parameter.name if parameter.required
48
- schema['properties'][parameter.name] = parameter.schema
49
- end
67
+ }
50
68
  end
51
69
  end
52
70
  end
@@ -1,9 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'hanami/utils/string'
4
+ require 'deep_merge/core'
4
5
 
5
6
  module OpenapiFirst
6
7
  module Utils
8
+ def self.deep_merge!(dest, source)
9
+ DeepMerge.deep_merge!(source, dest)
10
+ end
11
+
7
12
  def self.underscore(string)
8
13
  Hanami::Utils::String.underscore(string)
9
14
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OpenapiFirst
4
- VERSION = '0.9.0'
4
+ VERSION = '0.10.0'
5
5
  end
@@ -32,6 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.bindir = 'exe'
33
33
  spec.require_paths = ['lib']
34
34
 
35
+ spec.add_dependency 'deep_merge', '>= 1.2.1'
35
36
  spec.add_dependency 'hanami-router', '~> 2.0.alpha2'
36
37
  spec.add_dependency 'hanami-utils', '~> 2.0.alpha1'
37
38
  spec.add_dependency 'json_schemer', '~> 0.2'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openapi_first
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Haller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-04-24 00:00:00.000000000 Z
11
+ date: 2020-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deep_merge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.1
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: hanami-router
15
29
  requirement: !ruby/object:Gem::Requirement