Commit c443133e authored by Bob Van Landuyt's avatar Bob Van Landuyt

Handle exceptions outside the GraphQL schema

This allows us to report JSON parse exceptions to clients and ignore
them in sentry.
parent aa4b1ae7
...@@ -5,7 +5,7 @@ class GraphqlController < ApplicationController ...@@ -5,7 +5,7 @@ class GraphqlController < ApplicationController
before_action :check_graphql_feature_flag! before_action :check_graphql_feature_flag!
def execute def execute
variables = ensure_hash(params[:variables]) variables = Gitlab::Graphql::Variables.new(params[:variables]).to_h
query = params[:query] query = params[:query]
operation_name = params[:operationName] operation_name = params[:operationName]
context = { context = {
...@@ -15,35 +15,31 @@ class GraphqlController < ApplicationController ...@@ -15,35 +15,31 @@ class GraphqlController < ApplicationController
render json: result render json: result
end 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 private
# Overridden from the ApplicationController to make the response look like # Overridden from the ApplicationController to make the response look like
# a GraphQL response. That is nicely picked up in Graphiql. # a GraphQL response. That is nicely picked up in Graphiql.
def render_404 def render_404
error = { errors: [ message: "Not found" ] } render_error("Not found!", status: :not_found)
end
def render_error(message, status: 500)
error = { errors: [message: message] }
render json: error, status: :not_found render json: error, status: status
end end
def check_graphql_feature_flag! def check_graphql_feature_flag!
render_404 unless Feature.enabled?(:graphql) render_404 unless Feature.enabled?(:graphql)
end 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 end
module Gitlab
module Graphql
StandardGraphqlError = Class.new(StandardError)
end
end
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
...@@ -2,6 +2,8 @@ require 'spec_helper' ...@@ -2,6 +2,8 @@ require 'spec_helper'
describe GraphqlController do describe GraphqlController do
describe 'execute' do describe 'execute' do
let(:user) { nil }
before do before do
sign_in(user) if user sign_in(user) if user
...@@ -39,17 +41,26 @@ describe GraphqlController do ...@@ -39,17 +41,26 @@ describe GraphqlController do
is_expected.to eq('echo' => '"Simon" says: test success') is_expected.to eq('echo' => '"Simon" says: test success')
end end
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 end
# Chosen to exercise all the moving parts in GraphqlController#execute # 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 = <<~QUERY
query Echo($text: String) { query Echo($text: String) {
echo(text: $text) echo(text: $text)
} }
QUERY QUERY
post :execute, query: query, operationName: 'Echo', variables: { 'text' => 'test success' } post :execute, query: query, operationName: 'Echo', variables: variables
end end
def query_response def query_response
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment