graphql 1.10.2 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +10 -2
  3. data/lib/generators/graphql/templates/schema.erb +1 -0
  4. data/lib/graphql/define/defined_object_proxy.rb +1 -1
  5. data/lib/graphql/define/instance_definable.rb +1 -1
  6. data/lib/graphql/execution/interpreter.rb +3 -0
  7. data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
  8. data/lib/graphql/execution/interpreter/runtime.rb +37 -121
  9. data/lib/graphql/field.rb +1 -1
  10. data/lib/graphql/language/nodes.rb +41 -83
  11. data/lib/graphql/language/parser.rb +96 -96
  12. data/lib/graphql/language/parser.y +96 -96
  13. data/lib/graphql/pagination/connections.rb +21 -5
  14. data/lib/graphql/query/null_context.rb +5 -1
  15. data/lib/graphql/query/variables.rb +9 -3
  16. data/lib/graphql/schema.rb +18 -2
  17. data/lib/graphql/schema/build_from_definition.rb +15 -1
  18. data/lib/graphql/schema/enum.rb +4 -1
  19. data/lib/graphql/schema/field.rb +11 -0
  20. data/lib/graphql/schema/field/connection_extension.rb +2 -1
  21. data/lib/graphql/schema/input_object.rb +21 -38
  22. data/lib/graphql/schema/member/base_dsl_methods.rb +11 -4
  23. data/lib/graphql/schema/member/has_arguments.rb +53 -2
  24. data/lib/graphql/schema/member/has_ast_node.rb +4 -1
  25. data/lib/graphql/schema/member/has_fields.rb +1 -1
  26. data/lib/graphql/schema/resolver.rb +2 -2
  27. data/lib/graphql/schema/resolver/has_payload_type.rb +3 -1
  28. data/lib/graphql/schema/warden.rb +10 -0
  29. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +0 -1
  30. data/lib/graphql/subscriptions.rb +14 -9
  31. data/lib/graphql/subscriptions/subscription_root.rb +2 -1
  32. data/lib/graphql/version.rb +1 -1
  33. metadata +4 -3
@@ -23,6 +23,10 @@ module GraphQL
23
23
 
24
24
  def [](key); end
25
25
 
26
+ def interpreter?
27
+ false
28
+ end
29
+
26
30
  class << self
27
31
  extend Forwardable
28
32
 
@@ -32,7 +36,7 @@ module GraphQL
32
36
  @instance = self.new
33
37
  end
34
38
 
35
- def_delegators :instance, :query, :schema, :warden
39
+ def_delegators :instance, :query, :schema, :warden, :interpreter?
36
40
  end
37
41
  end
38
42
  end
@@ -34,7 +34,9 @@ module GraphQL
34
34
  if validation_result.valid?
35
35
  if value_was_provided
36
36
  # Add the variable if a value was provided
37
- memo[variable_name] = if provided_value.nil?
37
+ memo[variable_name] = if ctx.interpreter?
38
+ provided_value
39
+ elsif provided_value.nil?
38
40
  nil
39
41
  else
40
42
  schema.error_handler.with_error_handling(context) do
@@ -42,8 +44,12 @@ module GraphQL
42
44
  end
43
45
  end
44
46
  elsif default_value != nil
45
- # Add the variable if it wasn't provided but it has a default value (including `null`)
46
- memo[variable_name] = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
47
+ memo[variable_name] = if ctx.interpreter?
48
+ default_value
49
+ else
50
+ # Add the variable if it wasn't provided but it has a default value (including `null`)
51
+ GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
52
+ end
47
53
  end
48
54
  end
49
55
  rescue GraphQL::CoercionError, GraphQL::ExecutionError => ex
@@ -787,7 +787,7 @@ module GraphQL
787
787
  def_delegators :graphql_definition,
788
788
  # Execution
789
789
  :execution_strategy_for_operation,
790
- :validate, :multiplex_analyzers,
790
+ :validate,
791
791
  # Configuration
792
792
  :metadata, :redefine,
793
793
  :id_from_object_proc, :object_from_id_proc,
@@ -942,8 +942,24 @@ module GraphQL
942
942
  find_inherited_value(:types, EMPTY_HASH)[type_name]
943
943
  end
944
944
 
