Class: GraphQL::Schema::RelayClassicMutation

Inherits:
Mutation show all
Defined in:
lib/graphql/schema/relay_classic_mutation.rb

Overview

Mutations that extend this base class get some conventions added for free:

  • An argument called clientMutationId is always added, but it's not passed to the resolve method. The value is re-inserted to the response. (It's for client libraries to manage optimistic updates.)
  • The returned object type always has a field called clientMutationId to support that.
  • The mutation accepts one argument called input, arguments defined in the mutation class are added to that input object, which is generated by the mutation.

These conventions were first specified by Relay Classic, but they come in handy:

  • clientMutationId supports optimistic updates and cache rollbacks on the client
  • using a single input: argument makes it easy to post whole JSON objects to the mutation using one GraphQL variable ($input) instead of making a separate variable for each argument.

See Also:

  • for an example, it's basically the same.

Constant Summary

Constants included from Member::HasFields

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

Constants included from GraphQL::Schema::Resolver::HasPayloadType

GraphQL::Schema::Resolver::HasPayloadType::NO_INTERFACES

Constants included from Member::HasArguments

Member::HasArguments::NO_ARGUMENTS

Constants included from EmptyObjects

EmptyObjects::EMPTY_ARRAY, EmptyObjects::EMPTY_HASH

Constants included from Member::GraphQLTypeNames

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

Instance Attribute Summary

Attributes inherited from Resolver

#context, #field, #object

Attributes included from Member::BaseDSLMethods

#default_graphql_name, #graphql_name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Mutation

visible?

Methods included from Member::HasFields

#add_field, #all_field_definitions, #field, #field_class, #global_id_field, #own_fields

Methods included from GraphQL::Schema::Resolver::HasPayloadType

#field, #field_class, #object_class, #payload_type, #type

Methods inherited from Resolver

#arguments, #authorized?, broadcastable, broadcastable?, complexity, #dataloader, default_page_size, extension, extensions, extras, has_default_page_size?, has_max_page_size?, #initialize, max_page_size, null, #ready?, #resolve, resolve_method, resolver_method, type, type_expr, #unauthorized_object

Methods included from Member::BaseDSLMethods

#authorized?, #default_relay, #description, #introspection, #introspection?, #mutation, #name, #visible?

Methods included from Member::HasArguments

#add_argument, #all_argument_definitions, #argument, #argument_class, #arguments, #arguments_statically_coercible?, #coerce_arguments, #get_argument, #own_arguments, #remove_argument, #validate_directive_argument

Methods included from Member::HasValidators

#validates, #validators

Methods included from Member::HasPath

#path

Constructor Details

This class inherits a constructor from GraphQL::Schema::Resolver

Class Method Details

.all_field_argument_definitionsObject



95
96
97
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 95

def all_field_argument_definitions
  dummy.all_argument_definitions
end

.argument(*args, own_argument: false, **kwargs, &block) ⇒ Object

Also apply this argument to the input type:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 100

def argument(*args, own_argument: false, **kwargs, &block)
  it = input_type # make sure any inherited arguments are already added to it
  arg = super(*args, **kwargs, &block)

  # This definition might be overriding something inherited;
  # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
  prev_args = it.own_arguments[arg.graphql_name]
  case prev_args
  when GraphQL::Schema::Argument
    if prev_args.owner != self
      it.own_arguments.delete(arg.graphql_name)
    end
  when Array
    prev_args.reject! { |a| a.owner != self }
    if prev_args.empty?
      it.own_arguments.delete(arg.graphql_name)
    end
  end

  it.add_argument(arg)
  arg
end

.dummyObject



73
74
75
76
77
78
79
80
81
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 73

def dummy
  @dummy ||= begin
    d = Class.new(GraphQL::Schema::Resolver)
    d.argument_class(self.argument_class)
    # TODO make this lazier?
    d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
    d
  end
end

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



83
84
85
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 83

def field_arguments(context = GraphQL::Query::NullContext)
  dummy.arguments(context)
end

.get_field_argument(name, context = GraphQL::Query::NullContext) ⇒ Object



87
88
89
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 87

def get_field_argument(name, context = GraphQL::Query::NullContext)
  dummy.get_argument(name, context)
end

.input_object_class(new_class = nil) ⇒ Class

The base class for generated input object types

Parameters:

  • new_class (Class) (defaults to: nil)

    The base class to use for generating input object definitions

Returns:

  • (Class)

    The base class for this mutation's generated input object (default is InputObject)



126
127
128
129
130
131
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 126

def input_object_class(new_class = nil)
  if new_class
    @input_object_class = new_class
  end
  @input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
end

.input_type(new_input_type = nil) ⇒ Class

Returns The generated InputObject class for this mutation's input.

Parameters:

  • new_input_type (Class, nil) (defaults to: nil)

    If provided, it configures this mutation to accept new_input_type instead of generating an input type

Returns:

  • (Class)

    The generated InputObject class for this mutation's input



135
136
137
138
139
140
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 135

def input_type(new_input_type = nil)
  if new_input_type
    @input_type = new_input_type
  end
  @input_type ||= generate_input_type
end

.own_field_argumentsObject



91
92
93
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 91

def own_field_arguments
  dummy.own_arguments
end

Instance Method Details

#resolve_with_support(**inputs) ⇒ Object

Override GraphQL::Schema::Resolver#resolve_with_support to delete client_mutation_id from the kwargs.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/graphql/schema/relay_classic_mutation.rb', line 31

def resolve_with_support(**inputs)
  input = inputs[:input].to_kwargs

  new_extras = field ? field.extras : []
  all_extras = self.class.extras + new_extras

  # Transfer these from the top-level hash to the
  # shortcutted `input:` object
  all_extras.each do |ext|
    # It's possible that the `extra` was not passed along by this point,
    # don't re-add it if it wasn't given here.
    if inputs.key?(ext)
      input[ext] = inputs[ext]
    end
  end

  if input
    # This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
    input_kwargs = input.to_h
    client_mutation_id = input_kwargs.delete(:client_mutation_id)
  else
    # Relay Classic Mutations with no `argument`s
    # don't require `input:`
    input_kwargs = {}
  end

  return_value = if input_kwargs.any?
    super(**input_kwargs)
  else
    super()
  end

  context.query.after_lazy(return_value) do |return_hash|
    # It might be an error
    if return_hash.is_a?(Hash)
      return_hash[:client_mutation_id] = client_mutation_id
    end
    return_hash
  end
end