openapi_first 1.0.0.beta1 → 1.0.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +8 -20
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +23 -0
  5. data/Gemfile +4 -1
  6. data/Gemfile.lock +39 -51
  7. data/README.md +27 -25
  8. data/benchmarks/Gemfile.lock +28 -33
  9. data/benchmarks/apps/openapi_first_with_hanami_api.ru +1 -2
  10. data/benchmarks/apps/openapi_first_with_plain_rack.ru +32 -0
  11. data/benchmarks/apps/openapi_first_with_response_validation.ru +14 -11
  12. data/benchmarks/apps/openapi_first_with_sinatra.ru +29 -0
  13. data/lib/openapi_first/body_parser_middleware.rb +1 -1
  14. data/lib/openapi_first/config.rb +19 -0
  15. data/lib/openapi_first/default_error_response.rb +47 -0
  16. data/lib/openapi_first/definition.rb +8 -1
  17. data/lib/openapi_first/error_response.rb +31 -0
  18. data/lib/openapi_first/errors.rb +3 -40
  19. data/lib/openapi_first/operation.rb +33 -14
  20. data/lib/openapi_first/operation_schemas.rb +52 -0
  21. data/lib/openapi_first/plugins.rb +17 -0
  22. data/lib/openapi_first/request_body_validator.rb +41 -0
  23. data/lib/openapi_first/request_validation.rb +66 -84
  24. data/lib/openapi_first/response_validation.rb +38 -7
  25. data/lib/openapi_first/response_validator.rb +1 -1
  26. data/lib/openapi_first/router.rb +15 -14
  27. data/lib/openapi_first/schema_validation.rb +22 -21
  28. data/lib/openapi_first/string_keyed_hash.rb +20 -0
  29. data/lib/openapi_first/validation_result.rb +15 -0
  30. data/lib/openapi_first/version.rb +1 -1
  31. data/lib/openapi_first.rb +30 -21
  32. data/openapi_first.gemspec +4 -12
  33. metadata +20 -117
  34. data/benchmarks/apps/openapi_first.ru +0 -22
  35. data/lib/openapi_first/utils.rb +0 -35
  36. data/lib/openapi_first/validation_format.rb +0 -55
  37. /data/benchmarks/apps/{committee.ru → committee_with_hanami_api.ru} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2248133fb2e0b761fb314629dcbfa7c9a6b7e0ac03b59887fe158968b17ed827
4
- data.tar.gz: b0012d8af3c9dee1fa94334be7890e260f0173946129b53ec8f930f768f8de28
3
+ metadata.gz: c02c053192b6b39cb8f05acd35f7a257ee98b449c49f8ceeec4ac6e542f09e15
4
+ data.tar.gz: c36d3598b263ebd3d1a9bd23e6aa0b66256890f68e2f70fe245ca932d2f004f0
5
5
  SHA512:
6
- metadata.gz: c615c847efcd10cfd145bcc99ede55da2eaa76fe91b9d0ab3fdb159c20b6cc314309f583ba0b784c3e3493a7a534ed1836309807c549bd8c40b226f2f9bf77e9
7
- data.tar.gz: 83c476b9b67ce15ad71e052af8360e013659da13fbbd8f86a55bae9d4bbcf08f4ac291187dfccc5786e957c65d2a3124366f007998c675b4a3e29bccaecf332a
6
+ metadata.gz: e70192d7c2cb58734b8ebb6ccf53b2f243a98e78adbea271a3b0df1468f400d60080ead1ea68c07dd33bd9985d67de02ec4e0200e3ca0d49272a893c97611ffb
7
+ data.tar.gz: c59e62cd24dd767330f7e9ce6ad96a9c13005a4498c39f3ccf10bc801b49b6bddccf3ac62f858ae3ecf56fa4c695b4ad010fea7292171f98b640dec968ea357d
@@ -1,24 +1,12 @@
1
- name: Ruby
2
-
3
- on:
4
- push:
5
- branches: [ master ]
6
- pull_request:
7
- branches: [ master ]
8
-
1
+ name: Test
2
+ on: [push, pull_request]
9
3
  jobs:
