class GraphQL::Language::SanitizedPrinter
@see {Query#sanitized_query_string}
puts printer.sanitized_query_string
printer = QueryPrinter.new(query)
@example Printing a scrubbed string
and input type while printing the query.
on the type of fields or arguments, we have to track the current object, field
Since the GraphQL Ruby AST for a GraphQL query doesnt contain any reference
The printer returns ‘nil` if the query is invalid.
within the query for facilitate logging and analysis of queries.
A custom printer used to print sanitized queries. It inlines provided variables
def coerce_argument_value_to_list?(type, value)
def coerce_argument_value_to_list?(type, value) type.list? && !value.is_a?(Array) && !value.nil? && !value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) end
def initialize(query, inline_variables: true)
def initialize(query, inline_variables: true) @query = query @current_type = nil @current_field = nil @current_input_type = nil @inline_variables = inline_variables end
def print_argument(argument)
def print_argument(argument) # We won't have type information if we're recursing into a custom scalar return super if @current_input_type && @current_input_type.kind.scalar? arg_owner = @current_input_type || @current_directive || @current_field old_current_argument = @current_argument @current_argument = arg_owner.arguments[argument.name] old_input_type = @current_input_type @current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type argument_value = if coerce_argument_value_to_list?(@current_input_type, argument.value) [argument.value] else argument.value end res = "#{argument.name}: #{print_node(argument_value)}".dup @current_input_type = old_input_type @current_argument = old_current_argument res end
def print_directive(directive)
def print_directive(directive) @current_directive = query.schema.directives[directive.name] res = super @current_directive = nil res end
def print_field(field, indent: "")
def print_field(field, indent: "") @current_field = query.schema.get_field(@current_type, field.name) old_type = @current_type @current_type = @current_field.type.unwrap res = super @current_type = old_type res end
def print_fragment_definition(fragment_def, indent: "")
def print_fragment_definition(fragment_def, indent: "") old_type = @current_type @current_type = query.schema.types[fragment_def.type.name] res = super @current_type = old_type res end
def print_inline_fragment(inline_fragment, indent: "")
def print_inline_fragment(inline_fragment, indent: "") old_type = @current_type if inline_fragment.type @current_type = query.schema.types[inline_fragment.type.name] end res = super @current_type = old_type res end
def print_node(node, indent: "")
def print_node(node, indent: "") case node when FalseClass, Float, Integer, String, TrueClass if @current_argument && redact_argument_value?(@current_argument, node) redacted_argument_value(@current_argument) else super end when Array old_input_type = @current_input_type if @current_input_type && @current_input_type.list? @current_input_type = @current_input_type.of_type @current_input_type = @current_input_type.of_type if @current_input_type.non_null? end res = super @current_input_type = old_input_type res else super end end
def print_operation_definition(operation_definition, indent: "")
Print the operation definition but do not include the variable
def print_operation_definition(operation_definition, indent: "") old_type = @current_type @current_type = query.schema.public_send(operation_definition.operation_type) if @inline_variables out = "#{indent}#{operation_definition.operation_type}".dup out << " #{operation_definition.name}" if operation_definition.name out << print_directives(operation_definition.directives) out << print_selections(operation_definition.selections, indent: indent) else out = super end @current_type = old_type out end
def print_variable_identifier(variable_id)
def print_variable_identifier(variable_id) if @inline_variables variable_value = query.variables[variable_id.name] print_node(value_to_ast(variable_value, @current_input_type)) else super end end
def redact_argument_value?(argument, value)
Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings
def redact_argument_value?(argument, value) # Default to redacting any strings or custom scalars encoded as strings type = argument.type.unwrap value.is_a?(String) && type.kind.scalar? && (type.graphql_name == "String" || !type.default_scalar?) end
def redacted_argument_value(argument)
Returns the value to use for redacted versions of the given argument. Defaults to the
def redacted_argument_value(argument) REDACTED end
def sanitized_query_string
-
(String, nil)
- A scrubbed query string, if the query was valid.
def sanitized_query_string if query.valid? print(query.document) else nil end end
def value_to_ast(value, type)
def value_to_ast(value, type) type = type.of_type if type.non_null? if value.nil? return GraphQL::Language::Nodes::NullValue.new(name: "null") end case type.kind.name when "INPUT_OBJECT" value = if value.respond_to?(:to_unsafe_h) # for ActionController::Parameters value.to_unsafe_h else value.to_h end arguments = value.map do |key, val| sub_type = type.arguments[key.to_s].type GraphQL::Language::Nodes::Argument.new( name: key.to_s, value: value_to_ast(val, sub_type) ) end GraphQL::Language::Nodes::InputObject.new( arguments: arguments ) when "LIST" if value.is_a?(Array) value.map { |v| value_to_ast(v, type.of_type) } else [value].map { |v| value_to_ast(v, type.of_type) } end when "ENUM" GraphQL::Language::Nodes::Enum.new(name: value) else value end end