fix(graphql): fix and improuve query parsing for logs
This commit is contained in:
parent
71c54a226d
commit
758e7d68e6
3 changed files with 124 additions and 34 deletions
|
@ -1,4 +1,6 @@
|
|||
class API::V2::GraphqlController < API::V2::BaseController
|
||||
include GraphqlOperationLogConcern
|
||||
|
||||
def execute
|
||||
variables = ensure_hash(params[:variables])
|
||||
|
||||
|
@ -24,43 +26,10 @@ class API::V2::GraphqlController < API::V2::BaseController
|
|||
super
|
||||
|
||||
payload.merge!({
|
||||
graphql_operation: operation_log(params[:query], params[:operationName], params[:variables])
|
||||
graphql_operation: operation_log(params[:query], params[:operationName], params[:variables]&.to_unsafe_h)
|
||||
})
|
||||
end
|
||||
|
||||
def operation_log(query, operation_name, variables)
|
||||
return "NoQuery" if query.nil?
|
||||
|
||||
operation = GraphQL.parse(query).children.find do |node|
|
||||
if node.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
||||
node.name == operation_name
|
||||
end
|
||||
end
|
||||
|
||||
return "InvalidQuery" if operation.nil?
|
||||
return "IntrospectionQuery" if operation.name == "IntrospectionQuery"
|
||||
|
||||
message = operation.operation_type
|
||||
if operation.name
|
||||
message += ": #{operation.name} { "
|
||||
end
|
||||
message += operation.selections.map(&:name).join(', ')
|
||||
message += " }"
|
||||
if variables.present?
|
||||
message += " "
|
||||
message += variables.to_unsafe_h.flat_map do |(name, value)|
|
||||
if name == "input"
|
||||
value.map do |(name, value)|
|
||||
"#{name}: \"#{value.to_s.truncate(10)}\""
|
||||
end
|
||||
else
|
||||
"#{name}: \"#{value.to_s.truncate(10)}\""
|
||||
end
|
||||
end.join(', ')
|
||||
end
|
||||
message
|
||||
end
|
||||
|
||||
def process_action(*args)
|
||||
super
|
||||
rescue ActionDispatch::Http::Parameters::ParseError => exception
|
||||
|
|
62
app/controllers/concerns/graphql_operation_log_concern.rb
Normal file
62
app/controllers/concerns/graphql_operation_log_concern.rb
Normal file
|
@ -0,0 +1,62 @@
|
|||
module GraphqlOperationLogConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
# This method parses GraphQL query and creates a short description of the query. It is useful for logging.
|
||||
def operation_log(query, operation_name, variables)
|
||||
return "NoQuery" if query.nil?
|
||||
|
||||
operation = parse_graphql_query(query, operation_name)
|
||||
|
||||
return "InvalidQuery" if operation.nil?
|
||||
return "IntrospectionQuery" if operation.name == "IntrospectionQuery"
|
||||
|
||||
message = "#{operation.operation_type}: "
|
||||
message += if operation.name.present?
|
||||
"#{operation.name} { "
|
||||
else
|
||||
"{ "
|
||||
end
|
||||
message += operation.selections.map(&:name).join(', ')
|
||||
message += " } "
|
||||
message += if variables.present?
|
||||
variables.flat_map do |(name, value)|
|
||||
format_graphql_variable(name, value)
|
||||
end
|
||||
else
|
||||
operation.selections.flat_map(&:arguments).flat_map do |argument|
|
||||
format_graphql_variable(argument.name, argument.value)
|
||||
end
|
||||
end.join(', ')
|
||||
|
||||
message.strip
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_graphql_query(query, operation_name)
|
||||
operations = GraphQL.parse(query).children.filter do |node|
|
||||
node.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
||||
end
|
||||
if operations.size == 1
|
||||
operations.first
|
||||
else
|
||||
operations.find { |node| node.name == operation_name }
|
||||
end
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def format_graphql_variable(name, value)
|
||||
if value.is_a?(Hash)
|
||||
value.map do |(name, value)|
|
||||
format_graphql_variable(name, value)
|
||||
end
|
||||
elsif value.is_a?(GraphQL::Language::Nodes::InputObject)
|
||||
value.arguments.map do |argument|
|
||||
format_graphql_variable(argument.name, argument.value)
|
||||
end
|
||||
else
|
||||
"#{name}: \"#{value.to_s.truncate(10)}\""
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,59 @@
|
|||
RSpec.describe GraphqlOperationLogConcern, type: :controller do
|
||||
class TestController < ActionController::Base
|
||||
include GraphqlOperationLogConcern
|
||||
end
|
||||
|
||||
controller TestController do
|
||||
end
|
||||
|
||||
describe '#operation_log' do
|
||||
let(:query) { nil }
|
||||
let(:variables) { nil }
|
||||
let(:operation_name) { nil }
|
||||
|
||||
subject { controller.operation_log(query, operation_name, variables) }
|
||||
|
||||
context 'with no query' do
|
||||
it { expect(subject).to eq('NoQuery') }
|
||||
end
|
||||
|
||||
context 'with invalid query' do
|
||||
let(:query) { 'query { demarche {} }' }
|
||||
|
||||
it { expect(subject).to eq('InvalidQuery') }
|
||||
end
|
||||
|
||||
context 'with two queries' do
|
||||
let(:query) { 'query demarche { demarche } query dossier { dossier }' }
|
||||
let(:operation_name) { 'dossier' }
|
||||
|
||||
it { expect(subject).to eq('query: dossier { dossier }') }
|
||||
end
|
||||
|
||||
context 'with arguments' do
|
||||
let(:query) { 'query demarche { demarche(number: 123) { id } }' }
|
||||
|
||||
it { expect(subject).to eq('query: demarche { demarche } number: "123"') }
|
||||
end
|
||||
|
||||
context 'with variables' do
|
||||
let(:query) { 'query { demarche(number: 123) { id } }' }
|
||||
let(:variables) { { number: 124 } }
|
||||
|
||||
it { expect(subject).to eq('query: { demarche } number: "124"') }
|
||||
end
|
||||
|
||||
context 'with mutation and arguments' do
|
||||
let(:query) { 'mutation { passerDossierEnInstruction(input: { number: 123 }) { id } }' }
|
||||
|
||||
it { expect(subject).to eq('mutation: { passerDossierEnInstruction } number: "123"') }
|
||||
end
|
||||
|
||||
context 'with mutation and variables' do
|
||||
let(:query) { 'mutation { passerDossierEnInstruction(input: { number: 123 }) { id } }' }
|
||||
let(:variables) { { input: { number: 124 } } }
|
||||
|
||||
it { expect(subject).to eq('mutation: { passerDossierEnInstruction } number: "124"') }
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue