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: "")

definitions since we will inline them within the query
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)

arguments but this can be customized by subclasses.
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)

string "".
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

Returns:
  • (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