diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f7f48b0..dbd09f9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,34 +2,52 @@ ## Unreleased -### Changed +## 3.0.0 + +### openapi_first + +#### Changed - Breaking: Trailing slashes are no longer ignored in dynamic paths. See [#403](https://github.com/ahx/openapi_first/issues/403). Before this change `GET /things/24/` matched `/things/{id}:`, but it no longer does. - Breaking: Failure type `:response_not_found` was split into two more specific types `:response_content_type_not_found` and `:response_status_not_found`. This should be mostly internal stuff. So if your custom error response used `response_not_found`, you will have to adapt. -- `OpenapiFirst::Test.app` now returns an instance of `OpenapiFirst::Test::App`, instead of `Rack::Builer` and delegates methods other than `#call` to the original app. This wrapper adds validated requests, responses to the rack env at `env[OpenapiFirst::Test::REQUEST]`, `env[OpenapiFirst::Test::RESPONSE]`. This makes it possible to test Rails engines. Thanks to Josh! See [#410](https://github.com/ahx/openapi_first/issues/410). -- `OpenapiFirst::Test` now falls back to using globally registered OADs if nothing was registered inside `OpenapiFirst::Test.setup`. +- Deprecated configuration fields `request_validation_raise_error` and `response_validation_raise_error`. Please pass the `raise_error:` option to the middlewares directly. -### Added -- The Coverage feature in `OpenapiFirst::Test` now supports parallel tests via a DRB client/sever. Thanks to Richard! See [#394](https://github.com/ahx/openapi_first/issues/394). -- Added `OpenapiFirst::Test` Configuration options which are useful when adopting OpenAPI: - - `ignore_unknown_response_status = true` to make API coverage no longer complain about undefined response statuses it sees during a test run. - - `minimum_coverage=` is no longer deprecated. This is useful when gradually adopting OpenAPI +#### Added - Added support to register OADs globally via: ```ruby OpenapiFirst.configure { |config| config.register('openapi.yaml') } ``` This makes the `spec` argument in middlewares optional and removes the necessity to load the OAD in the same place where you use the middlewares and adds a cache for parsed OADs. -### Removed +#### Removed - Removed deprecated methods which produced a warning since 2.0.0. -- Removed internally used `Test::Coverage.current_run, .plans, .install, .uninstall`. If you are using these, use `OpenapiFirst::Test.setup` instead. - Removed `OpenapiFirst::Configuration#clone`. Use `#child` instead. -- It's not possible to remove locally added hooks. But you can restart with a blank list of hooks by calling `OpenapiFirst.configure` +- It's no longer supported to remove locally added hooks during runtime. -### Fixed +#### Fixed - Update dependency `openapi_parameters` to >= 0.7.0, because that version supports unpacking parameters the use `style: deepObject` with `explode: true`. - Make `OpenapiFirst::Test.setup` more robust by adding `OpenapiFirst::Configuration#child` so it does not matter if you load our OAD before callig `OpenapiFirst::Test.setup`. +### openapi_first/test + +#### Changed +- `OpenapiFirst::Test.app` now returns an instance of `OpenapiFirst::Test::App`, instead of `Rack::Builer` and delegates methods other than `#call` to the original app. This wrapper adds validated requests, responses to the rack env at `env[OpenapiFirst::Test::REQUEST]`, `env[OpenapiFirst::Test::RESPONSE]`. This makes it possible to test Rails engines. Thanks to Josh! See [#410](https://github.com/ahx/openapi_first/issues/410). +- `OpenapiFirst::Test` now falls back to using globally registered OADs if nothing was registered inside `OpenapiFirst::Test.setup`. +- 401er and 500er status are okay to not be described. + +#### Added +- The Coverage feature in `OpenapiFirst::Test` now supports parallel tests via a DRB client/sever. Thanks to Richard! See [#394](https://github.com/ahx/openapi_first/issues/394). +- Added `OpenapiFirst::Test` Configuration options which are useful when adopting OpenAPI: + - `ignore_unknown_response_status = true` to make API coverage no longer complain about undefined response statuses it sees during a test run. + - `minimum_coverage=` is no longer deprecated. This is useful when gradually adopting OpenAPI +- `ignored_unknown_status=` to overwrite the whole list of ignored unknown status at once + +#### Removed +- Removed internally used `Test::Coverage.current_run, .plans, .install, .uninstall`. If you are using these, use `OpenapiFirst::Test.setup` instead. + +#### Fixed +- Make `OpenapiFirst::Test.setup` more robust by adding `OpenapiFirst::Configuration#child` so it does not matter if you load our OAD before callig `OpenapiFirst::Test.setup`. + ## 2.11.1 - OpenapiFirst can now route requests correctly for paths like `/stuffs` and `/stuffs{format}` (https://github.com/ahx/openapi_first/issues/386) diff --git a/Gemfile.lock b/Gemfile.lock index d13a0d64..97fdbcd5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - openapi_first (2.11.1) + openapi_first (3.0.0) hana (~> 1.3) json_schemer (>= 2.1, < 3.0) openapi_parameters (>= 0.7.0, < 2.0) @@ -10,29 +10,31 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (8.0.2.1) - actionpack (= 8.0.2.1) - activesupport (= 8.0.2.1) + action_text-trix (2.1.15) + railties + actioncable (8.1.1) + actionpack (= 8.1.1) + activesupport (= 8.1.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailbox (8.1.1) + actionpack (= 8.1.1) + activejob (= 8.1.1) + activerecord (= 8.1.1) + activestorage (= 8.1.1) + activesupport (= 8.1.1) mail (>= 2.8.0) - actionmailer (8.0.2.1) - actionpack (= 8.0.2.1) - actionview (= 8.0.2.1) - activejob (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionmailer (8.1.1) + actionpack (= 8.1.1) + actionview (= 8.1.1) + activejob (= 8.1.1) + activesupport (= 8.1.1) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.2.1) - actionview (= 8.0.2.1) - activesupport (= 8.0.2.1) + actionpack (8.1.1) + actionview (= 8.1.1) + activesupport (= 8.1.1) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -40,42 +42,43 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.2.1) - actionpack (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + actiontext (8.1.1) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.1) + activerecord (= 8.1.1) + activestorage (= 8.1.1) + activesupport (= 8.1.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.2.1) - activesupport (= 8.0.2.1) + actionview (8.1.1) + activesupport (= 8.1.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.2.1) - activesupport (= 8.0.2.1) + activejob (8.1.1) + activesupport (= 8.1.1) globalid (>= 0.3.6) - activemodel (8.0.2.1) - activesupport (= 8.0.2.1) - activerecord (8.0.2.1) - activemodel (= 8.0.2.1) - activesupport (= 8.0.2.1) + activemodel (8.1.1) + activesupport (= 8.1.1) + activerecord (8.1.1) + activemodel (= 8.1.1) + activesupport (= 8.1.1) timeout (>= 0.4.0) - activestorage (8.0.2.1) - actionpack (= 8.0.2.1) - activejob (= 8.0.2.1) - activerecord (= 8.0.2.1) - activesupport (= 8.0.2.1) + activestorage (8.1.1) + actionpack (= 8.1.1) + activejob (= 8.1.1) + activerecord (= 8.1.1) + activesupport (= 8.1.1) marcel (~> 1.0) - activesupport (8.0.2.1) + activesupport (8.1.1) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) @@ -83,29 +86,28 @@ GEM uri (>= 0.13.1) ast (2.4.3) base64 (0.3.0) - benchmark (0.4.1) - bigdecimal (3.2.3) + bigdecimal (3.3.1) builder (3.3.0) concurrent-ruby (1.3.5) connection_pool (2.5.4) crass (1.0.6) - date (3.4.1) + date (3.5.0) diff-lcs (1.6.2) docile (1.4.1) drb (2.2.3) - erb (5.0.2) + erb (5.1.3) erubi (1.13.1) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) hana (1.3.7) i18n (1.14.7) concurrent-ruby (~> 1.0) io-console (0.8.1) - irb (1.15.2) + irb (1.15.3) pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - json (2.13.2) + json (2.15.2) json_schemer (2.4.0) bigdecimal hana (~> 1.3) @@ -117,17 +119,18 @@ GEM loofah (2.24.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) - mail (2.8.1) + mail (2.9.0) + logger mini_mime (>= 0.1.1) net-imap net-pop net-smtp - marcel (1.0.4) + marcel (1.1.0) mini_mime (1.1.5) - minitest (5.25.5) + minitest (5.26.0) mustermann (3.0.4) ruby2_keywords (~> 0.0.1) - net-imap (0.5.10) + net-imap (0.5.12) date net-protocol net-pop (0.1.2) @@ -136,7 +139,7 @@ GEM timeout net-smtp (0.5.1) net-protocol - nio4r (2.7.4) + nio4r (2.7.5) nokogiri (1.18.10-aarch64-linux-gnu) racc (~> 1.4) nokogiri (1.18.10-aarch64-linux-musl) @@ -156,19 +159,19 @@ GEM openapi_parameters (0.8.0) rack (>= 2.2) parallel (1.27.0) - parser (3.3.9.0) + parser (3.3.10.0) ast (~> 2.4.1) racc - pp (0.6.2) + pp (0.6.3) prettyprint prettyprint (0.2.0) - prism (1.5.1) + prism (1.6.0) psych (5.2.6) date stringio racc (1.8.1) - rack (3.2.2) - rack-protection (4.2.0) + rack (3.2.3) + rack-protection (4.2.1) base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) @@ -179,20 +182,20 @@ GEM rack (>= 1.3) rackup (2.2.1) rack (>= 3) - rails (8.0.2.1) - actioncable (= 8.0.2.1) - actionmailbox (= 8.0.2.1) - actionmailer (= 8.0.2.1) - actionpack (= 8.0.2.1) - actiontext (= 8.0.2.1) - actionview (= 8.0.2.1) - activejob (= 8.0.2.1) - activemodel (= 8.0.2.1) - activerecord (= 8.0.2.1) - activestorage (= 8.0.2.1) - activesupport (= 8.0.2.1) + rails (8.1.1) + actioncable (= 8.1.1) + actionmailbox (= 8.1.1) + actionmailer (= 8.1.1) + actionpack (= 8.1.1) + actiontext (= 8.1.1) + actionview (= 8.1.1) + activejob (= 8.1.1) + activemodel (= 8.1.1) + activerecord (= 8.1.1) + activestorage (= 8.1.1) + activesupport (= 8.1.1) bundler (>= 1.15.0) - railties (= 8.0.2.1) + railties (= 8.1.1) rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest @@ -200,36 +203,38 @@ GEM rails-html-sanitizer (1.6.2) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (8.0.2.1) - actionpack (= 8.0.2.1) - activesupport (= 8.0.2.1) + railties (8.1.1) + actionpack (= 8.1.1) + activesupport (= 8.1.1) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) + tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.0) - rdoc (6.14.2) + rake (13.3.1) + rdoc (6.15.1) erb psych (>= 4.0.0) - regexp_parser (2.11.2) + tsort + regexp_parser (2.11.3) reline (0.6.2) io-console (~> 0.5) - rspec (3.13.1) + rspec (3.13.2) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.5) + rspec-core (3.13.6) rspec-support (~> 3.13.0) rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.5) + rspec-mocks (3.13.7) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.5) - rubocop (1.80.2) + rspec-support (3.13.6) + rubocop (1.81.7) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -237,16 +242,16 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.46.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.46.0) + rubocop-ast (1.47.1) parser (>= 3.3.7.2) prism (~> 1.4) - rubocop-performance (1.26.0) + rubocop-performance (1.26.1) lint_roller (~> 1.1) rubocop (>= 1.75.0, < 2.0) - rubocop-ast (>= 1.44.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) securerandom (0.4.1) @@ -257,23 +262,24 @@ GEM simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) simpleidn (0.2.3) - sinatra (4.2.0) + sinatra (4.2.1) logger (>= 1.6.0) mustermann (~> 3.0) rack (>= 3.0.0, < 4) - rack-protection (= 4.2.0) + rack-protection (= 4.2.1) rack-session (>= 2.0.0, < 3) tilt (~> 2.0) stringio (3.1.7) thor (1.4.0) tilt (2.6.1) - timeout (0.4.3) + timeout (0.4.4) + tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) unicode-emoji (4.1.0) - uri (1.0.3) + uri (1.1.0) useragent (0.16.11) websocket-driver (0.8.0) base64 diff --git a/benchmarks/Gemfile.lock b/benchmarks/Gemfile.lock index 180c25e6..ebaead06 100644 --- a/benchmarks/Gemfile.lock +++ b/benchmarks/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - openapi_first (2.11.1) + openapi_first (3.0.0) hana (~> 1.3) json_schemer (>= 2.1, < 3.0) openapi_parameters (>= 0.7.0, < 2.0) @@ -11,12 +11,12 @@ GEM remote: https://rubygems.org/ specs: base64 (0.3.0) - benchmark (0.4.1) + benchmark (0.5.0) benchmark-ips (2.14.0) benchmark-memory (0.2.0) memory_profiler (~> 1) - bigdecimal (3.2.2) - committee (5.5.5) + bigdecimal (3.3.1) + committee (5.6.1) json_schema (~> 0.14, >= 0.14.3) openapi_parser (~> 2.0) rack (>= 1.5) @@ -31,36 +31,36 @@ GEM memory_profiler (1.1.0) mustermann (3.0.4) ruby2_keywords (~> 0.0.1) - nio4r (2.7.4) + nio4r (2.7.5) openapi_parameters (0.8.0) rack (>= 2.2) - openapi_parser (2.2.6) - optparse (0.6.0) + openapi_parser (2.3.0) + optparse (0.8.0) profile-viewer (0.0.5) optparse webrick - puma (6.6.1) + puma (7.1.0) nio4r (~> 2.0) rack (3.2.3) - rack-protection (4.2.0) + rack-protection (4.2.1) base64 (>= 0.1.0) logger (>= 1.6.0) rack (>= 3.0.0, < 4) rack-session (2.1.1) base64 (>= 0.1.0) rack (>= 3.0.0) - regexp_parser (2.11.2) + regexp_parser (2.11.3) ruby2_keywords (0.0.5) simpleidn (0.2.3) - sinatra (4.2.0) + sinatra (4.2.1) logger (>= 1.6.0) mustermann (~> 3.0) rack (>= 3.0.0, < 4) - rack-protection (= 4.2.0) + rack-protection (= 4.2.1) rack-session (>= 2.0.0, < 3) tilt (~> 2.0) tilt (2.6.1) - vernier (1.8.0) + vernier (1.8.1) webrick (1.9.1) PLATFORMS diff --git a/benchmarks/apps/openapi_first.ru b/benchmarks/apps/openapi_first.ru index 144944bd..f828ff37 100644 --- a/benchmarks/apps/openapi_first.ru +++ b/benchmarks/apps/openapi_first.ru @@ -4,5 +4,7 @@ require 'json' require 'openapi_first' require_relative 'app' -use OpenapiFirst::Middlewares::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__) +OpenapiFirst.register File.absolute_path('./openapi.yaml', __dir__) + +use OpenapiFirst::Middlewares::RequestValidation run App diff --git a/benchmarks/apps/openapi_first_with_response_validation.ru b/benchmarks/apps/openapi_first_with_response_validation.ru index 9c29334b..cddf9eba 100644 --- a/benchmarks/apps/openapi_first_with_response_validation.ru +++ b/benchmarks/apps/openapi_first_with_response_validation.ru @@ -4,7 +4,9 @@ require 'json' require 'openapi_first' require_relative 'app' -use OpenapiFirst::Middlewares::RequestValidation, spec: File.absolute_path('./openapi.yaml', __dir__) -use OpenapiFirst::Middlewares::ResponseValidation, spec: File.absolute_path('./openapi.yaml', __dir__) +OpenapiFirst.register File.absolute_path('./openapi.yaml', __dir__) + +use OpenapiFirst::Middlewares::RequestValidation +use OpenapiFirst::Middlewares::ResponseValidation run App diff --git a/examples/rails_app/Gemfile.lock b/examples/rails_app/Gemfile.lock index c5ed02e4..4e5f8fb1 100644 --- a/examples/rails_app/Gemfile.lock +++ b/examples/rails_app/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: ../.. specs: - openapi_first (2.11.1) + openapi_first (3.0.0) hana (~> 1.3) json_schemer (>= 2.1, < 3.0) openapi_parameters (>= 0.7.0, < 2.0) diff --git a/examples/rails_app/bin/setup b/examples/rails_app/bin/setup index cf2acd18..cd0d59e7 100755 --- a/examples/rails_app/bin/setup +++ b/examples/rails_app/bin/setup @@ -1,8 +1,8 @@ #!/usr/bin/env ruby require "fileutils" -# path to your application root. APP_ROOT = File.expand_path("..", __dir__) +APP_NAME = "train-travel" def system!(*args) system(*args, exception: true) @@ -22,4 +22,8 @@ FileUtils.chdir APP_ROOT do puts "\n== Restarting application server ==" system! "bin/rails restart" + + # puts "\n== Configuring puma-dev ==" + # system "ln -nfs #{APP_ROOT} ~/.puma-dev/#{APP_NAME}" + # system "curl -Is https://#{APP_NAME}.test/up | head -n 1" end diff --git a/examples/rails_app/config/application.rb b/examples/rails_app/config/application.rb index 53595712..2243bdf2 100644 --- a/examples/rails_app/config/application.rb +++ b/examples/rails_app/config/application.rb @@ -26,7 +26,7 @@ class Application < Rails::Application # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. # Common ones are `templates`, `generators`, or `middleware`, for example. - config.autoload_lib(ignore: %w(assets tasks)) + config.autoload_lib(ignore: %w[assets tasks]) # Configuration for the application, engines, and railties goes here. # diff --git a/examples/rails_app/config/environments/development.rb b/examples/rails_app/config/environments/development.rb index 38ec6a20..461d99dd 100644 --- a/examples/rails_app/config/environments/development.rb +++ b/examples/rails_app/config/environments/development.rb @@ -14,16 +14,14 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable server timing + # Enable server timing. config.server_timing = true # Enable/disable caching. By default caching is disabled. # Run rails dev:cache to toggle caching. if Rails.root.join("tmp/caching-dev.txt").exist? config.cache_store = :memory_store - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{2.days.to_i}" - } + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -33,8 +31,12 @@ # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false + config.action_mailer.default_url_options = { host: "localhost", port: 3000 } + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log @@ -47,16 +49,15 @@ # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true - # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true + config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true end diff --git a/examples/rails_app/config/environments/production.rb b/examples/rails_app/config/environments/production.rb index c0bf4e71..e164d0f7 100644 --- a/examples/rails_app/config/environments/production.rb +++ b/examples/rails_app/config/environments/production.rb @@ -41,6 +41,9 @@ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. config.force_ssl = true + # Skip http-to-https redirect for the default health check endpoint. + # config.ssl_options = { redirect: { exclude: ->(request) { request.path == "/up" } } } + # Log to STDOUT by default config.logger = ActiveSupport::Logger.new(STDOUT) .tap { |logger| logger.formatter = ::Logger::Formatter.new } @@ -61,6 +64,8 @@ # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "train_travel_production" + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. diff --git a/examples/rails_app/config/environments/test.rb b/examples/rails_app/config/environments/test.rb index c468e296..1735b493 100644 --- a/examples/rails_app/config/environments/test.rb +++ b/examples/rails_app/config/environments/test.rb @@ -18,10 +18,7 @@ config.eager_load = ENV["CI"].present? # Configure public file server for tests with Cache-Control for performance. - config.public_file_server.enabled = true - config.public_file_server.headers = { - "Cache-Control" => "public, max-age=#{1.hour.to_i}" - } + config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. config.consider_all_requests_local = true @@ -34,6 +31,8 @@ # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. @@ -41,6 +40,10 @@ # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "www.example.com" } + # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr @@ -56,6 +59,6 @@ # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. config.action_controller.raise_on_missing_callback_actions = true end diff --git a/examples/rails_app/config/initializers/filter_parameter_logging.rb b/examples/rails_app/config/initializers/filter_parameter_logging.rb index c2d89e28..c010b83d 100644 --- a/examples/rails_app/config/initializers/filter_parameter_logging.rb +++ b/examples/rails_app/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn ] diff --git a/examples/rails_app/config/puma.rb b/examples/rails_app/config/puma.rb index afa809b4..03c166f4 100644 --- a/examples/rails_app/config/puma.rb +++ b/examples/rails_app/config/puma.rb @@ -2,34 +2,33 @@ # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } -threads min_threads_count, max_threads_count - -# Specifies that the worker count should equal the number of processors in production. -if ENV["RAILS_ENV"] == "production" - require "concurrent-ruby" - worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }) - workers worker_count if worker_count > 1 -end - -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count # Specifies the `port` that Puma will listen on to receive requests; default is 3000. -port ENV.fetch("PORT") { 3000 } - -# Specifies the `environment` that Puma will run in. -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +port ENV.fetch("PORT", 3000) # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart + +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/lib/openapi_first/configuration.rb b/lib/openapi_first/configuration.rb index dbdfff82..090c7c97 100644 --- a/lib/openapi_first/configuration.rb +++ b/lib/openapi_first/configuration.rb @@ -22,8 +22,13 @@ def register(path_or_definition, as: :default) OpenapiFirst.register(path_or_definition, as:) end - attr_reader :request_validation_error_response, :hooks - attr_accessor :request_validation_raise_error, :response_validation_raise_error, :path + attr_reader :hooks, :request_validation_error_response + attr_accessor :path + + # @deprecated + attr_reader :request_validation_raise_error + # @deprecated + attr_reader :response_validation_raise_error # Return a child configuration that still receives updates of global hooks. def child @@ -35,6 +40,22 @@ def clone raise NoMethodError, 'OpenapiFirst::Configuration#clone was removed. You want to call #child instead' end + # @deprecated Pass `raise_error:` to OpenapiFirst::Middlewares::RequestValidation directly + def request_validation_raise_error=(value) + message = 'Setting OpenapiFirst::Configuration#request_validation_raise_error will be removed. ' \ + 'Please pass `raise_error:` to `OpenapiFirst::Middlewares::RequestValidation directly`' + warn message, category: :deprecated + @request_validation_raise_error = value + end + + # @deprecated Pass `raise_error:` to OpenapiFirst::Middlewares::ResponseValidation directly + def response_validation_raise_error=(value) + message = 'Setting OpenapiFirst::Configuration#request_validation_raise_error will be removed. ' \ + 'Please pass `raise_error:` to `OpenapiFirst::Middlewares::ResponseValidation directly`' + warn message + @response_validation_raise_error = value + end + HOOKS.each do |hook| define_method(hook) do |&block| return hooks[hook] if block.nil? diff --git a/lib/openapi_first/middlewares/request_validation.rb b/lib/openapi_first/middlewares/request_validation.rb index c6f4acf6..1f07e2ba 100644 --- a/lib/openapi_first/middlewares/request_validation.rb +++ b/lib/openapi_first/middlewares/request_validation.rb @@ -6,9 +6,11 @@ module Middlewares # A Rack middleware to validate requests against an OpenAPI API description class RequestValidation # @param app The parent Rack application - # @param spec [String, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. + # @param spec [String, Symbol, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. + # If you pass a Symbol, it will load the OAD registered via `OpenapiFirst.register` + # If you leave this blank, it will load the OAD registered as `:default` # @param options Hash - # :spec [String, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. + # :spec [String, Symbol, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. # This will be deprecated. Please use spec argument instead. # :raise_error A Boolean indicating whether to raise an error if validation fails. # default: false diff --git a/lib/openapi_first/middlewares/response_validation.rb b/lib/openapi_first/middlewares/response_validation.rb index 0b3fe7a6..ba12ddbd 100644 --- a/lib/openapi_first/middlewares/response_validation.rb +++ b/lib/openapi_first/middlewares/response_validation.rb @@ -6,7 +6,9 @@ module OpenapiFirst module Middlewares # A Rack middleware to validate requests against an OpenAPI API description class ResponseValidation - # @param spec [String, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition + # @param spec [String, Symbol, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. + # If you pass a Symbol, it will load the OAD registered via `OpenapiFirst.register` + # If you leave this blank, it will load the OAD registered as `:default` # @param options Hash # :spec [String, OpenapiFirst::Definition] Path to the OpenAPI file or an instance of Definition. # This will be deprecated. Please use spec argument instead. diff --git a/lib/openapi_first/test.rb b/lib/openapi_first/test.rb index 75df2b5f..0a7cd5ea 100644 --- a/lib/openapi_first/test.rb +++ b/lib/openapi_first/test.rb @@ -36,12 +36,8 @@ def self.configuration # Sets up OpenAPI test coverage and OAD registration. # @yieldparam [OpenapiFirst::Test::Configuration] configuration A configuration to setup test integration def self.setup - unless block_given? - raise ArgumentError, "Please provide a block to #{self.class}.confgure to register you API descriptions" - end - install - yield configuration + yield configuration if block_given? Coverage.start(skip_response: configuration.skip_response_coverage, skip_route: configuration.skip_coverage) diff --git a/lib/openapi_first/test/configuration.rb b/lib/openapi_first/test/configuration.rb index 16d973b4..0513b62e 100644 --- a/lib/openapi_first/test/configuration.rb +++ b/lib/openapi_first/test/configuration.rb @@ -11,7 +11,7 @@ def initialize @skip_response_coverage = nil @skip_coverage = nil @response_raise_error = true - @ignored_unknown_status = [404] + @ignored_unknown_status = Set.new([401, 404, 500]) @ignore_unknown_response_status = false @report_coverage = true @ignore_unknown_requests = false @@ -33,6 +33,12 @@ def observe(app, api: :default) :ignore_unknown_requests, :ignore_unknown_response_status, :minimum_coverage attr_reader :report_coverage, :ignored_unknown_status + # Set ignored unknown status codes. + # @param [Array] status Status codes that are okay not to cover in an OAD + def ignored_unknown_status=(status) + @ignored_unknown_status = status.to_set + end + # Configure report coverage # @param [Boolean, :warn] value Whether to report coverage or just warn. def report_coverage=(value) diff --git a/lib/openapi_first/version.rb b/lib/openapi_first/version.rb index 0db7d113..6269c1d0 100644 --- a/lib/openapi_first/version.rb +++ b/lib/openapi_first/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true module OpenapiFirst - VERSION = '2.11.1' + VERSION = '3.0.0' end diff --git a/spec/configuration_spec.rb b/spec/configuration_spec.rb index 20d7d90e..2998786b 100644 --- a/spec/configuration_spec.rb +++ b/spec/configuration_spec.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true RSpec.describe OpenapiFirst::Configuration do + after { Warning[:deprecated] = true } + describe '#after_...' do it 'adds a hook' do config = OpenapiFirst::Configuration.new @@ -28,6 +30,7 @@ end it 'can be set to true' do + Warning[:deprecated] = false config = OpenapiFirst::Configuration.new config.request_validation_raise_error = true expect(config.request_validation_raise_error).to be(true) @@ -40,6 +43,7 @@ end it 'can be set to false' do + Warning[:deprecated] = false config = OpenapiFirst::Configuration.new config.response_validation_raise_error = false expect(config.response_validation_raise_error).to be(false) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2ae5e623..6024f8f0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,6 +2,7 @@ ENV['RACK_ENV'] = 'test' +Warning[:deprecated] = true require 'bundler/setup' require 'openapi_first' require 'json' diff --git a/spec/test/configuration_spec.rb b/spec/test/configuration_spec.rb index 09e7af14..185b78cc 100644 --- a/spec/test/configuration_spec.rb +++ b/spec/test/configuration_spec.rb @@ -11,12 +11,12 @@ describe 'ignored_unknown_status' do it 'has a default value' do - expect(configuration.ignored_unknown_status).to eq [404] + expect(configuration.ignored_unknown_status).to contain_exactly(401, 404, 500) end it 'can be extended' do configuration.ignored_unknown_status << 401 - expect(configuration.ignored_unknown_status).to eq [404, 401] + expect(configuration.ignored_unknown_status).to include(401) end end diff --git a/spec/test_spec.rb b/spec/test_spec.rb index 708985fd..c2adbaee 100644 --- a/spec/test_spec.rb +++ b/spec/test_spec.rb @@ -129,10 +129,9 @@ def call(_env) expect(route_tasks.map { |route| [route.path, route.request_method] }).to eq([['/pets/{petId}', 'GET']]) end - it 'raises an error if no block is given' do - expect do - described_class.setup - end.to raise_error ArgumentError + it 'is okay if no block is given if an OAD is registered ' do + OpenapiFirst.register('./spec/data/dice.yaml', as: :dice) + expect(described_class.setup) end it 'returns the globally registered OADs if nothing was registered inside the block' do @@ -615,6 +614,18 @@ def call(_env) end end + context 'with ignored_unknown_status =' do + before(:each) do + described_class.configuration.ignored_unknown_status = [302] + end + + it 'does not raise an error' do + expect do + app.call(Rack::MockRequest.env_for('/roll', method: 'POST')) + end.not_to raise_error + end + end + context 'with ignore_unknown_response_status = true' do before(:each) do described_class.configuration.ignore_unknown_response_status = true