module GraphQL::Schema::Member::HasFields

Shared code for Objects, Interfaces, Mutations, Subscriptions

Constants

CONFLICT_FIELD_NAMES

A list of field names that we should advise users to pick a different resolve method name.

@api private

GRAPHQL_RUBY_KEYWORDS

A list of GraphQL-Ruby keywords.

@api private

RUBY_KEYWORDS

A list of Ruby keywords.

@api private

Public Instance Methods

add_field(field_defn, method_conflict_warning: field_defn.method_conflict_warning?) click to toggle source

Register this field with the class, overriding a previous one if needed. @param field_defn [GraphQL::Schema::Field] @return [void]

# File lib/graphql/schema/member/has_fields.rb, line 72
def add_field(field_defn, method_conflict_warning: field_defn.method_conflict_warning?)
  # Check that `field_defn.original_name` equals `resolver_method` and `method_sym` --
  # that shows that no override value was given manually.
  if method_conflict_warning && CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && field_defn.original_name == field_defn.resolver_method && field_defn.original_name == field_defn.method_sym
    warn(conflict_field_name_warning(field_defn))
  end
  prev_defn = own_fields[field_defn.name]

  case prev_defn
  when nil
    own_fields[field_defn.name] = field_defn
  when Array
    prev_defn << field_defn
  when GraphQL::Schema::Field
    own_fields[field_defn.name] = [prev_defn, field_defn]
  else
    raise "Invariant: unexpected previous field definition for #{field_defn.name.inspect}: #{prev_defn.inspect}"
  end

  nil
end
all_field_definitions() click to toggle source
# File lib/graphql/schema/member/has_fields.rb, line 118
def all_field_definitions
  all_fields = {}
  ancestors.reverse_each do |ancestor|
    if ancestor.respond_to?(:own_fields)
      all_fields.merge!(ancestor.own_fields)
    end
  end
  all_fields = all_fields.values
  all_fields.flatten!
  all_fields
end
field(*args, **kwargs, &block) click to toggle source

Add a field to this object or interface with the given definition @see {GraphQL::Schema::Field#initialize} for method signature @return [GraphQL::Schema::Field]

# File lib/graphql/schema/member/has_fields.rb, line 11
def field(*args, **kwargs, &block)
  field_defn = field_class.from_options(*args, owner: self, **kwargs, &block)
  add_field(field_defn)
  field_defn
end
field_class(new_field_class = nil) click to toggle source

@return [Class] The class to initialize when adding fields to this kind of schema member

# File lib/graphql/schema/member/has_fields.rb, line 95
def field_class(new_field_class = nil)
  if new_field_class
    @field_class = new_field_class
  elsif defined?(@field_class) && @field_class
    @field_class
  else
    find_inherited_value(:field_class, GraphQL::Schema::Field)
  end
end
fields(context = GraphQL::Query::NullContext) click to toggle source

@return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields

# File lib/graphql/schema/member/has_fields.rb, line 18
def fields(context = GraphQL::Query::NullContext)
  warden = Warden.from_context(context)
  is_object = self.respond_to?(:kind) && self.kind.object?
  # Local overrides take precedence over inherited fields
  visible_fields = {}
  for ancestor in ancestors
    if ancestor.respond_to?(:own_fields) &&
        (is_object ? visible_interface_implementation?(ancestor, context, warden) : true)

      ancestor.own_fields.each do |field_name, fields_entry|
        # Choose the most local definition that passes `.visible?` --
        # stop checking for fields by name once one has been found.
        if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
          visible_fields[field_name] = f
        end
      end
    end
  end
  visible_fields
end
get_field(field_name, context = GraphQL::Query::NullContext) click to toggle source
# File lib/graphql/schema/member/has_fields.rb, line 39
def get_field(field_name, context = GraphQL::Query::NullContext)
  warden = Warden.from_context(context)
  is_object = self.respond_to?(:kind) && self.kind.object?
  for ancestor in ancestors
    if ancestor.respond_to?(:own_fields) &&
        (is_object ? visible_interface_implementation?(ancestor, context, warden) : true) &&
        (f_entry = ancestor.own_fields[field_name]) &&
        (f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
      return f
    end
  end
  nil
end
global_id_field(field_name, **kwargs) click to toggle source
# File lib/graphql/schema/member/has_fields.rb, line 105
def global_id_field(field_name, **kwargs)
  id_resolver = GraphQL::Relay::GlobalIdResolve.new(type: self)
  field field_name, "ID", **kwargs, null: false
  define_method(field_name) do
    id_resolver.call(object, {}, context)
  end
end
own_fields() click to toggle source

@return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class specifically, not parent classes

# File lib/graphql/schema/member/has_fields.rb, line 114
def own_fields
  @own_fields ||= {}
end

Private Instance Methods

conflict_field_name_warning(field_defn) click to toggle source

@param [GraphQL::Schema::Field] @return [String] A warning to give when this field definition might conflict with a built-in method

# File lib/graphql/schema/member/has_fields.rb, line 157
def conflict_field_name_warning(field_defn)
  "#{self.graphql_name}'s `field :#{field_defn.original_name}` conflicts with a built-in method, use `resolver_method:` to pick a different resolver method for this field (for example, `resolver_method: :resolve_#{field_defn.resolver_method}` and `def resolve_#{field_defn.resolver_method}`). Or use `method_conflict_warning: false` to suppress this warning."
end
visible_interface_implementation?(type, context, warden) click to toggle source

If `type` is an interface, and `self` has a type membership for `type`, then make sure it's visible.

# File lib/graphql/schema/member/has_fields.rb, line 133
def visible_interface_implementation?(type, context, warden)
  if type.respond_to?(:kind) && type.kind.interface?
    implements_this_interface = false
    implementation_is_visible = false
    warden.interface_type_memberships(self, context).each do |tm|
      if tm.abstract_type == type
        implements_this_interface ||= true
        if warden.visible_type_membership?(tm, context)
          implementation_is_visible = true
          break
        end
      end
    end
    # It's possible this interface came by way of `include` in another interface which this
    # object type _does_ implement, and that's ok
    implements_this_interface ? implementation_is_visible : true
  else
    # If there's no implementation, then we're looking at Ruby-style inheritance instead
    true
  end
end