Class: GraphQL::Schema::Object

Inherits:
Member
  • Object
show all
Extended by:
Member::AcceptsDefinition, Member::HasFields
Defined in:
lib/graphql/schema/object.rb

Constant Summary

Constants included from Member::HasFields

Member::HasFields::CONFLICT_FIELD_NAMES, Member::HasFields::GRAPHQL_RUBY_KEYWORDS, Member::HasFields::RUBY_KEYWORDS

Constants included from Member::HasDirectives

Member::HasDirectives::NO_DIRECTIVES

Constants included from Member::GraphQLTypeNames

Member::GraphQLTypeNames::Boolean, Member::GraphQLTypeNames::ID, Member::GraphQLTypeNames::Int

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Member::HasFields

add_field, field, field_class, fields, get_field, global_id_field, own_fields

Methods included from Member::CachedGraphQLDefinition

#graphql_definition, #initialize_copy, #type_class

Methods included from Relay::TypeExtensions

#connection_type, #define_connection, #define_edge, #edge_type

Methods included from Member::BaseDSLMethods

#accessible?, #authorized?, #default_graphql_name, #description, #graphql_name, #introspection, #introspection?, #mutation, #name, #overridden_graphql_name, #to_graphql, #visible?

Methods included from Member::BaseDSLMethods::ConfigurationExtension

#inherited

Methods included from Member::TypeSystemHelpers

#kind, #list?, #non_null?, #to_list_type, #to_non_null_type, #to_type_signature

Methods included from Member::Scoped

#scope_items

Methods included from Member::RelayShortcuts

#connection_type, #connection_type_class, #edge_type, #edge_type_class

Methods included from Member::HasPath

#path

Methods included from Member::HasAstNode

#ast_node

Methods included from Member::HasDirectives

#directive, #directives, #remove_directive

Constructor Details

#initialize(object, context) ⇒ Object

Returns a new instance of Object.



93
94
95
96
# File 'lib/graphql/schema/object.rb', line 93

def initialize(object, context)
  @object = object
  @context = context
end

Instance Attribute Details

#contextGraphQL::Query::Context (readonly)

Returns the context instance for this query.

Returns:



15
16
17
# File 'lib/graphql/schema/object.rb', line 15

def context
  @context
end

#objectObject (readonly)

Returns the application object this type is wrapping.

Returns:

  • (Object)

    the application object this type is wrapping



12
13
14
# File 'lib/graphql/schema/object.rb', line 12

def object
  @object
end

Class Method Details

.authorized_new(object, context) ⇒ GraphQL::Schema::Object, GraphQL::Execution::Lazy

Make a new instance of this type if the auth check passes, otherwise, raise an error.

Probably only the framework should call this method.

This might return a Execution::Lazy if the user-provided .authorized? hook returns some lazy value (like a Promise).

The reason that the auth check is in this wrapper method instead of new is because of how it might return a Promise. It would be weird if .new returned a promise; It would be a headache to try to maintain Promise-y state inside a GraphQL::Schema::Object instance. So, hopefully this wrapper method will do the job.

Parameters:

Returns:

Raises:



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/graphql/schema/object.rb', line 50

def authorized_new(object, context)
  trace_payload = { context: context, type: self, object: object, path: context[:current_path] }

  maybe_lazy_auth_val = context.query.trace("authorized", trace_payload) do
    context.query.with_error_handling do
      begin
        authorized?(object, context)
      rescue GraphQL::UnauthorizedError => err
        context.schema.unauthorized_object(err)
      end
    end
  end

  auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
    GraphQL::Execution::Lazy.new do
      context.query.trace("authorized_lazy", trace_payload) do
        context.schema.sync_lazy(maybe_lazy_auth_val)
      end
    end
  else
    maybe_lazy_auth_val
  end

  context.schema.after_lazy(auth_val) do |is_authorized|
    if is_authorized
      self.new(object, context)
    else
      # It failed the authorization check, so go to the schema's authorized object hook
      err = GraphQL::UnauthorizedError.new(object: object, type: self, context: context)
      # If a new value was returned, wrap that instead of the original value
      begin
        new_obj = context.schema.unauthorized_object(err)
        if new_obj
          self.new(new_obj, context)
        else
          nil
        end
      end
    end
  end
end

.fieldsHash<String => GraphQL::Schema::Field>

Returns All of this object’s fields, indexed by name.



177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/graphql/schema/object.rb', line 177

