class GraphQL::Language::SanitizedPrinter
A custom printer used to print sanitized queries. It inlines provided variables within the query for facilitate logging and analysis of queries.
The printer returns `nil` if the query is invalid.
Since the GraphQL
Ruby AST for a GraphQL
query doesnt contain any reference on the type of fields or arguments, we have to track the current object, field and input type while printing the query.
@example Printing a scrubbed string
printer = QueryPrinter.new(query) puts printer.sanitized_query_string
@see {Query#sanitized_query_string}
Constants
- REDACTED
Attributes
Public Class Methods
# File lib/graphql/language/sanitized_printer.rb, line 22 def initialize(query, inline_variables: true) @query = query @current_type = nil @current_field = nil @current_input_type = nil @inline_variables = inline_variables end
Public Instance Methods
# File lib/graphql/language/sanitized_printer.rb, line 99 def coerce_argument_value_to_list?(type, value) type.list? && !value.is_a?(Array) && !value.nil? && !value.is_a?(GraphQL::Language::Nodes::VariableIdentifier) end
GraphQL::Language::Printer#print_argument
# File lib/graphql/language/sanitized_printer.rb, line 76 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.get_argument(argument.name, @query.context) 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
GraphQL::Language::Printer#print_directive
# File lib/graphql/language/sanitized_printer.rb, line 149 def print_directive(directive) @current_directive = query.schema.directives[directive.name] res = super @current_directive = nil res end
GraphQL::Language::Printer#print_field
# File lib/graphql/language/sanitized_printer.rb, line 115 def print_field(field, indent: "") @current_field = query.get_field(@current_type, field.name) old_type = @current_type @current_type = @current_field.type.unwrap res = super @current_type = old_type res end
GraphQL::Language::Printer#print_fragment_definition
# File lib/graphql/language/sanitized_printer.rb, line 138 def print_fragment_definition(fragment_def, indent: "") old_type = @current_type @current_type = query.get_type(fragment_def.type.name) res = super @current_type = old_type res end
GraphQL::Language::Printer#print_inline_fragment
# File lib/graphql/language/sanitized_printer.rb, line 124 def print_inline_fragment(inline_fragment, indent: "") old_type = @current_type if inline_fragment.type @current_type = query.get_type(inline_fragment.type.name) end res = super @current_type = old_type res end
GraphQL::Language::Printer#print_node
# File lib/graphql/language/sanitized_printer.rb, line 39 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
Print the operation definition but do not include the variable definitions since we will inline them within the query
GraphQL::Language::Printer#print_operation_definition
# File lib/graphql/language/sanitized_printer.rb, line 160 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
GraphQL::Language::Printer#print_variable_identifier
# File lib/graphql/language/sanitized_printer.rb, line 106 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
Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings arguments but this can be customized by subclasses.
# File lib/graphql/language/sanitized_printer.rb, line 64 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
Returns the value to use for redacted versions of the given argument. Defaults to the string “<REDACTED>”.
# File lib/graphql/language/sanitized_printer.rb, line 72 def redacted_argument_value(argument) REDACTED end
@return [String, nil] A scrubbed query string, if the query was valid.
# File lib/graphql/language/sanitized_printer.rb, line 31 def sanitized_query_string if query.valid? print(query.document) else nil end end
Private Instance Methods
# File lib/graphql/language/sanitized_printer.rb, line 179 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.get_argument(key.to_s, @query.context).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