openapi_first 1.0.0.beta1 → 1.0.0.beta4

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.
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