Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Api::ErrorHandling concern for api/base controller #29574

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 1 addition & 45 deletions app/controllers/api/base_controller.rb
Expand Up @@ -8,6 +8,7 @@ class Api::BaseController < ApplicationController
include Api::AccessTokenTrackingConcern
include Api::CachingConcern
include Api::ContentSecurityPolicy
include Api::ErrorHandling

skip_before_action :require_functional!, unless: :limited_federation_mode?

Expand All @@ -18,51 +19,6 @@ class Api::BaseController < ApplicationController

protect_from_forgery with: :null_session

rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422
end

rescue_from ActiveRecord::RecordNotUnique do
render json: { error: 'Duplicate record' }, status: 422
end

rescue_from Date::Error do
render json: { error: 'Invalid date supplied' }, status: 422
end

rescue_from ActiveRecord::RecordNotFound do
render json: { error: 'Record not found' }, status: 404
end

rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
render json: { error: 'Remote data could not be fetched' }, status: 503
end

rescue_from OpenSSL::SSL::SSLError do
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
end

rescue_from Mastodon::NotPermittedError do
render json: { error: 'This action is not allowed' }, status: 403
end

rescue_from Seahorse::Client::NetworkingError do |e|
Rails.logger.warn "Storage server error: #{e}"
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end

rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight do
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end

rescue_from Mastodon::RateLimitExceededError do
render json: { error: I18n.t('errors.429') }, status: 429
end

rescue_from ActionController::ParameterMissing, Mastodon::InvalidParameterError do |e|
render json: { error: e.to_s }, status: 400
end

def doorkeeper_unauthorized_render_options(error: nil)
{ json: { error: error.try(:description) || 'Not authorized' } }
end
Expand Down
52 changes: 52 additions & 0 deletions app/controllers/concerns/api/error_handling.rb
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module Api::ErrorHandling
extend ActiveSupport::Concern

included do
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
render json: { error: e.to_s }, status: 422
end

rescue_from ActiveRecord::RecordNotUnique do
render json: { error: 'Duplicate record' }, status: 422
end

rescue_from Date::Error do
render json: { error: 'Invalid date supplied' }, status: 422
end

rescue_from ActiveRecord::RecordNotFound do
render json: { error: 'Record not found' }, status: 404
end

rescue_from HTTP::Error, Mastodon::UnexpectedResponseError do
render json: { error: 'Remote data could not be fetched' }, status: 503
end

rescue_from OpenSSL::SSL::SSLError do
render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
end

rescue_from Mastodon::NotPermittedError do
render json: { error: 'This action is not allowed' }, status: 403
end

rescue_from Seahorse::Client::NetworkingError do |e|
Rails.logger.warn "Storage server error: #{e}"
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end

rescue_from Mastodon::RaceConditionError, Stoplight::Error::RedLight do
render json: { error: 'There was a temporary problem serving your request, please try again' }, status: 503
end

rescue_from Mastodon::RateLimitExceededError do
render json: { error: I18n.t('errors.429') }, status: 429
end

rescue_from ActionController::ParameterMissing, Mastodon::InvalidParameterError do |e|
render json: { error: e.to_s }, status: 400
end
end
end
36 changes: 0 additions & 36 deletions spec/controllers/api/base_controller_spec.rb
Expand Up @@ -3,10 +3,6 @@
require 'rails_helper'

describe Api::BaseController do
before do
stub_const('FakeService', Class.new)
end

controller do
def success
head 200
Expand Down Expand Up @@ -72,36 +68,4 @@ def failure
expect(response).to have_http_status(403)
end
end

describe 'error handling' do
before do
routes.draw { get 'failure' => 'api/base#failure' }
end

{
ActiveRecord::RecordInvalid => 422,
ActiveRecord::RecordNotFound => 404,
ActiveRecord::RecordNotUnique => 422,
Date::Error => 422,
HTTP::Error => 503,
Mastodon::InvalidParameterError => 400,
Mastodon::NotPermittedError => 403,
Mastodon::RaceConditionError => 503,
Mastodon::RateLimitExceededError => 429,
Mastodon::UnexpectedResponseError => 503,
Mastodon::ValidationError => 422,
OpenSSL::SSL::SSLError => 503,
Seahorse::Client::NetworkingError => 503,
Stoplight::Error::RedLight => 503,
}.each do |error, code|
it "Handles error class of #{error}" do
allow(FakeService).to receive(:new).and_raise(error)

get :failure

expect(response).to have_http_status(code)
expect(FakeService).to have_received(:new)
end
end
end
end
51 changes: 51 additions & 0 deletions spec/controllers/concerns/api/error_handling_spec.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

require 'rails_helper'

describe Api::ErrorHandling do
before do
stub_const('FakeService', Class.new)
end

controller(Api::BaseController) do
def failure
FakeService.new
end
end

describe 'error handling' do
before do
routes.draw { get 'failure' => 'api/base#failure' }
end

{
ActiveRecord::RecordInvalid => 422,
ActiveRecord::RecordNotFound => 404,
ActiveRecord::RecordNotUnique => 422,
Date::Error => 422,
HTTP::Error => 503,
Mastodon::InvalidParameterError => 400,
Mastodon::NotPermittedError => 403,
Mastodon::RaceConditionError => 503,
Mastodon::RateLimitExceededError => 429,
Mastodon::UnexpectedResponseError => 503,
Mastodon::ValidationError => 422,
OpenSSL::SSL::SSLError => 503,
Seahorse::Client::NetworkingError => 503,
Stoplight::Error::RedLight => 503,
}.each do |error, code|
it "Handles error class of #{error}" do
allow(FakeService)
.to receive(:new)
.and_raise(error)

get :failure

expect(response)
.to have_http_status(code)
expect(FakeService)
.to have_received(:new)
end
end
end
end