graphql 1.12.6 → 1.12.11

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.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +1 -1
  3. data/lib/generators/graphql/templates/graphql_controller.erb +2 -2
  4. data/lib/graphql.rb +10 -10
  5. data/lib/graphql/backtrace/table.rb +14 -2
  6. data/lib/graphql/dataloader.rb +44 -15
  7. data/lib/graphql/execution/errors.rb +109 -11
  8. data/lib/graphql/execution/execute.rb +1 -1
  9. data/lib/graphql/execution/interpreter.rb +4 -8
  10. data/lib/graphql/execution/interpreter/runtime.rb +207 -188
  11. data/lib/graphql/introspection.rb +1 -1
  12. data/lib/graphql/introspection/directive_type.rb +7 -3
  13. data/lib/graphql/introspection/schema_type.rb +1 -1
  14. data/lib/graphql/pagination/active_record_relation_connection.rb +7 -0
  15. data/lib/graphql/pagination/connections.rb +1 -1
  16. data/lib/graphql/pagination/relation_connection.rb +8 -1
  17. data/lib/graphql/query.rb +1 -3
  18. data/lib/graphql/query/null_context.rb +7 -1
  19. data/lib/graphql/query/validation_pipeline.rb +1 -1
  20. data/lib/graphql/rake_task.rb +3 -0
  21. data/lib/graphql/schema.rb +49 -237
  22. data/lib/graphql/schema/addition.rb +238 -0
  23. data/lib/graphql/schema/argument.rb +55 -36
  24. data/lib/graphql/schema/directive/transform.rb +13 -1
  25. data/lib/graphql/schema/input_object.rb +2 -2
  26. data/lib/graphql/schema/loader.rb +8 -0
  27. data/lib/graphql/schema/member/base_dsl_methods.rb +3 -15
  28. data/lib/graphql/schema/object.rb +19 -5
  29. data/lib/graphql/schema/resolver.rb +46 -24
  30. data/lib/graphql/schema/scalar.rb +3 -1
  31. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +3 -1
  32. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
  33. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
  34. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  35. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  36. data/lib/graphql/static_validation/rules/fields_will_merge.rb +17 -8
  37. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
  38. data/lib/graphql/static_validation/validator.rb +5 -0
  39. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +4 -3
  40. data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
  41. data/lib/graphql/subscriptions/serialize.rb +11 -1
  42. data/lib/graphql/types/relay/base_connection.rb +4 -0
  43. data/lib/graphql/types/relay/connection_behaviors.rb +21 -10
  44. data/lib/graphql/types/relay/edge_behaviors.rb +12 -1
  45. data/lib/graphql/version.rb +1 -1
  46. metadata +3 -3
  47. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
@@ -333,30 +333,32 @@ module GraphQL
333
333
  arg_defn = super(*args, from_resolver: true, **kwargs)
334
334
  own_arguments_loads_as_type[arg_defn.keyword] = loads if loads
335
335
 