10
- build:
11
-
4
+ test:
12
5
  runs-on: ubuntu-latest
13
-
14
6
  steps:
15
- - uses: actions/checkout@v2
16
- - name: Set up Ruby 2.6
17
- uses: actions/setup-ruby@v1
7
+ - uses: actions/checkout@v3
8
+ - uses: ruby/setup-ruby@v1
18
9
  with:
19
- ruby-version: 2.6.x
20
- - name: Build and test with Rake
21
- run: |
22
- gem install bundler
23
- bundle install --jobs 4 --retry 3
24
- bundle exec rake
10
+ ruby-version: '3.1'
11
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
12
+ - run: bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  AllCops:
2
- TargetRubyVersion: 3.0.5
2
+ TargetRubyVersion: 3.1.1
3
3
  NewCops: enable
4
4
  SuggestExtensions: false
5
5
  Style/Documentation:
data/CHANGELOG.md CHANGED
@@ -1,6 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ ## 1.0.0.beta4
6
+
7
+ - Update json_schemer to version 2.0
8
+ - Breaking: Requires Ruby 3.1 or later
9
+ - Added: Parameters are available at `env[OpenapiFirst::PATH_PARAMS]`, `env[OpenapiFirst::QUERY_PARAMS]`, `env[OpenapiFirst::HEADER_PARAMS]`, `env[OpenapiFirst::COOKIE_PARAMS]` in case you need to access them separately. Merged path and query parameters are still available at `env[OpenapiFirst::PARAMS]`
10
+ - Breaking / Added: ResponseValidation now validates response headers
11
+ - Breaking / Added: RequestValidation now validates cookie, path and header parameters
12
+ - Breaking: multipart File uploads are now read and then validated
13
+ - Breaking: Remove OpenapiFirst.env method
14
+ - Breaking: Request validation returns 400 instead of 415 if request body is required, but empty
15
+
16
+ ## 1.0.0.beta3
17
+
18
+ - Remove obsolete dependency: deep_merge
19
+ - Remove obsolete dependency: hanami-utils
20
+
21
+ ## 1.0.0.beta2
22
+
23
+ - Fixed dependencies. Remove unused code.
24
+
3
25
  ## 1.0.0.beta1
26
+
4
27
  - Removed: `OpenapiFirst::Responder` and `OpenapiFirst::RackResponder`
5
28
  - Removed: `OpenapiFirst.app` and `OpenapiFirst.middleware`
6
29
  - Removed: `OpenapiFirst::Coverage`
data/Gemfile CHANGED
@@ -6,6 +6,9 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  group :test, :development do
9
- gem 'pry'
9
+ gem 'bundler'
10
+ gem 'rack-test'
11
+ gem 'rake'
12
+ gem 'rspec'
10
13
  gem 'rubocop'
11
14
  end
data/Gemfile.lock CHANGED
@@ -1,114 +1,102 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- openapi_first (1.0.0.beta1)
5
- deep_merge (>= 1.2.1)
4
+ openapi_first (1.0.0.beta4)
6
5
  hanami-router (~> 2.0.0)
7
- hanami-utils (~> 2.0.0)
8
6
  json_refs (~> 0.1, >= 0.1.7)
9
- json_schemer (~> 0.2.16)
7
+ json_schemer (~> 2.0.0)
10
8
  multi_json (~> 1.14)
11
- mustermann-contrib (~> 3.0.0)
9
+ openapi_parameters (~> 0.2.2)
12
10
  rack (>= 2.2, < 4.0)
13
11
 
14
12
  GEM
15
13
  remote: https://rubygems.org/
16
14
  specs:
17
15
  ast (2.4.2)
18
- coderay (1.1.3)
19
- concurrent-ruby (1.2.2)
20
- deep_merge (1.2.2)
16
+ base64 (0.1.1)
21
17
  diff-lcs (1.5.0)
22
- dry-core (1.0.0)
23
- concurrent-ruby (~> 1.0)
24
- zeitwerk (~> 2.6)
25
- dry-transformer (1.0.1)
26
- zeitwerk (~> 2.6)
27
- ecma-re-validator (0.4.0)
28
- regexp_parser (~> 2.2)
29
18
  hana (1.3.7)
30
19
  hanami-router (2.0.2)
31
20
  mustermann (~> 3.0)
32
21
  mustermann-contrib (~> 3.0)
33
22
  rack (~> 2.0)
34
- hanami-utils (2.0.3)
35
- concurrent-ruby (~> 1.0)
36
- dry-core (~> 1.0, < 2)
37
- dry-transformer (~> 1.0, < 2)
38
23
  hansi (0.2.1)
39
24
  json (2.6.3)
40
- json_refs (0.1.7)
25
+ json_refs (0.1.8)
41
26
  hana
42
- json_schemer (0.2.24)
43
- ecma-re-validator (~> 0.3)
27
+ json_schemer (2.0.0)
44
28
  hana (~> 1.3)
45
29
  regexp_parser (~> 2.0)
46
- uri_template (~> 0.7)
47
- method_source (1.0.0)
30
+ simpleidn (~> 0.2)
31
+ language_server-protocol (3.17.0.3)
48
32
  multi_json (1.15.0)
49
33
  mustermann (3.0.0)
50
34
  ruby2_keywords (~> 0.0.1)
51
35
  mustermann-contrib (3.0.0)
52
36
  hansi (~> 0.2.0)
53
37
  mustermann (= 3.0.0)
54
- openapi_parameters (0.2.0)
38
+ openapi_parameters (0.2.2)
55
39
  rack (>= 2.2)
56
40
  zeitwerk (~> 2.6)
57
- parallel (1.22.1)
58
- parser (3.2.1.1)
41
+ parallel (1.23.0)
42
+ parser (3.2.2.3)
59
43
  ast (~> 2.4.1)
60
- pry (0.14.2)
61
- coderay (~> 1.1)
62
- method_source (~> 1.0)
63
- rack (2.2.6.4)
64
- rack-test (1.1.0)
65
- rack (>= 1.0, < 3)
44
+ racc
45
+ racc (1.7.1)
46
+ rack (2.2.8)
47
+ rack-test (2.1.0)
48
+ rack (>= 1.3)
66
49
  rainbow (3.1.1)
67
50
  rake (13.0.6)
68
- regexp_parser (2.7.0)
69
- rexml (3.2.5)
51
+ regexp_parser (2.8.1)
52
+ rexml (3.2.6)
70
53
  rspec (3.12.0)
71
54
  rspec-core (~> 3.12.0)
72
55
  rspec-expectations (~> 3.12.0)
73
56
  rspec-mocks (~> 3.12.0)
74
- rspec-core (3.12.1)
57
+ rspec-core (3.12.2)
75
58
  rspec-support (~> 3.12.0)
76
- rspec-expectations (3.12.2)
59
+ rspec-expectations (3.12.3)
77
60
  diff-lcs (>= 1.2.0, < 2.0)
78
61
  rspec-support (~> 3.12.0)
79
- rspec-mocks (3.12.5)
62
+ rspec-mocks (3.12.6)
80
63
  diff-lcs (>= 1.2.0, < 2.0)
81
64
  rspec-support (~> 3.12.0)
82
- rspec-support (3.12.0)
83
- rubocop (1.48.1)
65
+ rspec-support (3.12.1)
66
+ rubocop (1.56.3)
67
+ base64 (~> 0.1.1)
84
68
  json (~> 2.3)
69
+ language_server-protocol (>= 3.17.0)
85
70
  parallel (~> 1.10)
