Class: GraphQL::Query::Context

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Schema::Member::HasDataloader
Defined in:
lib/graphql/query/context.rb,
lib/graphql/query/context/scoped_context.rb

Overview

Expose some query-specific info to field resolve functions. It delegates [] to the hash that's passed to GraphQL::Query#initialize.

Direct Known Subclasses

NullContext

Defined Under Namespace

Classes: ExecutionErrors, Scoped, ScopedContext

Constant Summary collapse

RUNTIME_METADATA_KEYS =
Set.new([:current_object, :current_arguments, :current_field, :current_path]).freeze
UNSPECIFIED_FETCH_DEFAULT =
Object.new

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Schema::Member::HasDataloader

#dataload, #dataload_all, #dataload_all_associations, #dataload_all_records, #dataload_association, #dataload_record

Constructor Details

#initialize(query:, schema: query.schema, values:) ⇒ Context

Make a new context which delegates key lookup to values

Parameters:

  • query (GraphQL::Query)

    the query who owns this context

  • values (Hash)

    A hash of arbitrary values which will be accessible at query-time



46
47
48
49
50
51
52
53
54
55
# File 'lib/graphql/query/context.rb', line 46

def initialize(query:, schema: query.schema, values:)
  @query = query
  @schema = schema
  @provided_values = values || {}
  # Namespaced storage, where user-provided values are in `nil` namespace:
  @storage = Hash.new { |h, k| h[k] = {} }
  @storage[nil] = @provided_values
  @errors = []
  @scoped_context = ScopedContext.new(self)
end

Instance Attribute Details

#errorsArray<GraphQL::ExecutionError> (readonly)

Returns errors returned during execution.

Returns:



35
36
37
# File 'lib/graphql/query/context.rb', line 35

def errors
  @errors
end

#interpreter=(value) ⇒ Object (writeonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



68
69
70
# File 'lib/graphql/query/context.rb', line 68

def interpreter=(value)
  @interpreter = value
end

#queryGraphQL::Query (readonly)

Returns The query whose context this is.

Returns:



38
39
40
# File 'lib/graphql/query/context.rb', line 38

def query
  @query
end

#schemaGraphQL::Schema (readonly)

Returns:



41
42
43
# File 'lib/graphql/query/context.rb', line 41

def schema
  @schema
end

#scoped_contextObject (readonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



74
75
76
# File 'lib/graphql/query/context.rb', line 74

def scoped_context
  @scoped_context
end

#typesObject



82
83
84
# File 'lib/graphql/query/context.rb', line 82

def types
  @types ||= @query.types
end

#value=(value) ⇒ Object (writeonly)

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



71
72
73
# File 'lib/graphql/query/context.rb', line 71

def value=(value)
  @value = value
end

#wardenGraphQL::Schema::Warden



221
222
223
# File 'lib/graphql/query/context.rb', line 221

def warden
  @warden ||= (@query && @query.warden)
end

Instance Method Details

#[](key) ⇒ Object

Lookup key from the hash passed to Schema#execute as context:



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/graphql/query/context.rb', line 93