336
- if loads && arg_defn.type.list?
337
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
338
- def load_#{arg_defn.keyword}(values)
339
- argument = @arguments_by_keyword[:#{arg_defn.keyword}]
340
- lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
341
- context.schema.after_lazy(values) do |values2|
342
- GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
336
+ if !method_defined?(:"load_#{arg_defn.keyword}")
337
+ if loads && arg_defn.type.list?
338
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
339
+ def load_#{arg_defn.keyword}(values)
340
+ argument = @arguments_by_keyword[:#{arg_defn.keyword}]
341
+ lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
342
+ context.schema.after_lazy(values) do |values2|
343
+ GraphQL::Execution::Lazy.all(values2.map { |value| load_application_object(argument, lookup_as_type, value, context) })
344
+ end
343
345
  end
346
+ RUBY
347
+ elsif loads
348
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
349
+ def load_#{arg_defn.keyword}(value)
350
+ argument = @arguments_by_keyword[:#{arg_defn.keyword}]
351
+ lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
352
+ load_application_object(argument, lookup_as_type, value, context)
353
+ end
354
+ RUBY
355
+ else
356
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
357
+ def load_#{arg_defn.keyword}(value)
358
+ value
359
+ end
360
+ RUBY
344
361
  end
345
- RUBY
346
- elsif loads
347
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
348
- def load_#{arg_defn.keyword}(value)
349
- argument = @arguments_by_keyword[:#{arg_defn.keyword}]
350
- lookup_as_type = @arguments_loads_as_type[:#{arg_defn.keyword}]
351
- load_application_object(argument, lookup_as_type, value, context)
352
- end
353
- RUBY
354
- else
355
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
356
- def load_#{arg_defn.keyword}(value)
357
- value
358
- end
359
- RUBY
360
362
  end
361
363
 
362
364
  arg_defn
@@ -372,16 +374,36 @@ module GraphQL
372
374
  # @param extension [Class] Extension class
373
375
  # @param options [Hash] Optional extension options
374
376
  def extension(extension, **options)
375
- extensions << {extension => options}
377
+ @own_extensions ||= []
378
+ @own_extensions << {extension => options}
376
379
  end
377
380
 
378
381
  # @api private
379
382
  def extensions
380
- @extensions ||= []
383
+ own_exts = @own_extensions
384
+ # Jump through some hoops to avoid creating arrays when we don't actually need them
385
+ if superclass.respond_to?(:extensions)
386
+ s_exts = superclass.extensions
387
+ if own_exts
388
+ if s_exts.any?
389
+ own_exts + s_exts
390
+ else
391
+ own_exts
392
+ end
393
+ else
394
+ s_exts
395
+ end
396
+ else
397
+ own_exts || EMPTY_ARRAY
398
+ end
381
399
  end
382
400
 
383
401
  private
384
402
 
403
+ def own_extensions
404
+ @own_extensions
405
+ end
406
+
385
407
  def own_arguments_loads_as_type
386
408
  @own_arguments_loads_as_type ||= {}
387
409
  end
@@ -44,7 +44,9 @@ module GraphQL
44
44
  def validate_non_null_input(value, ctx)
45
45
  result = Query::InputValidationResult.new
46
46
  coerced_result = begin
47
- coerce_input(value, ctx)
47
+ ctx.query.with_error_handling do
48
+ coerce_input(value, ctx)
49
+ end
48
50
  rescue GraphQL::CoercionError => err
49
51
  err
50
52
  end
@@ -41,7 +41,9 @@ module GraphQL
41
41
  error_options = {
42
42
  nodes: parent,
43
43
  type: kind_of_node,
44
- argument: node.name
44
+ argument_name: node.name,
45
+ argument: arg_defn,
46
+ value: node.value
45
47
  }
46
48
  if coerce_extensions
47
49
  error_options[:coerce_extensions] = coerce_extensions
@@ -4,13 +4,17 @@ module GraphQL
4
4
  class ArgumentLiteralsAreCompatibleError < StaticValidation::Error
5
5
  attr_reader :type_name
6
6
  attr_reader :argument_name
7
+ attr_reader :argument
8
+ attr_reader :value
7
9
 
8
- def initialize(message, path: nil, nodes: [], type:, argument: nil, extensions: nil, coerce_extensions: nil)
10
+ def initialize(message, path: nil, nodes: [], type:, argument_name: nil, extensions: nil, coerce_extensions: nil, argument: nil, value: nil)
9
11
  super(message, path: path, nodes: nodes)
10
12
  @type_name = type
11
- @argument_name = argument
13
+ @argument_name = argument_name
12
14
  @extensions = extensions
13
15
  @coerce_extensions = coerce_extensions
16
+ @argument = argument
17
+ @value = value
14
18
  end
15
19
 
16
20
  # A hash representation of this Message
@@ -15,7 +15,8 @@ module GraphQL
15
15
  nodes: node,
16
16
  name: error_arg_name,
17
17
  type: kind_of_node,
18
- argument: node.name
18
+ argument_name: node.name,
19
+ parent: parent_defn
19
20
  ))
20
21
  else
21
22
  # Some other weird error
@@ -5,12 +5,14 @@ module GraphQL
5
5
  attr_reader :name
6
6
  attr_reader :type_name
7
7
  attr_reader :argument_name
8
+ attr_reader :parent
8
9
 
9
- def initialize(message, path: nil, nodes: [], name:, type:, argument:)
10
+ def initialize(message, path: nil, nodes: [], name:, type:, argument_name:, parent:)
10
11
  super(message, path: path, nodes: nodes)
11
12
  @name = name
12
13
  @type_name = type
13
- @argument_name = argument
14
+ @argument_name = argument_name
15
+ @parent = parent
14
16
  end
15
17
 
16
18
  # A hash representation of this Message
@@ -4,7 +4,7 @@ module GraphQL
4
4
  module DirectivesAreDefined
5
5
  def initialize(*)
6
6
  super
7
- @directive_names = context.schema.directives.keys
7
+ @directive_names = context.warden.directives.map(&:graphql_name)
8
8
  end
9
9
 
10
10
  def on_directive(node, parent)
@@ -373,17 +373,26 @@ module GraphQL
373
373
  # In this context, `parents` represends the "self scope" of the field,
374
374
  # what types may be found at this point in the query.
375
375
  def mutually_exclusive?(parents1, parents2)
376
- parents1.each do |type1|
377
- parents2.each do |type2|
378
- # If the types we're comparing are both different object types,
379
- # they have to be mutually exclusive.
380
- if type1 != type2 && type1.kind.object? && type2.kind.object?
381
- return true
376
+ if parents1.empty? || parents2.empty?
377
+ false
378
+ elsif parents1.length == parents2.length
379
+ parents1.length.times.any? do |i|
380
+ type1 = parents1[i - 1]
381
+ type2 = parents2[i - 1]
382
+ if type1 == type2
383
+ # If the types we're comparing are the same type,
384
+ # then they aren't mutually exclusive
385
+ false
386
+ else
387
+ # Check if these two scopes have _any_ types in common.
388
+ possible_right_types = context.query.possible_types(type1)
389
+ possible_left_types = context.query.possible_types(type2)
390
+ (possible_right_types & possible_left_types).empty?
382
391
  end
383
392
  end
393
+ else
394
+ true
384
395
  end
385
-
386
- false
387
396
  end
388
397
  end
389
398
  end
@@ -23,7 +23,7 @@ module GraphQL
23
23
  defn = if arg_defn && arg_defn.type.unwrap.kind.input_object?
24
24
  arg_defn.type.unwrap
25
25
  else
26
- context.field_definition
26
+ context.directive_definition || context.field_definition
27
27
  end
28
28
 
29
29
  parent_type = context.warden.get_argument(defn, parent_name(parent, defn))
@@ -73,6 +73,11 @@ module GraphQL
73
73
  irep: irep,
74
74
  }
75
75
  end
76
+ rescue GraphQL::ExecutionError => e
77
+ {
78
+ errors: [e],
79
+ irep: nil,
80
+ }
76
81
  end
77
82
 
78
83
  # Invoked when static validation times out.
@@ -34,12 +34,12 @@ module GraphQL
34
34
  # channel: self,
35
35
  # }
36
36
  #
37
- # result = MySchema.execute({
37
+ # result = MySchema.execute(
38
38
  # query: query,
39
39
  # context: context,
40
40
  # variables: variables,
41
41
  # operation_name: operation_name
42
- # })
42
+ # )
43
43
  #
44
44
  # payload = {
45
45
  # result: result.to_h,
@@ -146,14 +146,15 @@ module GraphQL
146
146
  def setup_stream(channel, initial_event)
147
147
  topic = initial_event.topic
148
148
  channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
149
- object = @serializer.load(message)
150
149
  events_by_fingerprint = @events[topic]
150
+ object = nil
151
151
  events_by_fingerprint.each do |_fingerprint, events|
152
152
  if events.any? && events.first == initial_event
153
153
  # The fingerprint has told us that this response should be shared by all subscribers,
154
154
  # so just run it once, then deliver the result to every subscriber
155
155
  first_event = events.first
156
156
  first_subscription_id = first_event.context.fetch(:subscription_id)
157
+ object ||= @serializer.load(message)
157
158
  result = execute_update(first_subscription_id, first_event, object)
158
159
  # Having calculated the result _once_, send the same payload to all subscribers
159
160
  events.each do |event|
@@ -35,9 +35,6 @@ module GraphQL
35
35
  pt = @query.possible_types(current_type)
36
36
  pt.each do |object_type|
37
37
  ot_field = @query.get_field(object_type, current_field.graphql_name)
38
- if !ot_field
39
- binding.pry
40
- end
41
38
  # Inherited fields would be exactly the same object;
42
39
  # only check fields that are overrides of the inherited one
43
40
  if ot_field && ot_field != current_field
@@ -55,7 +55,14 @@ module GraphQL
55
55
  # @return [Object] An object that load Global::Identification recursive
56
56
  def load_value(value)
57
57
  if value.is_a?(Array)
58
- value.map{|item| load_value(item)}
58
+ is_gids = (v1 = value[0]).is_a?(Hash) && v1.size == 1 && v1[GLOBALID_KEY]
59
+ if is_gids
60
+ # Assume it's an array of global IDs
61
+ ids = value.map { |v| v[GLOBALID_KEY] }
62
+ GlobalID::Locator.locate_many(ids)
63
+ else
64
+ value.map { |item| load_value(item) }
65
+ end
59
66
  elsif value.is_a?(Hash)
60
67
  if value.size == 1
61
68
  case value.keys.first # there's only 1 key
@@ -70,6 +77,9 @@ module GraphQL
70
77
  when OPEN_STRUCT_KEY
71
78
  ostruct_values = load_value(value[OPEN_STRUCT_KEY])
72
79
  OpenStruct.new(ostruct_values)
80
+ else
81
+ key = value.keys.first
82
+ { key => load_value(value[key]) }
73
83
  end
74
84
  else
75
85
  loaded_h = {}
@@ -24,6 +24,10 @@ module GraphQL
24
24
  # end
25
25
  # class Types::PostConnection < Types::BaseConnection
26
26
  # edge_type(Types::PostEdge)
27
+ # edges_nullable(true)
28
+ # edge_nullable(true)
29
+ # node_nullable(true)
30
+ # has_nodes_field(true)
27
31
  # end
28
32
  #
29
33
  # @see Relay::BaseEdge for edge types
@@ -11,6 +11,7 @@ module GraphQL
11
11
  child_class.extend(ClassMethods)
12
12
  child_class.extend(Relay::DefaultRelay)
13
13
  child_class.default_relay(true)
14
+ child_class.has_nodes_field(true)
14
15
  child_class.node_nullable(true)
15
16
  child_class.edges_nullable(true)
16
17
  child_class.edge_nullable(true)
@@ -34,7 +35,7 @@ module GraphQL
34
35
  # It's called when you subclass this base connection, trying to use the
35
36
  # class name to set defaults. You can call it again in the class definition
36
37
  # to override the default (or provide a value, if the default lookup failed).
37
- def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: true, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable)
38
+ def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: self.has_nodes_field, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable)
38
39
  # Set this connection's graphql name