86
- parser (>= 3.2.0.0)
71
+ parser (>= 3.2.2.3)
87
72
  rainbow (>= 2.2.2, < 4.0)
88
73
  regexp_parser (>= 1.8, < 3.0)
89
74
  rexml (>= 3.2.5, < 4.0)
90
- rubocop-ast (>= 1.26.0, < 2.0)
75
+ rubocop-ast (>= 1.28.1, < 2.0)
91
76
  ruby-progressbar (~> 1.7)
92
77
  unicode-display_width (>= 2.4.0, < 3.0)
93
- rubocop-ast (1.28.0)
78
+ rubocop-ast (1.29.0)
94
79
  parser (>= 3.2.1.0)
95
80
  ruby-progressbar (1.13.0)
96
81
  ruby2_keywords (0.0.5)
82
+ simpleidn (0.2.1)
83
+ unf (~> 0.1.4)
84
+ unf (0.1.4)
85
+ unf_ext
86
+ unf_ext (0.0.8.2)
97
87
  unicode-display_width (2.4.2)
98
- uri_template (0.7.0)
99
- zeitwerk (2.6.7)
88
+ zeitwerk (2.6.11)
100
89
 
101
90
  PLATFORMS
102
91
  arm64-darwin-21
92
+ x86_64-linux
103
93
 
104
94
  DEPENDENCIES
105
- bundler (~> 2)
95
+ bundler
106
96
  openapi_first!
107
- openapi_parameters (~> 0.2, <= 2.0.0)
108
- pry
109
- rack-test (~> 1)
110
- rake (~> 13)
111
- rspec (~> 3)
97
+ rack-test
98
+ rake
99
+ rspec
112
100
  rubocop
113
101
 
114
102
  BUNDLED WITH
data/README.md CHANGED
@@ -2,35 +2,37 @@
2
2
 
