module GraphQL::Backtrace::Tracer

TODO this is not fiber-friendly

Public Instance Methods

trace(key, metadata) { || ... } click to toggle source

Implement the {GraphQL::Tracing} API.

# File lib/graphql/backtrace/tracer.rb, line 9
def trace(key, metadata)
  case key
  when "lex", "parse"
    # No context here, don't have a query yet
    nil
  when "execute_multiplex", "analyze_multiplex"
    # No query context yet
    nil
  when "validate", "analyze_query", "execute_query", "execute_query_lazy"
    push_key = []
    if (query = metadata[:query]) || ((queries = metadata[:queries]) && (query = queries.first))
      push_data = query
      multiplex = query.multiplex
    elsif (multiplex = metadata[:multiplex])
      push_data = multiplex.queries.first
    end
  when "execute_field", "execute_field_lazy"
    query = metadata[:query] || raise(ArgumentError, "Add `legacy: true` to use GraphQL::Backtrace without the interpreter runtime.")
    multiplex = query.multiplex
    push_key = metadata[:path]
    parent_frame = multiplex.context[:graphql_backtrace_contexts][push_key[0..-2]]

    if parent_frame.is_a?(GraphQL::Query)
      parent_frame = parent_frame.context
    end

    push_data = Frame.new(
      query: query,
      path: push_key,
      ast_node: metadata[:ast_node],
      field: metadata[:field],
      object: metadata[:object],
      arguments: metadata[:arguments],
      parent_frame: parent_frame,
    )
  else
    # Custom key, no backtrace data for this
    nil
  end

  if push_data && multiplex
    push_storage = multiplex.context[:graphql_backtrace_contexts] ||= {}
    push_storage[push_key] = push_data
    multiplex.context[:last_graphql_backtrace_context] = push_data
  end

  if key == "execute_multiplex"
    multiplex_context = metadata[:multiplex].context
    begin
      yield
    rescue StandardError => err
      # This is an unhandled error from execution,
      # Re-raise it with a GraphQL trace.
      potential_context = multiplex_context[:last_graphql_backtrace_context]

      if potential_context.is_a?(GraphQL::Query::Context) ||
          potential_context.is_a?(GraphQL::Query::Context::FieldResolutionContext) ||
          potential_context.is_a?(Backtrace::Frame)
        raise TracedError.new(err, potential_context)
      else
        raise
      end
    ensure
      multiplex_context.delete(:graphql_backtrace_contexts)
      multiplex_context.delete(:last_graphql_backtrace_context)
    end
  else
    yield
  end
end