graphql 1.10.2 → 1.10.3

Sign up to get free protection for your applications and to get access to all the features.
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