feat(graphql): improuve logs

This commit is contained in:
Paul Chavard 2023-04-04 14:45:22 +02:00
parent 5ca0ed18a4
commit a5733002ed
6 changed files with 37 additions and 39 deletions

View file

@ -1,9 +1,7 @@
class API::V2::GraphqlController < API::V2::BaseController class API::V2::GraphqlController < API::V2::BaseController
def execute def execute
result = API::V2::Schema.execute(query, result = API::V2::Schema.execute(query:, variables:, context:, operation_name:)
variables: variables, @query_info = result.context.query_info
context: context,
operation_name: params[:operationName])
render json: result render json: result
rescue GraphQL::ParseError, JSON::ParserError => exception rescue GraphQL::ParseError, JSON::ParserError => exception
@ -27,10 +25,7 @@ class API::V2::GraphqlController < API::V2::BaseController
super super
payload.merge!({ payload.merge!(@query_info.presence || {})
graphql_query: query(fallback: params[:queryId]),
graphql_variables: to_unsafe_hash(params[:variables]).to_json
})
end end
def process_action(*args) def process_action(*args)
@ -44,9 +39,9 @@ class API::V2::GraphqlController < API::V2::BaseController
}, status: 400 }, status: 400
end end
def query(fallback: nil) def query
if params[:queryId].present? if params[:queryId].present?
API::V2::StoredQuery.get(params[:queryId], fallback: fallback) API::V2::StoredQuery.get(params[:queryId])
else else
params[:query] params[:query]
end end
@ -56,6 +51,10 @@ class API::V2::GraphqlController < API::V2::BaseController
ensure_hash(params[:variables]) ensure_hash(params[:variables])
end end
def operation_name
params[:operationName]
end
# Handle form data, JSON body, or a blank value # Handle form data, JSON body, or a blank value
def ensure_hash(ambiguous_param) def ensure_hash(ambiguous_param)
case ambiguous_param case ambiguous_param
@ -76,23 +75,6 @@ class API::V2::GraphqlController < API::V2::BaseController
end end
end end
def to_unsafe_hash(ambiguous_param)
case ambiguous_param
when String
if ambiguous_param.present?
JSON.parse(ambiguous_param)
else
{}
end
when ActionController::Parameters
ambiguous_param.to_unsafe_h
else
ambiguous_param
end
rescue JSON::ParserError
{}
end
def handle_parse_error(exception) def handle_parse_error(exception)
render json: { render json: {
errors: [ errors: [
@ -115,15 +97,15 @@ class API::V2::GraphqlController < API::V2::BaseController
end end
def handle_error_in_production(exception) def handle_error_in_production(exception)
id = SecureRandom.uuid extra = { exception_id: SecureRandom.uuid }
Sentry.capture_exception(exception, extra: { exception_id: id }) Sentry.capture_exception(exception, extra:)
render json: { render json: {
errors: [ errors: [
{ {
message: "Internal Server Error", message: "Internal Server Error",
extensions: { extensions: {
exception: { id: id } exception: { id: extra[:exception_id] }
} }
} }
], ],

View file

@ -55,6 +55,15 @@ class API::V2::Context < GraphQL::Query::Context
self[:authorized][demarche.id] self[:authorized][demarche.id]
end end
def query_info
{
graphql_query: query.query_string,
graphql_variables: query.provided_variables&.to_json,
graphql_null_error: errors.any? { _1.is_a? GraphQL::InvalidNullError }.presence,
graphql_timeout_error: errors.any? { _1.is_a? GraphQL::Schema::Timeout::TimeoutError }.presence
}.compact
end
private private
def compute_demarche_authorization(demarche) def compute_demarche_authorization(demarche)

View file

@ -125,14 +125,13 @@ class API::V2::Schema < GraphQL::Schema
def self.type_error(error, ctx) def self.type_error(error, ctx)
# Capture type errors in Sentry. Thouse errors are our responsability and usually linked to # Capture type errors in Sentry. Thouse errors are our responsability and usually linked to
# instances of "bad data". # instances of "bad data".
Sentry.capture_exception(error) Sentry.capture_exception(error, extra: ctx.query_info)
super super
end end
class Timeout < GraphQL::Schema::Timeout class Timeout < GraphQL::Schema::Timeout
def handle_timeout(error, query) def handle_timeout(error, query)
extra = { query: query.query_string, variables: query.provided_variables&.to_json } Sentry.capture_exception(error, extra: query.context.query_info)
Sentry.capture_exception(error, extra:)
end end
end end

View file

@ -1,5 +1,5 @@
class API::V2::StoredQuery class API::V2::StoredQuery
def self.get(query_id, fallback: nil) def self.get(query_id)
case query_id case query_id
when 'ds-query-v2' when 'ds-query-v2'
QUERY_V2 QUERY_V2
@ -8,11 +8,7 @@ class API::V2::StoredQuery
when 'introspection' when 'introspection'
GraphQL::Introspection::INTROSPECTION_QUERY GraphQL::Introspection::INTROSPECTION_QUERY
else else
if fallback.nil?
raise GraphQL::ExecutionError.new("No query with id \"#{query_id}\"") raise GraphQL::ExecutionError.new("No query with id \"#{query_id}\"")
else
fallback
end
end end
end end

View file

@ -16,6 +16,8 @@ Rails.application.configure do
user_agent: event.payload[:user_agent], user_agent: event.payload[:user_agent],
graphql_query: event.payload[:graphql_query], graphql_query: event.payload[:graphql_query],
graphql_variables: event.payload[:graphql_variables], graphql_variables: event.payload[:graphql_variables],
graphql_null_error: event.payload[:graphql_null_error],
graphql_timeout_error: event.payload[:graphql_timeout_error],
browser: event.payload[:browser], browser: event.payload[:browser],
browser_version: event.payload[:browser_version], browser_version: event.payload[:browser_version],
platform: event.payload[:platform], platform: event.payload[:platform],

View file

@ -684,6 +684,16 @@ describe API::V2::GraphqlController do
}) })
end end
end end
context "error in degraded mode" do
before do
dossier.etablissement.update!(adresse: nil, siege_social: nil)
end
it "should return an error" do
expect(gql_errors).to eq([{ message: "Cannot return null for non-nullable field PersonneMorale.siegeSocial" }])
end
end
end end
context "champs" do context "champs" do