diff --git a/CHANGELOG.md b/CHANGELOG.md index a1d47b76..692e115b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Request validation middleware now accepts `error_response: false` do disable rendering a response. This is useful if you just want to collect metrics (via hooks) during a migration phase. + ## 2.0.2 - Fix setting custom error response (thanks @gobijan) diff --git a/README.md b/README.md index 87764772..e7f854c3 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ use OpenapiFirst::Middlewares::RequestValidation, spec: 'openapi.yaml' | :---------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | | `spec:` | | The path to the spec file or spec loaded via `OpenapiFirst.load` | | `raise_error:` | `false` (default), `true` | If set to true the middleware raises `OpenapiFirst::RequestInvalidError` or `OpenapiFirst::NotFoundError` instead of returning 4xx. | -| `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` | +| `error_response:` | `:default` (default), `:jsonapi`, Your implementation of `ErrorResponse` or `false` to disable responding | #### Error responses diff --git a/lib/openapi_first/middlewares/request_validation.rb b/lib/openapi_first/middlewares/request_validation.rb index 73fe933c..eddf44f2 100644 --- a/lib/openapi_first/middlewares/request_validation.rb +++ b/lib/openapi_first/middlewares/request_validation.rb @@ -10,11 +10,13 @@ class RequestValidation # :raise_error A Boolean indicating whether to raise an error if validation fails. # default: false # :error_response The Class to use for error responses. + # This can be a Symbol-name of an registered error response (:default, :jsonapi) + # or it can be set to false to disable returning a response. # default: OpenapiFirst::Plugins::Default::ErrorResponse (Config.default_options.error_response) def initialize(app, options = {}) @app = app @raise = options.fetch(:raise_error, OpenapiFirst.configuration.request_validation_raise_error) - @error_response_class = error_response(options[:error_response]) + @error_response_class = error_response_option(options[:error_response]) spec = options.fetch(:spec) raise "You have to pass spec: when initializing #{self.class}" unless spec @@ -29,17 +31,18 @@ def call(env) validated = @definition.validate_request(Rack::Request.new(env), raise_error: @raise) env[REQUEST] = validated failure = validated.error - return @error_response_class.new(failure:).render if failure + return @error_response_class.new(failure:).render if failure && @error_response_class @app.call(env) end private - def error_response(mod) - return OpenapiFirst.find_error_response(mod) if mod.is_a?(Symbol) + def error_response_option(value) + return if value == false + return OpenapiFirst.find_error_response(value) if value.is_a?(Symbol) - mod || OpenapiFirst.configuration.request_validation_error_response + value || OpenapiFirst.configuration.request_validation_error_response end end end diff --git a/spec/middlewares/request_validation_spec.rb b/spec/middlewares/request_validation_spec.rb index db480e1a..8b105b42 100644 --- a/spec/middlewares/request_validation_spec.rb +++ b/spec/middlewares/request_validation_spec.rb @@ -75,7 +75,25 @@ end end - context 'with custom error_response option' do + context 'with error_response: :default' do + let(:app) do + Rack::Builder.app do + use OpenapiFirst::Middlewares::RequestValidation, spec: './spec/data/request-body-validation.yaml', + error_response: :default + run lambda { |_env| + Rack::Response.new('hello', 200).finish + } + end + end + + it 'returns 400' do + header 'Content-Type', 'application/json' + post '/pets' + expect(last_response.status).to eq 400 + end + end + + context 'with error_response: MyCustomClass' do let(:app) do custom_class = Class.new do include OpenapiFirst::ErrorResponse @@ -101,21 +119,48 @@ def status = 409 end end - context 'with :default error_response option' do + context 'with error_response: false' do + let(:called) { [] } + + let(:app) do + spec = OpenapiFirst.load('./spec/data/request-body-validation.yaml') do |config| + config.after_request_validation do |validated_request| + called << { valid: validated_request.valid? } + end + end + Rack::Builder.app do + use OpenapiFirst::Middlewares::RequestValidation, spec:, + error_response: false + run lambda { |_env| + Rack::Response.new('hello', 200).finish + } + end + end + + it 'validates the response, but calls the application' do + header 'Content-Type', 'application/json' + post '/pets' + expect(last_response.status).to eq 200 + expect(called).to eq [{ valid: false }] + end + end + + context 'with error_response: nil' do let(:app) do Rack::Builder.app do use OpenapiFirst::Middlewares::RequestValidation, spec: './spec/data/request-body-validation.yaml', - error_response: :default + error_response: nil run lambda { |_env| Rack::Response.new('hello', 200).finish } end end - it 'returns 400' do + it 'uses the default error reponse class' do header 'Content-Type', 'application/json' post '/pets' expect(last_response.status).to eq 400 + expect(last_response.content_type).to eq 'application/problem+json' end end