def [](key)
  if @scoped_context.key?(key)
    @scoped_context[key]
  elsif @provided_values.key?(key)
    @provided_values[key]
  elsif RUNTIME_METADATA_KEYS.include?(key)
    if key == :current_path
      current_path
    else
      (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
        (query_runtime_state = current_runtime_state[@query]) &&
        (query_runtime_state.public_send(key))
    end
  else
    # not found
    nil
  end
end

#[]=(key, value) ⇒ Object

Reassign key to the hash passed to Schema#execute as context:



89
90
91
# File 'lib/graphql/query/context.rb', line 89

def []=(key, value)
  @provided_values[key] = value
end

#add_error(error) ⇒ void

This method returns an undefined value.

Add error at query-level.

Parameters:



121
122
123
124
125
126
127
# File 'lib/graphql/query/context.rb', line 121

def add_error(error)
  if !error.is_a?(GraphQL::RuntimeError)
    raise TypeError, "expected error to be a GraphQL::RuntimeError, but was #{error.class}"
  end
  errors << error
  nil
end

#backtraceGraphQL::Backtrace

Returns The backtrace for this point in query execution.

Examples:

Print the GraphQL backtrace during field resolution

puts ctx.backtrace

Returns:



139
140
141
# File 'lib/graphql/query/context.rb', line 139

def backtrace
  GraphQL::Backtrace.new(self)
end

#current_pathObject



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/graphql/query/context.rb', line 147

def current_path
  current_runtime_state = Fiber[:__graphql_runtime_info]
  query_runtime_state = current_runtime_state && current_runtime_state[@query]

  path = query_runtime_state &&
    (result = query_runtime_state.current_result) &&
    (result.path)
  if path && (rn = query_runtime_state.current_result_name)
    path = path.dup
    path.push(rn)
  end
  path
end

#dataloaderObject



63
64
65
# File 'lib/graphql/query/context.rb', line 63

def dataloader
  @dataloader ||= self[:dataloader] || (query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new)
end

#delete(key) ⇒ Object



161
162
163
164
165
166
167
# File 'lib/graphql/query/context.rb', line 161

def delete(key)
  if @scoped_context.key?(key)
    @scoped_context.delete(key)
  else
    @provided_values.delete(key)
  end
end

#dig(key, *other_keys) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/graphql/query/context.rb', line 189

def dig(key, *other_keys)
  if RUNTIME_METADATA_KEYS.include?(key)
    (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
      (query_runtime_state = current_runtime_state[@query]) &&
      (obj = query_runtime_state.public_send(key)) &&
      if other_keys.empty?
        obj
      else
        obj.dig(*other_keys)
      end
  elsif @scoped_context.key?(key)
    @scoped_context.dig(key, *other_keys)
  else
    @provided_values.dig(key, *other_keys)
  end
end

#execution_errorsObject



143
144
145
# File 'lib/graphql/query/context.rb', line 143

def execution_errors
  @execution_errors ||= ExecutionErrors.new(self)
end

#fetch(key, default = UNSPECIFIED_FETCH_DEFAULT) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/graphql/query/context.rb', line 171

def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
  if RUNTIME_METADATA_KEYS.include?(key)
    (runtime = Fiber[:__graphql_runtime_info]) &&
      (query_runtime_state = runtime[@query]) &&
      (query_runtime_state.public_send(key))
  elsif @scoped_context.key?(key)
    scoped_context[key]
  elsif @provided_values.key?(key)
    @provided_values[key]
  elsif default != UNSPECIFIED_FETCH_DEFAULT
    default
  elsif block_given?
    yield(self, key)
  else
    raise KeyError.new(key: key)
  end
end

#inspectObject



248
249
250
# File 'lib/graphql/query/context.rb', line 248

def inspect
  "#<#{self.class} ...>"
end

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


216
217
218
# File 'lib/graphql/query/context.rb', line 216

def key?(key)
  @scoped_context.key?(key) || @provided_values.key?(key)
end

#loggerObject



244
245
246
# File 'lib/graphql/query/context.rb', line 244

def logger
  @query && @query.logger
end

#namespace(ns) ⇒ Hash

Get an isolated hash for ns. Doesn't affect user-provided storage.

Parameters:

  • ns (Object)

    a usage-specific namespace identifier

Returns:

  • (Hash)

    namespaced storage



231
232
233
234
235
236
237
# File 'lib/graphql/query/context.rb', line 231

def namespace(ns)
  if ns == :interpreter
    self
  else
    @storage[ns]
  end
end

#namespace?(ns) ⇒ Boolean

Returns true if this namespace was accessed before.

Returns:

  • (Boolean)

    true if this namespace was accessed before



240
241
242
# File 'lib/graphql/query/context.rb', line 240

def namespace?(ns)
  @storage.key?(ns)
end

#raw_value(value) ⇒ GraphQL::Execution::Interpreter::RawValue

Return this from the field

Parameters:

  • value (Object)

    Any object to be inserted directly into the final response

Returns:



131
132
133
# File 'lib/graphql/query/context.rb', line 131

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

#response_extensionsHash

Modify this hash to return extensions to client.

Returns:

  • (Hash)

    A hash that will be added verbatim to the result hash, as "extensions" => { ... }



59
60
61
# File 'lib/graphql/query/context.rb', line 59

def response_extensions
  namespace(:__query_result_extensions__)
end

#scopedContext::Scoped

Use this when you need to do a scoped set inside a lazy-loaded (or batch-loaded) block of code.

Examples:

using scoped context inside a promise

scoped_ctx = context.scoped
SomeBatchLoader.load(...).then do |thing|
  # use a scoped_ctx which was created _before_ dataloading:
  scoped_ctx.set!(:thing, thing)
end

Returns:



271
272
273
# File 'lib/graphql/query/context.rb', line 271

def scoped
  Scoped.new(@scoped_context, current_path)
end

#scoped_merge!(hash) ⇒ Object



252
253
254
# File 'lib/graphql/query/context.rb', line 252

def scoped_merge!(hash)
  @scoped_context.merge!(hash)
end

#scoped_set!(key, value) ⇒ Object



256
257
258
259
# File 'lib/graphql/query/context.rb', line 256

def scoped_set!(key, value)
  scoped_merge!(key => value)
  nil
end

#skipObject

Return this value to tell the runtime to exclude this field from the response altogether



114
115
116
# File 'lib/graphql/query/context.rb', line 114

def skip
  GraphQL::Execution::Skip.new
end

#to_hObject Also known as: to_hash



206
207
208
209
210
211
212
# File 'lib/graphql/query/context.rb', line 206

def to_h
  if (current_scoped_context = @scoped_context.merged_context)
    @provided_values.merge(current_scoped_context)
  else
    @provided_values
  end
end