39
40
  node_type_name = node_type.graphql_name
40
41
 
@@ -79,9 +80,9 @@ module GraphQL
79
80
  # Use `node_nullable(false)` in your base class to make non-null `node` and `nodes` fields.
80
81
  def node_nullable(new_value = nil)
81
82
  if new_value.nil?
82
- @node_nullable || superclass.node_nullable
83
+ defined?(@node_nullable) ? @node_nullable : superclass.node_nullable
83
84
  else
84
- @node_nullable ||= new_value
85
+ @node_nullable = new_value
85
86
  end
86
87
  end
87
88
 
@@ -89,21 +90,31 @@ module GraphQL
89
90
  # Use `edges_nullable(false)` in your base class to make non-null `edges` fields.
90
91
  def edges_nullable(new_value = nil)
91
92
  if new_value.nil?
92
- @edges_nullable || superclass.edges_nullable
93
+ defined?(@edges_nullable) ? @edges_nullable : superclass.edges_nullable
93
94
  else
94
- @edges_nullable ||= new_value
95
+ @edges_nullable = new_value
95
96
  end
96
- end
97
-
97
+ end
98
+
98
99
  # Set the default `edge_nullable` for this class and its child classes. (Defaults to `true`.)
99
100
  # Use `edge_nullable(false)` in your base class to make non-null `edge` fields.