945
+ # @api private
946
+ attr_writer :connections
947
+
945
948
  # @return [GraphQL::Pagination::Connections] if installed
946
- attr_accessor :connections
949
+ def connections
950
+ if defined?(@connections)
951
+ @connections
952
+ else
953
+ inherited_connections = find_inherited_value(:connections, nil)
954
+ # This schema is part of an inheritance chain which is using new connections,
955
+ # make a new instance, so we don't pollute the upstream one.
956
+ if inherited_connections
957
+ @connections = Pagination::Connections.new(schema: self)
958
+ else
959
+ nil
960
+ end
961
+ end
962
+ end
947
963
 
948
964
  def new_connections?
949
965
  !!connections
@@ -42,7 +42,6 @@ module GraphQL
42
42
  end
43
43
  schema_definition = schema_defns.first
44
44
  types = {}
45
- types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
46
45
  directives = {}
47
46
  type_resolver = ->(type) { resolve_type(types, type) }
48
47
 
@@ -69,6 +68,21 @@ module GraphQL
69
68
  end
70
69
  end
71
70
 
71
+ # At this point, if types named by the built in types are _late-bound_ types,
72
+ # that means they were referenced in the schema but not defined in the schema.
73
+ # That's supported for built-in types. (Eg, you can use `String` without defining it.)
74
+ # In that case, insert the concrete type definition now.
75
+ #
76
+ # However, if the type in `types` is a _concrete_ type definition, that means that
77
+ # the document contained an explicit definition of the scalar type.
78
+ # Don't override it in this case.
79
+ GraphQL::Schema::BUILT_IN_TYPES.each do |scalar_name, built_in_scalar|
80
+ existing_type = types[scalar_name]
81
+ if existing_type.is_a?(GraphQL::Schema::LateBoundType)
82
+ types[scalar_name] = built_in_scalar
83
+ end
84
+ end
85
+
72
86
  directives = GraphQL::Schema.default_directives.merge(directives)
73
87
 
74
88
  if schema_definition
@@ -63,8 +63,11 @@ module GraphQL
63
63
  def enum_value_class(new_enum_value_class = nil)
64
64
  if new_enum_value_class
65
65
  @enum_value_class = new_enum_value_class
66
+ elsif defined?(@enum_value_class) && @enum_value_class
67
+ @enum_value_class
68
+ else
69
+ superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil
66
70
  end
67
- @enum_value_class || (superclass <= GraphQL::Schema::Enum ? superclass.enum_value_class : nil)
68
71
  end
69
72
 
70
73
  def kind
@@ -648,6 +648,17 @@ module GraphQL
648
648
  # Apply any `prepare` methods. Not great code organization, can this go somewhere better?
649
649
  arguments.each do |name, arg_defn|
650
650
  ruby_kwargs_key = arg_defn.keyword
651
+
652
+ loads = arg_defn.loads
653
+ if ruby_kwargs.key?(ruby_kwargs_key) && loads && !arg_defn.from_resolver?
654
+ value = ruby_kwargs[ruby_kwargs_key]
655
+ ruby_kwargs[ruby_kwargs_key] = if arg_defn.type.list?
656
+ value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
657
+ else
658
+ load_application_object(arg_defn, loads, value, field_ctx.query.context)
659
+ end
660
+ end
661
+
651
662
  if ruby_kwargs.key?(ruby_kwargs_key) && arg_defn.prepare
652
663
  ruby_kwargs[ruby_kwargs_key] = arg_defn.prepare_value(obj, ruby_kwargs[ruby_kwargs_key])
653
664
  end
@@ -38,7 +38,8 @@ module GraphQL
38
38
  value.max_page_size ||= field.max_page_size
39
39
  value
40
40
  elsif context.schema.new_connections?
41
- context.schema.connections.wrap(field, value, arguments, context)
41
+ wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
42
+ context.schema.connections.wrap(field, value, arguments, context, wrappers: wrappers)
42
43
  else
43
44
  if object.is_a?(GraphQL::Schema::Object)
44
45
  object = object.object
@@ -5,6 +5,7 @@ module GraphQL
5
5
  extend GraphQL::Schema::Member::AcceptsDefinition
6
6
  extend Forwardable
7
7
  extend GraphQL::Schema::Member::HasArguments