def fields
  all_fields = super
  interfaces.each do |int|
    # Include legacy-style interfaces, too
    if int.is_a?(GraphQL::InterfaceType)
      int_f = {}
      int.fields.each do |name, legacy_field|
        int_f[name] = field_class.from_options(name, field: legacy_field)
      end
      all_fields = int_f.merge(all_fields)
    end
  end
  all_fields
end

.implements(*new_interfaces, **options) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/graphql/schema/object.rb', line 106

def implements(*new_interfaces, **options)
  new_memberships = []
  new_interfaces.each do |int|
    if int.is_a?(Module)
      unless int.include?(GraphQL::Schema::Interface)
        raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
      end

      new_memberships << int.type_membership_class.new(int, self, **options)

      # Include the methods here,
      # `.fields` will use the inheritance chain
      # to find inherited fields
      include(int)
    elsif int.is_a?(GraphQL::InterfaceType)
      new_memberships << int.type_membership_class.new(int, self, **options)
    elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
      if options.any?
        raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
      end
      new_memberships << int
    else
      raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
    end
  end

  # Remove any interfaces which are being replaced (late-bound types are updated in place this way)
  own_interface_type_memberships.reject! { |old_i_m|
    old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
    old_name = Schema::Member::BuildType.to_type_name(old_int_type)

    new_memberships.any? { |new_i_m|
      new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
      new_name = Schema::Member::BuildType.to_type_name(new_int_type)

      new_name == old_name
    }
  }
  own_interface_type_memberships.concat(new_memberships)
end

.inherited(child_class) ⇒ Object

Set up a type-specific invalid null error to use when this object’s non-null fields wrongly return nil. It should help with debugging and bug tracker integrations.



101
102
103
104
# File 'lib/graphql/schema/object.rb', line 101

def inherited(child_class)
  child_class.const_set(:InvalidNullError, GraphQL::InvalidNullError.subclass_for(child_class))
  super
end

.interface_type_membershipsObject



151
152
153
# File 'lib/graphql/schema/object.rb', line 151

def interface_type_memberships
  own_interface_type_memberships + (superclass.respond_to?(:interface_type_memberships) ? superclass.interface_type_memberships : [])
end

.interfaces(context = GraphQL::Query::NullContext) ⇒ Object

param context [Query::Context] If omitted, skip filtering.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/graphql/schema/object.rb', line 156

def interfaces(context = GraphQL::Query::NullContext)
  visible_interfaces = []
  unfiltered = context == GraphQL::Query::NullContext
  own_interface_type_memberships.each do |type_membership|
    # During initialization, `type_memberships` can hold late-bound types
    case type_membership
    when String, Schema::LateBoundType
      visible_interfaces << type_membership
    when Schema::TypeMembership
      if unfiltered || type_membership.visible?(context)
        visible_interfaces << type_membership.abstract_type
      end
    else
      raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
    end
  end
  visible_interfaces + (superclass <= GraphQL::Schema::Object ? superclass.interfaces(context) : [])
end

.kindObject



211
212
213
# File 'lib/graphql/schema/object.rb', line 211

def kind
  GraphQL::TypeKinds::OBJECT
end

.own_interface_type_membershipsObject



147
148
149
# File 'lib/graphql/schema/object.rb', line 147

def own_interface_type_memberships
  @own_interface_type_memberships ||= []
end

.to_graphqlGraphQL::ObjectType

Returns:



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/graphql/schema/object.rb', line 193

def to_graphql
  obj_type = GraphQL::ObjectType.new
  obj_type.name = graphql_name
  obj_type.description = description
  obj_type.structural_interface_type_memberships = interface_type_memberships
  obj_type.introspection = introspection
  obj_type.mutation = mutation
  obj_type.ast_node = ast_node
  fields.each do |field_name, field_inst|
    field_defn = field_inst.to_graphql
    obj_type.fields[field_defn.name] = field_defn
  end

  obj_type.[:type_class] = self

  obj_type
end

Instance Method Details

#dataloaderGraphQL::Dataloader

Returns:



18
19
20
# File 'lib/graphql/schema/object.rb', line 18

def dataloader
  context.dataloader
end

#raw_value(obj) ⇒ Object

Call this in a field method to return a value that should be returned to the client without any further handling by GraphQL.



24
25
26
# File 'lib/graphql/schema/object.rb', line 24

def raw_value(obj)
  GraphQL::Execution::Interpreter::RawValue.new(obj)
end