100
101
  def edge_nullable(new_value = nil)
101
102
  if new_value.nil?
102
- @edge_nullable || superclass.edge_nullable
103
+ defined?(@edge_nullable) ? @edge_nullable : superclass.edge_nullable
104
+ else
105
+ @edge_nullable = new_value
106
+ end
107
+ end
108
+
109
+ # Set the default `nodes_field` for this class and its child classes. (Defaults to `true`.)
110
+ # Use `nodes_field(false)` in your base class to prevent adding of a nodes field.
111
+ def has_nodes_field(new_value = nil)
112
+ if new_value.nil?
113
+ defined?(@nodes_field) ? @nodes_field : superclass.has_nodes_field
103
114
  else
104
- @edge_nullable ||= new_value
115
+ @nodes_field = new_value
105
116
  end
106
- end
117
+ end
107
118
 
108
119
  private
109
120
 
@@ -8,6 +8,7 @@ module GraphQL
8
8
  child_class.description("An edge in a connection.")
9
9
  child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.")
10
10
  child_class.extend(ClassMethods)
11
+ child_class.node_nullable(true)
11
12
  end
12
13
 
13
14
  module ClassMethods
@@ -15,7 +16,7 @@ module GraphQL
15
16
  #
16
17
  # @param node_type [Class] A `Schema::Object` subclass
17
18
  # @param null [Boolean]
18
- def node_type(node_type = nil, null: true)
19
+ def node_type(node_type = nil, null: self.node_nullable)
19
20
  if node_type
20
21
  @node_type = node_type
21
22
  # Add a default `node` field
@@ -35,6 +36,16 @@ module GraphQL
35
36
  def visible?(ctx)
36
37
  node_type.visible?(ctx)
37
38
  end
39
+
40
+ # Set the default `node_nullable` for this class and its child classes. (Defaults to `true`.)
41
+ # Use `node_nullable(false)` in your base class to make non-null `node` field.
42
+ def node_nullable(new_value = nil)
43
+ if new_value.nil?
44
+ defined?(@node_nullable) ? @node_nullable : superclass.node_nullable
45
+ else
46
+ @node_nullable = new_value
47
+ end
48
+ end
38
49
  end
39
50
  end
40
51
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.12.6"
3
+ VERSION = "1.12.11"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.6
4
+ version: 1.12.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-11 00:00:00.000000000 Z
11
+ date: 2021-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -369,7 +369,6 @@ files:
369
369
  - lib/graphql/execution/interpreter/arguments_cache.rb
370
370
  - lib/graphql/execution/interpreter/execution_errors.rb
371
371
  - lib/graphql/execution/interpreter/handles_raw_value.rb
372
- - lib/graphql/execution/interpreter/hash_response.rb
373
372
  - lib/graphql/execution/interpreter/resolve.rb
374
373
  - lib/graphql/execution/interpreter/runtime.rb
375
374
  - lib/graphql/execution/lazy.rb
@@ -485,6 +484,7 @@ files:
485
484
  - lib/graphql/runtime_type_error.rb
486
485
  - lib/graphql/scalar_type.rb
487
486
  - lib/graphql/schema.rb
487
+ - lib/graphql/schema/addition.rb
488
488
  - lib/graphql/schema/argument.rb
489
489
  - lib/graphql/schema/base_64_bp.rb
490
490
  - lib/graphql/schema/base_64_encoder.rb