class BetterGraphQL::DetectInvalidIdField

Attributes

cache[R]

Public Class Methods

new() click to toggle source
# File lib/better_graphql/detect_invalid_id_field.rb, line 9
def initialize
  @cache = {}
end
use(schema) click to toggle source
# File lib/better_graphql/detect_invalid_id_field.rb, line 4
def self.use(schema)
  tracer = new
  schema.tracer(tracer)
end

Public Instance Methods

trace(key, data) { || ... } click to toggle source
# File lib/better_graphql/detect_invalid_id_field.rb, line 13
def trace(key, data)
  @cache = {} if key == 'parse'

  return yield if key != 'execute_field'

  yield.tap do |resolved_value|
    next unless comparable?(resolved_value)

    if data[:context]
      parent_type = data[:context].parent_type
      field = data[:context].field
      path = data[:context].path
    else
      parent_type = data[:owner]
      field = data[:field]
      path = data[:path]
    end

    cache_by_type = cache[parent_type] ||= {}
    nodes_by_path = cache_by_type[:nodes_by_path] ||= {}
    nodes_by_id = cache_by_type[:nodes_by_id] ||= {}

    field_name = field.name.to_sym
    parent_path = path[0..-2]

    resolving_object = nodes_by_path[parent_path] ||= {
      __metadata__: { parent_path: parent_path },
    }

    resolving_object[field_name] = resolved_value

    if field_name == :id
      nodes_by_id[resolved_value] ||= []
      nodes_by_id[resolved_value] << resolving_object
      next
    end

    if resolving_object.key?(:id)
      all_nodes_with_same_id = nodes_by_id[resolving_object[:id]]

      other_nodes_with_same_id = all_nodes_with_same_id - [resolving_object]

      other_nodes_with_same_id.each do |another_node|
        next unless another_node[field_name] != resolved_value
        raise StandardError,
          "The field #{parent_type.graphql_name}#id must be unique across the graph." \
          ' You have two nodes representing the same object in the graph, but they are returning different values for the same field.' \
          " #{parent_type.graphql_name}#id = #{resolving_object[:id]}." \
          " Divergence: #{(another_node[:__metadata__][:parent_path] + [field_name]).join('.')} = #{another_node[field_name].to_json} and #{path.join('.')} = #{resolved_value.to_json}"
      end
    end
  end
end

Private Instance Methods

comparable?(value) click to toggle source
# File lib/better_graphql/detect_invalid_id_field.rb, line 71
def comparable?(value)
  value.is_a?(Numeric) ||
    value.is_a?(String) ||
    value.is_a?(Date) ||
    value.is_a?(DateTime) ||
    value.is_a?(Time) ||
    value.is_a?(TrueClass) ||
    value.is_a?(FalseClass) ||
    value.is_a?(NilClass) ||
    value.is_a?(Hash)
end