Class: GraphQL::Schema::Argument

Constant Summary collapse

NO_DEFAULT =
:__no_default__

Constants included from FindInheritedValue::EmptyObjects

FindInheritedValue::EmptyObjects::EMPTY_ARRAY, FindInheritedValue::EmptyObjects::EMPTY_HASH

Constants included from Member::HasDirectives

Member::HasDirectives::NO_DIRECTIVES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Member::HasValidators

#validates, #validators

Methods included from Member::HasDirectives

#directive, #directives, #remove_directive

Methods included from Member::HasAstNode

#ast_node

Methods included from Member::HasPath

#path

Methods included from Member::CachedGraphQLDefinition

#graphql_definition, #initialize_copy, #type_class

Constructor Details

#initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block) ⇒ Argument

Returns a new instance of Argument.

Parameters:

  • arg_name (Symbol) (defaults to: nil)
  • type_expr (defaults to: nil)
  • desc (String) (defaults to: nil)
  • required (Boolean)

    if true, this argument is non-null; if false, this argument is nullable

  • description (String) (defaults to: nil)
  • default_value (Object) (defaults to: NO_DEFAULT)
  • as (Symbol) (defaults to: nil)

    Override the keyword name when passed to a method

  • prepare (Symbol) (defaults to: nil)

    A method to call to transform this argument's valuebefore sending it to field resolution

  • camelize (Boolean) (defaults to: true)

    if true, the name will be camelized when building the schema

  • from_resolver (Boolean) (defaults to: false)

    if true, a Resolver class defined this argument

  • method_access (Boolean) (defaults to: true)

    If false, don't build method access on legacy Query::Arguments instances.

  • directives (Hash{Class => Hash}) (defaults to: nil)
  • deprecation_reason (String) (defaults to: nil)
  • validates (Hash, nil) (defaults to: nil)

    Options for building validators, if any should be applied


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
# File 'lib/graphql/schema/argument.rb', line 55

def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
  arg_name ||= name
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
  @type_expr = type_expr || type
  @description = desc || description
  @null = !required
  @default_value = default_value
  @owner = owner
  @as = as
  @loads = loads
  @keyword = as || (arg_name.is_a?(Symbol) ? arg_name : Schema::Member::BuildType.underscore(@name).to_sym)
  @prepare = prepare
  @ast_node = ast_node
  @from_resolver = from_resolver
  @method_access = method_access
  self.deprecation_reason = deprecation_reason

  if directives
    directives.each do |dir_class, dir_options|
      directive(dir_class, **dir_options)
    end
  end

  self.validates(validates)

  if definition_block
    if definition_block.arity == 1
      instance_exec(self, &definition_block)
    else
      instance_eval(&definition_block)
    end
  end
end

Instance Attribute Details

#default_valueObject (readonly)

Returns the value used when the client doesn't provide a value for this argument.

Returns:

  • (Object)

    the value used when the client doesn't provide a value for this argument


90
91
92
# File 'lib/graphql/schema/argument.rb', line 90

def default_value
  @default_value
end

#description(text = nil) ⇒ String

Returns Documentation for this argument.

Returns:

  • (String)

    Documentation for this argument


100
101
102
103
104
105
106
# File 'lib/graphql/schema/argument.rb', line 100

def description(text = nil)
  if text
    @description = text
  else
    @description
  end
end

#keywordSymbol (readonly)

Returns This argument's name in Ruby keyword arguments.

Returns:

  • (Symbol)

    This argument's name in Ruby keyword arguments


31
32
33
# File 'lib/graphql/schema/argument.rb', line 31

def keyword
  @keyword
end

#loadsClass, ... (readonly)

Returns If this argument should load an application object, this is the type of object to load.

Returns:

  • (Class, Module, nil)

    If this argument should load an application object, this is the type of object to load


34
35
36
# File 'lib/graphql/schema/argument.rb', line 34

def loads
  @loads
end

#nameString (readonly) Also known as: graphql_name

Returns the GraphQL name for this argument, camelized unless camelize: false is provided.

Returns:

  • (String)

    the GraphQL name for this argument, camelized unless camelize: false is provided


21
22
23
# File 'lib/graphql/schema/argument.rb', line 21

def name
  @name
end

#ownerGraphQL::Schema::Field, Class (readonly)

Returns The field or input object this argument belongs to.

Returns:


25
26
27
# File 'lib/graphql/schema/argument.rb', line 25

def owner
  @owner
end

#prepareSymbol (readonly)

Returns A method to call to transform this value before sending it to field resolution method.

Returns:

  • (Symbol)

    A method to call to transform this value before sending it to field resolution method


28
29
30
# File 'lib/graphql/schema/argument.rb', line 28

def prepare
  @prepare
end

Instance Method Details

#accessible?(context) ⇒ Boolean

Returns:

  • (Boolean)

126
127
128
# File 'lib/graphql/schema/argument.rb', line 126

def accessible?(context)
  true
end

#authorized?(obj, value, ctx) ⇒ Boolean

Returns:

  • (Boolean)

130
131
132
# File 'lib/graphql/schema/argument.rb', line 130

def authorized?(obj, value, ctx)
  authorized_as_type?(obj, value, ctx, as_type: type)
end

#authorized_as_type?(obj, value, ctx, as_type:) ⇒ Boolean

Returns:

  • (Boolean)

134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/graphql/schema/argument.rb', line 134

