Handle exceptions outside the GraphQL schema

This allows us to report JSON parse exceptions to clients and ignore
them in sentry.
This commit is contained in:
Bob Van Landuyt 2018-05-21 13:42:07 +02:00
parent aa4b1ae712
commit c443133e77
4 changed files with 72 additions and 23 deletions

View File

@ -5,7 +5,7 @@ class GraphqlController < ApplicationController
before_action :check_graphql_feature_flag!
def execute
variables = ensure_hash(params[:variables])
variables = Gitlab::Graphql::Variables.new(params[:variables]).to_h
query = params[:query]
operation_name = params[:operationName]
context = {
@ -15,35 +15,31 @@ class GraphqlController < ApplicationController
render json: result
end
rescue_from StandardError do |exception|
log_exception(exception)
render_error("Internal server error")
end
rescue_from Gitlab::Graphql::Variables::Invalid do |exception|
render_error(exception.message, status: :unprocessable_entity)
end
private
# Overridden from the ApplicationController to make the response look like
# a GraphQL response. That is nicely picked up in Graphiql.
def render_404
error = { errors: [ message: "Not found" ] }
render_error("Not found!", status: :not_found)
end
render json: error, status: :not_found
def render_error(message, status: 500)
error = { errors: [message: message] }
render json: error, status: status
end
def check_graphql_feature_flag!
render_404 unless Feature.enabled?(:graphql)
end
# Handle form data, JSON body, or a blank value
def ensure_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
ensure_hash(JSON.parse(ambiguous_param))
else
{}
end
when Hash, ActionController::Parameters
ambiguous_param
when nil
{}
else
raise ArgumentError, "Unexpected parameter: #{ambiguous_param}"
end
end
end

5
lib/gitlab/graphql.rb Normal file
View File

@ -0,0 +1,5 @@
module Gitlab
module Graphql
StandardGraphqlError = Class.new(StandardError)
end
end

View File

@ -0,0 +1,37 @@
module Gitlab
module Graphql
class Variables
Invalid = Class.new(Gitlab::Graphql::StandardGraphqlError)
def initialize(param)
@param = param
end
def to_h
ensure_hash(@param)
end
private
# Handle form data, JSON body, or a blank value
def ensure_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
ensure_hash(JSON.parse(ambiguous_param))
else
{}
end
when Hash, ActionController::Parameters
ambiguous_param
when nil
{}
else
raise Invalid, "Unexpected parameter: #{ambiguous_param}"
end
rescue JSON::ParserError => e
raise Invalid.new(e)
end
end
end
end

View File

@ -2,6 +2,8 @@ require 'spec_helper'
describe GraphqlController do
describe 'execute' do
let(:user) { nil }
before do
sign_in(user) if user
@ -39,17 +41,26 @@ describe GraphqlController do
is_expected.to eq('echo' => '"Simon" says: test success')
end
end
context 'invalid variables' do
it 'returns an error' do
run_test_query!(variables: "This is not JSON")
expect(response).to have_gitlab_http_status(422)
expect(json_response['errors'].first['message']).not_to be_nil
end
end
end
# Chosen to exercise all the moving parts in GraphqlController#execute
def run_test_query!
def run_test_query!(variables: { 'text' => 'test success' })
query = <<~QUERY
query Echo($text: String) {
echo(text: $text)
}
QUERY
post :execute, query: query, operationName: 'Echo', variables: { 'text' => 'test success' }
post :execute, query: query, operationName: 'Echo', variables: variables
end
def query_response