8
+ extend GraphQL::Schema::Member::HasArguments::ArgumentObjectLoader
8
9
  extend GraphQL::Schema::Member::ValidatesInput
9
10
 
10
11
  include GraphQL::Dig
@@ -23,19 +24,25 @@ module GraphQL
23
24
  self.class.arguments.each do |name, arg_defn|
24
25
  @arguments_by_keyword[arg_defn.keyword] = arg_defn
25
26
  ruby_kwargs_key = arg_defn.keyword
26
- loads = arg_defn.loads
27
-
28
- if @ruby_style_hash.key?(ruby_kwargs_key) && loads && !arg_defn.from_resolver?
29
- value = @ruby_style_hash[ruby_kwargs_key]
30
- @ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
31
- value.map { |val| load_application_object(arg_defn, loads, val) }
32
- else
33
- load_application_object(arg_defn, loads, value)
27
+
28
+ if @ruby_style_hash.key?(ruby_kwargs_key)
29
+ loads = arg_defn.loads
30
+ # Resolvers do this loading themselves;
31
+ # With the interpreter, it's done during `coerce_arguments`
32
+ if loads && !arg_defn.from_resolver? && !context.interpreter?
33
+ value = @ruby_style_hash[ruby_kwargs_key]
34
+ @ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
35
+ value.map { |val| load_application_object(arg_defn, loads, val, context) }
36
+ else
37
+ load_application_object(arg_defn, loads, value, context)
38
+ end
34
39
  end
35
- end
36
40
 
37
- if @ruby_style_hash.key?(ruby_kwargs_key) && arg_defn.prepare
38
- @ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
41
+ # Weirdly, procs are applied during coercion, but not methods.
42
+ # Probably because these methods require a `self`.
43
+ if arg_defn.prepare.is_a?(Symbol) || context.nil? || !context.interpreter?
44
+ @ruby_style_hash[ruby_kwargs_key] = arg_defn.prepare_value(self, @ruby_style_hash[ruby_kwargs_key])
45
+ end
39
46
  end
40
47
  end
41
48
  end
@@ -169,7 +176,7 @@ module GraphQL
169
176
  # Items in the input that are unexpected
170
177
  input.each do |name, value|
171
178
  if visible_arguments_map[name].nil?
172
- result.add_problem("Argument is not defined on #{self.graphql_name}", [name])
179
+ result.add_problem("Field is not defined on #{self.graphql_name}", [name])
173
180
  end
174
181
  end
175
182
 
@@ -188,32 +195,8 @@ module GraphQL
188
195
  if value.nil?
189
196
  return nil
190
197
  end
191
- input_values = {}
192
-
193
- arguments.each do |name, argument_defn|
194
- arg_key = argument_defn.keyword
195
- has_value = false
196
-
197
- # Accept either string or symbol
198
- field_value = if value.key?(name)
199
- has_value = true
200
- value[name]
201
- elsif value.key?(arg_key)
202
- has_value = true
203
- value[arg_key]
204
- elsif argument_defn.default_value?
205
- has_value = true
206
- argument_defn.default_value
207
- else
208
- nil
209
- end
210
- # Only continue if some value was found for this argument
211
- if has_value
212
- coerced_value = argument_defn.type.coerce_input(field_value, ctx)
213
- prepared_value = argument_defn.prepare_value(nil, coerced_value, context: ctx)
214
- input_values[arg_key] = prepared_value
215
- end
216
- end
198
+
199
+ input_values = coerce_arguments(nil, value, ctx)
217
200
 
218
201
  input_obj_instance = self.new(ruby_kwargs: input_values, context: ctx, defaults_used: nil)
219
202
  input_obj_instance.prepare
@@ -26,7 +26,7 @@ module GraphQL
26
26
  end
27
27
 
28
28
  def overridden_graphql_name
29
- @graphql_name
29
+ defined?(@graphql_name) ? @graphql_name : nil
30
30
  end
31
31
 
32
32
  # Just a convenience method to point out that people should use graphql_name instead
@@ -46,8 +46,10 @@ module GraphQL
46
46
  def description(new_description = nil)
47
47
  if new_description
48
48
  @description = new_description
49
- else
49
+ elsif defined?(@description)
50
50
  @description