def authorized_as_type?(obj, value, ctx, as_type:)
  if value.nil?
    return true
  end

  if as_type.kind.non_null?
    as_type = as_type.of_type
  end

  if as_type.kind.list?
    value.each do |v|
      if !authorized_as_type?(obj, v, ctx, as_type: as_type.of_type)
        return false
      end
    end
  elsif as_type.kind.input_object?
    as_type.arguments.each do |_name, input_obj_arg|
      input_obj_arg = input_obj_arg.type_class
      # TODO: this skips input objects whose values were alread replaced with application objects.
      # See: https://github.com/rmosolgo/graphql-ruby/issues/2633
      if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
        return false
      end
    end
  end
  # None of the early-return conditions were activated,
  # so this is authorized.
  true
end

#coerce_into_values(parent_object, values, context, argument_values) ⇒ Object

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.


240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/graphql/schema/argument.rb', line 240

def coerce_into_values(parent_object, values, context, argument_values)
  arg_name = graphql_name
  arg_key = keyword
  has_value = false
  default_used = false
  if values.key?(arg_name)
    has_value = true
    value = values[arg_name]
  elsif values.key?(arg_key)
    has_value = true
    value = values[arg_key]
  elsif default_value?
    has_value = true
    value = default_value
    default_used = true
  end

  if has_value
    loaded_value = nil
    coerced_value = context.schema.error_handler.with_error_handling(context) do
      type.coerce_input(value, context)
    end

    # TODO this should probably be inside after_lazy
    if loads && !from_resolver?
      loaded_value = if type.list?
        loaded_values = coerced_value.map { |val| owner.load_application_object(self, loads, val, context) }
        context.schema.after_any_lazies(loaded_values) { |result| result }
      else
        owner.load_application_object(self, loads, coerced_value, context)
      end
    end

    coerced_value = if loaded_value
      loaded_value
    else
      coerced_value
    end

    # If this isn't lazy, then the block returns eagerly and assigns the result here
    # If it _is_ lazy, then we write the lazy to the hash, then update it later
    argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |coerced_value|
      owner.validate_directive_argument(self, coerced_value)
      prepared_value = context.schema.error_handler.with_error_handling(context) do
        prepare_value(parent_object, coerced_value, context: context)
      end

      # TODO code smell to access such a deeply-nested constant in a distant module
      argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
        value: prepared_value,
        definition: self,
        default_used: default_used,
      )
    end
  else
    # has_value is false
    owner.validate_directive_argument(self, nil)
  end
end

#default_value?Boolean

Returns True if this argument has a default value.

Returns:

  • (Boolean)

    True if this argument has a default value


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

def default_value?
  @default_value != NO_DEFAULT
end

#deprecation_reason(text = nil) ⇒ String

Returns Deprecation reason for this argument.

Returns:

  • (String)

    Deprecation reason for this argument


109
110
111
112
113
114
115
# File 'lib/graphql/schema/argument.rb', line 109

def deprecation_reason(text = nil)
  if text
    self.deprecation_reason = text
  else
    super()
  end
end

#deprecation_reason=(new_reason) ⇒ Object


117
118
119
120
# File 'lib/graphql/schema/argument.rb', line 117

def deprecation_reason=(new_reason)
  validate_deprecated_or_optional(null: @null, deprecation_reason: new_reason)
  super
end

#from_resolver?Boolean

Returns true if a resolver defined this argument.

Returns:

  • (Boolean)

    true if a resolver defined this argument


37
38
39
# File 'lib/graphql/schema/argument.rb', line 37

def from_resolver?
  @from_resolver
end

#prepare_value(obj, value, context: nil) ⇒ Object

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.

Apply the #prepare configuration to value, using methods from obj. Used by the runtime.


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/graphql/schema/argument.rb', line 213

def prepare_value(obj, value, context: nil)
  if value.is_a?(GraphQL::Schema::InputObject)
    value = value.prepare
  end

  Schema::Validator.validate!(validators, obj, context, value)

  if @prepare.nil?
    value
  elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
    if obj.nil?
      # The problem here is, we _used to_ prepare while building variables.
      # But now we don't have the runtime object there.
      #
      # This will have to be called later, when the runtime object _is_ available.
      value
    else
      obj.public_send(@prepare, value)
    end
  elsif @prepare.respond_to?(:call)
    @prepare.call(value, context || obj.context)
  else
    raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
  end
end

#statically_coercible?Boolean

Returns:

  • (Boolean)

204
205
206
207
208
# File 'lib/graphql/schema/argument.rb', line 204

def statically_coercible?
  return @statically_coercible if defined?(@statically_coercible)

  @statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
end

#to_graphqlObject


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/graphql/schema/argument.rb', line 164

def to_graphql
  argument = GraphQL::Argument.new
  argument.name = @name
  argument.type = -> { type }
  argument.description = @description
  argument.[:type_class] = self
  argument.as = @as
  argument.ast_node = ast_node
  argument.method_access = @method_access
  if NO_DEFAULT != @default_value
    argument.default_value = @default_value
  end
  if self.deprecation_reason
    argument.deprecation_reason = self.deprecation_reason
  end
  argument
end

#typeObject


192
193
194
195
196
197
198
199
200
201
202
# File 'lib/graphql/schema/argument.rb', line 192

def type
  @type ||= begin
    parsed_type = begin
      Member::BuildType.parse_type(@type_expr, null: @null)
    rescue StandardError => err
      raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
    end
    # Use the setter method to get validations
    self.type = parsed_type
  end
end

#type=(new_type) ⇒ Object


182
183
184
185
186
187
188
189
190
# File 'lib/graphql/schema/argument.rb', line 182

def type=(new_type)
  validate_input_type(new_type)
  # This isn't true for LateBoundTypes, but we can assume those will
  # be updated via this codepath later in schema setup.
  if new_type.respond_to?(:non_null?)
    validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason)
  end
  @type = new_type
end

#visible?(context) ⇒ Boolean

Returns:

  • (Boolean)

122
123
124
# File 'lib/graphql/schema/argument.rb', line 122

def visible?(context)
  true
end