3
3
  [![Join the chat at https://gitter.im/openapi_first/community](https://badges.gitter.im/openapi_first/community.svg)](https://gitter.im/openapi_first/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
 
5
- OpenapiFirst helps to implement HTTP APIs based on an [OpenApi](https://www.openapis.org/) API description. The idea is that you create an API description first, then add code that returns data and implements your business logic and be done.
5
+ OpenapiFirst helps to implement HTTP APIs based on an [OpenAPI](https://www.openapis.org/) API description. It supports OpenAPI 3.0 and 3.1.
6
6
 
7
- Start with writing an OpenAPI file that describes the API, which you are about to implement. Use a [validator](https://github.com/stoplightio/spectral/) to make sure the file is valid.
7
+ It provides these Rack middlewares:
8
8
 
9
- OpenapiFirst consists of these Rack middlewares:
9
+ - [`OpenapiFirst::RequestValidation`](#request-validation) Validates the request against the API description and returns 400 if the request is invalid.
10
+ - [`OpenapiFirst::ResponseValidation`](#response-validation) Validates the response and raises an exception if the response body is invalid.
11
+ - [`OpenapiFirst::Router`](#openapifirstrouter) – This internal middleware is added automatically when using request/response validation. It adds the OpenAPI operation for the current request to the Rack env.
10
12
 
11
- - [`OpenapiFirst::RequestValidation`](#OpenapiFirst::RequestValidation) Validates the request against the API description and returns 400 if the request is invalid.
12
- - [`OpenapiFirst::ResponseValidation`](#OpenapiFirst::ResponseValidation) Validates the response and raises an exception if the response body is invalid.
13
- - [`OpenapiFirst::Router`](#OpenapiFirst::Router) – This internal middleware is added automatically when using request/response validation. It adds the OpenAPI operation for the current request to the Rack env or returns 404 if no operation was found.
13
+ Using Request and Response validation together ensures that your implementation follows exactly the API description. This enables you to use the API description as a single source of truth for your API, reason about details and use various tooling.
14
14
 
15
- ## OpenapiFirst::RequestValidation
15
+ ## Request Validation
16
16
 
17
- This middleware returns a 400 status code with a body that describes the error if the request is not valid.
17
+ The `OpenapiFirst::RequestValidation` middleware returns a 400 status code with a body that describes the error if the request is not valid.
18
18
 
19
19
  ```ruby
20
20
  use OpenapiFirst::RequestValidation, spec: 'openapi.yaml'
21
21
  ```
22
22
 
23
- This will add these fields to the Rack env:
24
- - `env[OpenapiFirst::OPERATION]` – The Operation object for the current request. This is an instance of `OpenapiFirst::Operation`.
23
+ It adds these fields to the Rack env:
24
+
25
25
  - `env[OpenapiFirst::PARAMS]` – The parsed parameters (query, path) for the current request (string keyed)
26
26
  - `env[OpenapiFirst::REQUEST_BODY]` – The parsed request body (string keyed)
27
+ - `env[OpenapiFirst::OPERATION]` (Added via Router) – The Operation object for the current request. This is an instance of `OpenapiFirst::Operation`.
27
28
 
28
29
  ### Options and defaults
29
30
 
30
31
  | Name | Possible values | Description | Default |
31
32
  | :------------- | --------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------- |
32
- | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load`
33
+ | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
33
34
  | `raise_error:` | `false`, `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` instead of returning 4xx. | `false` (don't raise an exception) |
35
+ | `error_response:`| `:default`, Your implementation of `ErrorResponse` | :default
34
36
 
35
37
  The error responses conform with [JSON:API](https://jsonapi.org).
36
38
 
@@ -52,8 +54,8 @@ content-type: "application/vnd.api+json"
52
54
  }
53
55
  ```
54
56
 
55
- ### Parameters
56
57
 
58
+ ### Parameters
57
59
 
58
60
  The `RequestValidation` middleware adds `env[OpenapiFirst::PARAMS]` (or `env['openapi.params']` ) with the converted query and path parameters. This only includes the parameters that are defined in the API description. It supports every [`style` and `explode` value as described](https://spec.openapis.org/oas/latest.html#style-examples) in the OpenAPI 3.0 and 3.1 specs. So you can do things these:
59
61
 
@@ -75,9 +77,10 @@ This middleware adds the parsed request body to `env[OpenapiFirst::REQUEST_BODY]
75
77
 
76
78
  The middleware will return a status `415` if the requests content type does not match or `400` if the request body is invalid.
77
79
 
78
- ### Header, Cookie, Path parameter validation
80
+ ### Header, Cookie, Query and Path parameter validation
79
81
 
80
- tbd.
82
+ The `RequestValidation` middleware validates the request headers, cookies and path parameters as defined in you API description. It returns a `400` status code if the request is invalid. It adds the parsed merged _path_ and _query_ parameters to `env['openapi.params']`.
83
+ Separate parsed parameters are made available by location at `env['openapi.path_params']`, `env['openapi.query']`, `env['openapi.headers']`, `env['openapi.cookies']` as well if you need to access them separately.
81
84
 
82
85
  ### readOnly / writeOnly properties
83
86
 
@@ -85,10 +88,9 @@ Request validation fails if request includes a property with `readOnly: true`.
85
88
 
86
89
  Response validation fails if response body includes a property with `writeOnly: true`.
87
90
 
88
- ## OpenapiFirst::ResponseValidation
89
-
90
- This middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
91
+ ## Response validation
91
92
 
93
+ The `OpenapiFirst::ResponseValidation` middleware is especially useful when testing. It _always_ raises an error if the response is not valid.
92
94
 
93
95
  ```ruby
94
96
  use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] == 'test'
@@ -96,25 +98,25 @@ use OpenapiFirst::ResponseValidation, spec: 'openapi.yaml' if ENV['RACK_ENV'] ==
96
98
 
97
99
  ### Options
98
100
 
99
- | Name | Possible values | Description | Default |
100
- | :------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
101
- | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load`
101
+ | Name | Possible values | Description | Default |
102
+ | :------ | --------------- | ---------------------------------------------------------------- | ------- |
103
+ | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` |
102
104
 
103
105
  ## OpenapiFirst::Router
104
106
 
105
- This middleware is used automatically, but you can add it to the top of your middleware stack if you want to change configuration.
107
+ This middleware is used automatically, but you can add it to the top of your middleware stack if you want to customize the behavior via options.
106
108
 
107
109
  ```ruby
108
110
  use OpenapiFirst::Router, spec: './openapi/openapi.yaml'
109
111
  ```
110
112
 
111
- This middleware adds `env[OpenapiFirst::OPERATION]` which holds an Operation object that responds to `#operation_id`, `#path` (and `#[string]` to access raw fields).
113
+ This middleware adds `env['openapi.operation']` which holds an instance of `OpenapiFirst::Operation` that responds to `#operation_id`, `#path` (and `#[]` to access raw fields).
112
114
 
113
115
  ### Options and defaults
114
116
 
115
117
  | Name | Possible values | Description | Default |
116
118
  | :------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
117
- | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` | |
119
+ | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` | |
118
120
  | `raise_error:` | `false`, `true` | If set to true the middleware raises `OpenapiFirst::NotFoundError` when a path or method was not found in the API description. This is useful during testing to spot an incomplete API description. | `false` (don't raise an exception) |
119
121
  | `not_found:` | `:continue`, `:halt` | If set to `:continue` the middleware will not return 404 (405, 415), but just pass handling the request to the next middleware or application in the Rack stack. If combined with `raise_error: true` `raise_error` gets preference and an exception is raised. | `:halt` (return 4xx response) |
120
122
 
@@ -164,9 +166,9 @@ run OpenapiFirst.app(spec, namespace: Pets)
164
166
 
165
167
  Run `bin/setup` to install dependencies.
166
168
 
167
- Run `bundle exec rspec` to run the tests.
169
+ See `bundle exec rake` to run the linter and the tests.
168
170
 
169
- See `bundle exec rake -T` for rubygems related tasks.
171
+ Run `bundle exec rspec` to run the tests only.
170
172
 
171
173
  ## Benchmarks
172
174
 
@@ -1,20 +1,18 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- openapi_first (1.0.0.beta1)
5
- deep_merge (>= 1.2.1)
4
+ openapi_first (1.0.0.beta3)
6
5
  hanami-router (~> 2.0.0)
7
- hanami-utils (~> 2.0.0)
8
6
  json_refs (~> 0.1, >= 0.1.7)
9
- json_schemer (~> 0.2.16)
7
+ json_schemer (~> 2.0.0)
10
8
  multi_json (~> 1.14)
11
- mustermann-contrib (~> 3.0.0)
9
+ openapi_parameters (~> 0.2.2)
12
10
  rack (>= 2.2, < 4.0)
13
11
 
14
12
  GEM
15
13
  remote: https://rubygems.org/
16
14
  specs:
17
- activesupport (7.0.4.3)
15
+ activesupport (7.0.8)
18
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
19
17
  i18n (>= 1.6, < 2)
20
18
  minitest (>= 5.1)
@@ -28,7 +26,6 @@ GEM
28
26
  openapi_parser (~> 1.0)
29
27
  rack (>= 1.5)
30
28
  concurrent-ruby (1.2.2)
31
- deep_merge (1.2.2)
32
29
  dry-core (1.0.0)
33
30
  concurrent-ruby (~> 1.0)
34
31
  zeitwerk (~> 2.6)
@@ -37,22 +34,18 @@ GEM
37
34
  concurrent-ruby (~> 1.0)
38
35
  dry-core (~> 1.0, < 2)
39
36
  zeitwerk (~> 2.6)
40
- dry-transformer (1.0.1)
41
- zeitwerk (~> 2.6)
42
37
  dry-types (1.7.1)
43
38
  concurrent-ruby (~> 1.0)
44
39
  dry-core (~> 1.0)
45
40
  dry-inflector (~> 1.0)
46
41
  dry-logic (~> 1.4)
47
42
  zeitwerk (~> 2.6)
48
- ecma-re-validator (0.4.0)
49
- regexp_parser (~> 2.2)
50
- grape (1.7.0)
43
+ grape (1.7.1)
51
44
  activesupport
52
45
  builder
53
46
  dry-types (>= 1.1)
54
47
  mustermann-grape (~> 1.0.0)
55
- rack (>= 1.3.0)
48
+ rack (>= 1.3.0, < 3)
56
49
  rack-accept
57
50
  hana (1.3.7)
58
51
  hanami-api (0.3.0)
@@ -61,23 +54,18 @@ GEM
61
54
  mustermann (~> 3.0)
62
55
  mustermann-contrib (~> 3.0)
63
56
  rack (~> 2.0)
64
- hanami-utils (2.0.3)
65
- concurrent-ruby (~> 1.0)
66
- dry-core (~> 1.0, < 2)
67
- dry-transformer (~> 1.0, < 2)
68
57
  hansi (0.2.1)
69
- i18n (1.12.0)
58
+ i18n (1.14.1)
70
59
  concurrent-ruby (~> 1.0)
71
- json_refs (0.1.7)
60
+ json_refs (0.1.8)
72
61
  hana
73
62
  json_schema (0.21.0)
74
- json_schemer (0.2.24)
75
- ecma-re-validator (~> 0.3)
63
+ json_schemer (2.0.0)
76
64
  hana (~> 1.3)
77
65
  regexp_parser (~> 2.0)
78
- uri_template (~> 0.7)
66
+ simpleidn (~> 0.2)
79
67
  memory_profiler (1.0.1)
80
- minitest (5.18.0)
68
+ minitest (5.20.0)
81
69
  multi_json (1.15.0)
82
70
  mustermann (3.0.0)
83
71
  ruby2_keywords (~> 0.0.1)
@@ -86,24 +74,29 @@ GEM
86
74
  mustermann (= 3.0.0)
87
75
  mustermann-grape (1.0.2)
88
76
  mustermann (>= 1.0.0)
89
- nio4r (2.5.8)
77
+ nio4r (2.5.9)
78
+ openapi_parameters (0.2.2)
79
+ rack (>= 2.2)
80
+ zeitwerk (~> 2.6)
90
81
  openapi_parser (1.0.0)
91
- puma (6.2.0)
82
+ puma (6.3.1)
92
83
  nio4r (~> 2.0)
93
- rack (2.2.6.4)
84
+ rack (2.2.8)
94
85
  rack-accept (0.4.5)
95
86
  rack (>= 0.4)
96
- rack-protection (3.0.5)
87
+ rack-protection (3.0.6)
97
88
  rack
98
- regexp_parser (2.8.0)
99
- roda (3.66.0)
89
+ regexp_parser (2.8.1)
90
+ roda (3.68.0)
100
91
  rack
101
92
  ruby2_keywords (0.0.5)
102
93
  seg (1.2.0)
103
- sinatra (3.0.5)
94
+ simpleidn (0.2.1)
95
+ unf (~> 0.1.4)
96
+ sinatra (3.0.6)
104
97
  mustermann (~> 3.0)
105
98
  rack (~> 2.2, >= 2.2.4)
106
- rack-protection (= 3.0.5)
99
+ rack-protection (= 3.0.6)
107
100
  tilt (~> 2.0)
108
101
  syro (3.2.1)
109
102
  rack (>= 1.6.0)
@@ -111,8 +104,10 @@ GEM
111
104
  tilt (2.1.0)
112
105
  tzinfo (2.0.6)
113
106
  concurrent-ruby (~> 1.0)
114
- uri_template (0.7.0)
115
- zeitwerk (2.6.7)
107
+ unf (0.1.4)
108
+ unf_ext
109
+ unf_ext (0.0.8.2)
110
+ zeitwerk (2.6.11)
116
111
 
117
112
  PLATFORMS
118
113
  arm64-darwin-21
@@ -19,7 +19,6 @@ app = Class.new(Hanami::API) do
19
19
  end
20
20
  end.new
21
21
 
22
- use OpenapiFirst::Router, spec: File.absolute_path('./openapi.yaml', __dir__)
23
- use OpenapiFirst::RequestValidation
22
+ use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
24
23
 
25
24
  run app
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'openapi_first'
5
+
6
+ app = Rack::Builder.new do
7
+ use OpenapiFirst::RequestValidation, spec: File.expand_path('./openapi.yaml', __dir__)
8
+
9
+ handlers = {
10
+ 'find_thing' => lambda do |env|
11
+ params = env[OpenapiFirst::PARAMS]
12
+ body = MultiJson.dump(hello: 'world', id: params.fetch('id'))
13
+ [200, { 'Content-Type' => 'application/json' }, [body]]
14
+ end,
15
+ 'find_things' => lambda do |_env|
16
+ body = MultiJson.dump(hello: 'world')
17
+ [200, { 'Content-Type' => 'application/json' }, [body]]
18
+ end,
19
+ 'create_thing' => lambda do |_env|
20
+ body = MultiJson.dump(hello: 'world')
21
+ [201, { 'Content-Type' => 'application/json' }, [body]]
22
+ end
23
+ }
24
+
25
+ not_found = ->(_env) { [404, {}, []] }
26
+
27
+ run(lambda do |env|
28
+ handlers.fetch(env[OpenapiFirst::OPERATION]&.operation_id, not_found).call(env)
29
+ end)
30
+ end
31
+
32
+ run app
@@ -2,21 +2,24 @@
2
2
 
3
3
  require 'multi_json'
4
4
  require 'openapi_first'
5
+ require 'hanami/api'
5
6
 
6
- namespace = Module.new do
7
- def self.find_thing(params, _res)
8
- { hello: 'world', id: params.fetch(:id) }
7
+ app = Class.new(Hanami::API) do
8
+ get '/hello/:id' do
9
+ json(hello: 'world', id: params.fetch(:id))
9
10
  end
10
11
 
11
- def self.find_things(_params, _res)
12
- [{ hello: 'world' }]
12
+ get '/hello' do
13
+ json([{ hello: 'world' }])
13
14
  end
14
15
 
15
- def self.create_thing(_params, res)
16
- res.status = 201
17
- { hello: 'world' }
16
+ post '/hello' do
17
+ status 201
18
+ json(hello: 'world')
18
19
  end
19
- end
20
+ end.new
20
21
 
21
- oas_path = File.absolute_path('./openapi.yaml', __dir__)
22
- run OpenapiFirst.app(oas_path, namespace: namespace, response_validation: true)
22
+ use OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
23
+ use OpenapiFirst::ResponseValidation
24
+
25
+ run app
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'multi_json'
4
+ require 'openapi_first'
5
+ require 'sinatra'
6
+
7
+ class SinatraWithOpenapiFirstExample < 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 OpenapiFirst::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__)
28
+
29
+ run SinatraWithOpenapiFirstExample
@@ -23,7 +23,7 @@ module OpenapiFirst
23
23
  errors = [err]
24
24
 
25
25
  Rack::Response.new(
26
- MultiJson.dump(errors: errors),
26
+ MultiJson.dump(errors:),
27
27
  400,
28
28
  Rack::CONTENT_TYPE => 'application/vnd.api+json'
29
29
  ).finish
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenapiFirst
4
+ class Config
5
+ def initialize(error_response: :default)
6
+ @error_response = error_response
7
+ end
8
+
9
+ attr_reader :error_response
10
+
11
+ def self.default_options
12
+ @default_options ||= new
13
+ end
14
+
15
+ def self.default_options=(options)
16
+ @default_options = new(**options)
17
+ end
18
+ end
19
+ end