51
+ else
52
+ nil
51
53
  end
52
54
  end
53
55
 
@@ -68,8 +70,10 @@ module GraphQL
68
70
  def introspection(new_introspection = nil)
69
71
  if !new_introspection.nil?
70
72
  @introspection = new_introspection
71
- else
73
+ elsif defined?(@introspection)
72
74
  @introspection
75
+ else
76
+ false
73
77
  end
74
78
  end
75
79
 
@@ -82,8 +86,11 @@ module GraphQL
82
86
  def mutation(mutation_class = nil)
83
87
  if mutation_class
84
88
  @mutation = mutation_class
89
+ elsif defined?(@mutation)
90
+ @mutation
91
+ else
92
+ nil
85
93
  end
86
- @mutation
87
94
  end
88
95
 
89
96
  # @return [GraphQL::BaseType] Convert this type to a legacy-style object.
@@ -63,12 +63,63 @@ module GraphQL
63
63
  self.class.argument_class(new_arg_class)
64
64
  end
65
65
 
66
+ # @param values [Hash<String, Object>]
67
+ # @param context [GraphQL::Query::Context]
68
+ # @return Hash<Symbol, Object>
69
+ def coerce_arguments(parent_object, values, context)
70
+ kwarg_arguments = {}
71
+ # Cache this hash to avoid re-merging it
72
+ arg_defns = self.arguments
73
+
74
+ arg_defns.each do |arg_name, arg_defn|
75
+ arg_key = arg_defn.keyword
76
+ has_value = false
77
+ if values.key?(arg_name)
78
+ has_value = true
79
+ value = values[arg_name]
80
+ elsif values.key?(arg_key)
81
+ has_value = true
82
+ value = values[arg_key]
83
+ elsif arg_defn.default_value?
84
+ has_value = true
85
+ value = arg_defn.default_value
86
+ end
87
+
88
+ if has_value
89
+ loads = arg_defn.loads
90
+ loaded_value = nil
91
+ if loads && !arg_defn.from_resolver?
92
+ loaded_value = if arg_defn.type.list?
93
+ value.map { |val| load_application_object(arg_defn, loads, val, context) }
94
+ else
95
+ load_application_object(arg_defn, loads, value, context)
96
+ end
97
+ end
98
+
99
+ prepared_value = context.schema.error_handler.with_error_handling(context) do
100
+
101
+ coerced_value = if loaded_value
102
+ loaded_value
103
+ else
104
+ arg_defn.type.coerce_input(value, context)
105
+ end
106
+
107
+ arg_defn.prepare_value(parent_object, coerced_value, context: context)
108
+ end
109
+ kwarg_arguments[arg_defn.keyword] = prepared_value
110
+ end
111
+ end
112
+ kwarg_arguments
113
+ end
114
+
66
115
  module ArgumentClassAccessor
67
116
  def argument_class(new_arg_class = nil)
68
117
  if new_arg_class
69
118
  @argument_class = new_arg_class
119
+ elsif defined?(@argument_class) && @argument_class
120
+ @argument_class
70
121
  else
71
- @argument_class || (superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument)
122
+ superclass.respond_to?(:argument_class) ? superclass.argument_class : GraphQL::Schema::Argument
72
123
  end
73
124
  end
74
125
  end
@@ -85,7 +136,7 @@ module GraphQL
85
136
  context.schema.object_from_id(id, context)
86
137
  end
87
138
 
88
- def load_application_object(argument, lookup_as_type, id)
139
+ def load_application_object(argument, lookup_as_type, id, context)
89
140
  # See if any object can be found for this ID
90
141
  loaded_application_object = object_from_id(lookup_as_type, id, context)
91
142
  context.schema.after_lazy(loaded_application_object) do |application_object|
@@ -8,8 +8,11 @@ module GraphQL
8
8
  def ast_node(new_ast_node = nil)
9
9
  if new_ast_node
10
10
  @ast_node = new_ast_node
11
+ elsif defined?(@ast_node)
12
+ @ast_node
13
+ else
14
+ nil
11
15
  end
12
- @ast_node
13
16
  end
14
17
  end
15
18
  end
@@ -70,7 +70,7 @@ module GraphQL
70
70
  def field_class(new_field_class = nil)
71
71
  if new_field_class
72
72
  @field_class = new_field_class
73
- elsif @field_class
73
+ elsif defined?(@field_class) && @field_class
74
74
  @field_class
75
75
  elsif self.is_a?(Class)
76
76
  superclass.respond_to?(:field_class) ? superclass.field_class : GraphQL::Schema::Field
@@ -285,7 +285,7 @@ module GraphQL
285
285
  argument = @arguments_by_keyword[:#{arg_defn.keyword}]
286
286
  lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
287
287
  context.schema.after_lazy(values) do |values2|
288
- GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value) })
288
+ GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
289
289
  end
290
290
  end
291
291
  RUBY
@@ -294,7 +294,7 @@ module GraphQL
294
294
  def load_#{arg_defn.keyword}(value)
295
295
  argument = @arguments_by_keyword[:#{arg_defn.keyword}]
296
296
  lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
297
- load_application_object(argument, lookup_as_type, value)
297
+ load_application_object(argument, lookup_as_type, value, context)
298
298
  end
299
299
  RUBY
300
300
  else
@@ -26,8 +26,10 @@ module GraphQL
26
26
  def field_class(new_class = nil)
27
27
  if new_class
28
28
  @field_class = new_class
29
+ elsif defined?(@field_class) && @field_class
30
+ @field_class
29
31
  else
30
- @field_class || find_inherited_value(:field_class, GraphQL::Schema::Field)
32
+ find_inherited_value(:field_class, GraphQL::Schema::Field)
31
33
  end
32
34
  end
33
35
 
@@ -230,6 +230,16 @@ module GraphQL
230
230
  unvisited_types << root_type if root_type
231
231
  end
232
232
  unvisited_types.concat(@schema.introspection_system.types.values)
233
+
234
+ directives.each do |dir_class|
235
+ dir_class.arguments.values.each do |arg_defn|
236
+ arg_t = arg_defn.type.unwrap
237
+ if get_type(arg_t.graphql_name)
238
+ unvisited_types << arg_t
239
+ end
240
+ end
241
+ end
242
+
233
243
  @schema.orphan_types.each do |orphan_type|
234
244
  if get_type(orphan_type.graphql_name)
235
245
  unvisited_types << orphan_type
@@ -27,7 +27,6 @@ module GraphQL
27
27
 
28
28
  if !valid
29
29
  error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
30
- VariableDefaultValuesAreCorrectlyTypedError
31
30
  add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
32
31
  error_message,
33
32
  nodes: node,
@@ -88,6 +88,10 @@ module GraphQL
88
88
  def execute(subscription_id, event, object)
89
89
  # Lookup the saved data for this subscription
90
90
  query_data = read_subscription(subscription_id)
91
+ if query_data.nil?
92
+ # Jump down to the `delete_subscription` call
93
+ raise GraphQL::Schema::Subscription::UnsubscribedError
94
+ end
91
95
  # Fetch the required keys from the saved data
92
96
  query_string = query_data.fetch(:query_string)
93
97
  variables = query_data.fetch(:variables)
@@ -95,14 +99,12 @@ module GraphQL
95
99
  operation_name = query_data.fetch(:operation_name)
96
100
  # Re-evaluate the saved query
97
101
  result = @schema.execute(
98
- **{
99
- query: query_string,
100
- context: context,
101
- subscription_topic: event.topic,
102
- operation_name: operation_name,
103
- variables: variables,
104
- root_value: object,
105
- }
102
+ query: query_string,
103
+ context: context,
104
+ subscription_topic: event.topic,
105
+ operation_name: operation_name,
106
+ variables: variables,
107
+ root_value: object,
106
108
  )
107
109
  deliver(subscription_id, result)
108
110
  rescue GraphQL::Schema::Subscription::NoUpdateError
@@ -226,7 +228,10 @@ module GraphQL
226
228
  # match query arguments.
227
229
  arg_owner.arguments.each do |name, arg_defn|
228
230
  if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
229
- normalized_args[arg_defn.name] = arg_defn.default_value
231
+ default_value = arg_defn.default_value
232
+ # We don't have an underlying "object" here, so it can't call methods.
233
+ # This is broken.
234
+ normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: GraphQL::Query::NullContext)
230
235
  end
231
236
  end
232
237