graphql 1.12.20 → 1.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/generators/graphql/mutation_generator.rb +1 -1
  5. data/lib/generators/graphql/object_generator.rb +2 -1
  6. data/lib/generators/graphql/templates/schema.erb +2 -2
  7. data/lib/generators/graphql/type_generator.rb +0 -1
  8. data/lib/graphql/analysis/ast/field_usage.rb +2 -2
  9. data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
  10. data/lib/graphql/analysis/ast/visitor.rb +4 -4
  11. data/lib/graphql/backtrace/table.rb +1 -1
  12. data/lib/graphql/base_type.rb +4 -2
  13. data/lib/graphql/boolean_type.rb +1 -1
  14. data/lib/graphql/dataloader.rb +55 -22
  15. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  16. data/lib/graphql/directive/include_directive.rb +1 -1
  17. data/lib/graphql/directive/skip_directive.rb +1 -1
  18. data/lib/graphql/directive.rb +0 -4
  19. data/lib/graphql/enum_type.rb +5 -1
  20. data/lib/graphql/execution/errors.rb +1 -0
  21. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  22. data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
  23. data/lib/graphql/execution/interpreter/runtime.rb +30 -18
  24. data/lib/graphql/execution/lookahead.rb +2 -2
  25. data/lib/graphql/execution/multiplex.rb +4 -1
  26. data/lib/graphql/float_type.rb +1 -1
  27. data/lib/graphql/id_type.rb +1 -1
  28. data/lib/graphql/int_type.rb +1 -1
  29. data/lib/graphql/introspection/directive_type.rb +1 -1
  30. data/lib/graphql/introspection/entry_points.rb +2 -2
  31. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  32. data/lib/graphql/introspection/field_type.rb +2 -2
  33. data/lib/graphql/introspection/input_value_type.rb +4 -4
  34. data/lib/graphql/introspection/schema_type.rb +2 -2
  35. data/lib/graphql/introspection/type_type.rb +10 -10
  36. data/lib/graphql/language/block_string.rb +2 -6
  37. data/lib/graphql/language/document_from_schema_definition.rb +4 -2
  38. data/lib/graphql/language/lexer.rb +0 -3
  39. data/lib/graphql/language/lexer.rl +0 -4
  40. data/lib/graphql/language/nodes.rb +12 -2
  41. data/lib/graphql/language/parser.rb +442 -434
  42. data/lib/graphql/language/parser.y +5 -4
  43. data/lib/graphql/language/printer.rb +6 -1
  44. data/lib/graphql/language/sanitized_printer.rb +5 -5
  45. data/lib/graphql/language/token.rb +0 -4
  46. data/lib/graphql/name_validator.rb +0 -4
  47. data/lib/graphql/query/arguments.rb +1 -1
  48. data/lib/graphql/query/arguments_cache.rb +1 -1
  49. data/lib/graphql/query/context.rb +15 -2
  50. data/lib/graphql/query/literal_input.rb +1 -1
  51. data/lib/graphql/query/null_context.rb +12 -7
  52. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  53. data/lib/graphql/query/variables.rb +5 -1
  54. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  55. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  56. data/lib/graphql/relay/page_info.rb +1 -1
  57. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  58. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  59. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  60. data/lib/graphql/rubocop.rb +4 -0
  61. data/lib/graphql/schema/addition.rb +37 -28
  62. data/lib/graphql/schema/argument.rb +8 -6
  63. data/lib/graphql/schema/build_from_definition.rb +5 -5
  64. data/lib/graphql/schema/directive/feature.rb +1 -1
  65. data/lib/graphql/schema/directive/flagged.rb +2 -2
  66. data/lib/graphql/schema/directive/include.rb +1 -1
  67. data/lib/graphql/schema/directive/skip.rb +1 -1
  68. data/lib/graphql/schema/directive/transform.rb +1 -1
  69. data/lib/graphql/schema/directive.rb +7 -3
  70. data/lib/graphql/schema/enum.rb +60 -10
  71. data/lib/graphql/schema/enum_value.rb +6 -0
  72. data/lib/graphql/schema/field/connection_extension.rb +1 -1
  73. data/lib/graphql/schema/field.rb +130 -39
  74. data/lib/graphql/schema/field_extension.rb +52 -2
  75. data/lib/graphql/schema/find_inherited_value.rb +1 -0
  76. data/lib/graphql/schema/finder.rb +5 -5
  77. data/lib/graphql/schema/input_object.rb +8 -5
  78. data/lib/graphql/schema/interface.rb +11 -20
  79. data/lib/graphql/schema/introspection_system.rb +1 -1
  80. data/lib/graphql/schema/list.rb +3 -1
  81. data/lib/graphql/schema/member/accepts_definition.rb +15 -3
  82. data/lib/graphql/schema/member/build_type.rb +0 -4
  83. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  84. data/lib/graphql/schema/member/has_arguments.rb +55 -13
  85. data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
  86. data/lib/graphql/schema/member/has_fields.rb +76 -18
  87. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  88. data/lib/graphql/schema/member.rb +1 -0
  89. data/lib/graphql/schema/non_null.rb +3 -1
  90. data/lib/graphql/schema/object.rb +10 -75
  91. data/lib/graphql/schema/printer.rb +1 -1
  92. data/lib/graphql/schema/relay_classic_mutation.rb +29 -3
  93. data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
  94. data/lib/graphql/schema/resolver.rb +19 -5
  95. data/lib/graphql/schema/scalar.rb +2 -0
  96. data/lib/graphql/schema/subscription.rb +11 -1
  97. data/lib/graphql/schema/traversal.rb +1 -1
  98. data/lib/graphql/schema/type_expression.rb +1 -1
  99. data/lib/graphql/schema/type_membership.rb +18 -4
  100. data/lib/graphql/schema/union.rb +8 -1
  101. data/lib/graphql/schema/validator/format_validator.rb +3 -5
  102. data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
  103. data/lib/graphql/schema/validator.rb +4 -7
  104. data/lib/graphql/schema/warden.rb +116 -52
  105. data/lib/graphql/schema.rb +106 -22
  106. data/lib/graphql/static_validation/base_visitor.rb +5 -5
  107. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  108. data/lib/graphql/static_validation/literal_validator.rb +1 -1
  109. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  110. data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
  111. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
  112. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
  113. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
  114. data/lib/graphql/string_type.rb +1 -1
  115. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
  116. data/lib/graphql/subscriptions/event.rb +20 -12
  117. data/lib/graphql/subscriptions.rb +17 -19
  118. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  119. data/lib/graphql/types/relay/default_relay.rb +5 -1
  120. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  121. data/lib/graphql/types/relay/has_node_field.rb +1 -1
  122. data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
  123. data/lib/graphql/version.rb +1 -1
  124. data/lib/graphql.rb +10 -32
  125. metadata +10 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8312601f8e51973aaa9418c4c4691b6eab97140351c91d47f6c2c4f4deb88fb
4
- data.tar.gz: 4cb698ef0a6739ceca4026b4dfaa594faf5658ac10a6005b051b70ec34af7f41
3
+ metadata.gz: 4f950da72b7fb585a9135dc893b2c2cf41c8a6482a82a0f7401ba913d48c643f
4
+ data.tar.gz: 8d5cd615133ca107e38ab18ccc59902c6eaa1f8d7ffeef5c7af474f1e4c5cde9
5
5
  SHA512:
6
- metadata.gz: e9122ecfa0c4c28d219af2c828adb1b5e7e0ae29b10df55f543cbfa8973d8d9706ba5258c3de4f620b6cba5f16d6e6e031ebb6605193153ce5be821eaeb1adaa
7
- data.tar.gz: 4db8f3435262d72f1d0d1995407f83c76a55dc0b5f28f7b5d8c6c4a378b2da00e4119e371585bdfac847ae5661c6e91e3bfdfcde82cd232198456905dee1f1a0
6
+ metadata.gz: 961ac5fe60ef45ea3eed645bcbef7793363dabe18d676c739188430b95e86887470d93966eccdfe89f0bebe99e0c4e5cfa68b5e697865d8228713a080d28f98d
7
+ data.tar.gz: a545feab4e634fc1039c232c43fe45412382b44215fe4038211e19a9fcef44dd02bb0db22eda2c3f78553867b7c3aed1b7630947a83d72741431e6fe2caeffdf
@@ -19,7 +19,9 @@ module Graphql
19
19
  sentinel = /< GraphQL::Schema\s*\n/m
20
20
 
21
21
  in_root do
22
- inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false
22
+ if File.exist?(schema_file_path)
23
+ inject_into_file schema_file_path, " #{type}(Types::#{name})\n", after: sentinel, verbose: false, force: false
24
+ end
23
25
  end
24
26
  end
25
27
 
@@ -122,8 +122,15 @@ module Graphql
122
122
  if options.api?
123
123
  say("Skipped graphiql, as this rails project is API only")
124
124
  say(" You may wish to use GraphiQL.app for development: https://github.com/skevy/graphiql-app")
125
- elsif !options[:skip_graphiql] && !File.read(Rails.root.join("Gemfile")).include?("graphiql-rails")
126
- gem("graphiql-rails", group: :development)
125
+ elsif !options[:skip_graphiql]
126
+ # `gem(...)` uses `gsub_file(...)` under the hood, which is a no-op for `rails destroy...` (when `behavior == :revoke`).
127
+ # So handle that case by calling `gsub_file` with `force: true`.
128
+ if behavior == :invoke && !File.read(Rails.root.join("Gemfile")).include?("graphiql-rails")
129
+ gem("graphiql-rails", group: :development)
130
+ elsif behavior == :revoke
131
+ gemfile_pattern = /\n\s*gem ('|")graphiql-rails('|"), :?group(:| =>) :development/
132
+ gsub_file Rails.root.join("Gemfile"), gemfile_pattern, "", { force: true }
133
+ end
127
134
 
128
135
  # This is a little cheat just to get cleaner shell output:
129
136
  log :route, 'graphiql-rails'
@@ -17,7 +17,7 @@ module Graphql
17
17
 
18
18
  argument :name, type: :string
19
19
 
20
- def initialize(args, *options) #:nodoc:
20
+ def initialize(args, *options) # :nodoc:
21
21
  # Unfreeze name in case it's given as a frozen string
22
22
  args[0] = args[0].dup if args[0].is_a?(String) && args[0].frozen?
23
23
  super
@@ -12,7 +12,8 @@ module Graphql
12
12
  #
13
13
  # Add the Node interface with `--node`.
14
14
  class ObjectGenerator < TypeGeneratorBase
15
- desc "Create a GraphQL::ObjectType with the given name and fields"
15
+ desc "Create a GraphQL::ObjectType with the given name and fields." \
16
+ "If the given type name matches an existing ActiveRecord model, the generated type will automatically include fields for the models database columns."
16
17
  source_root File.expand_path('../templates', __FILE__)
17
18
 
18
19
  argument :custom_fields,
@@ -9,7 +9,7 @@ class <%= schema_name %> < GraphQL::Schema
9
9
  use GraphQL::Dataloader
10
10
  <% end %>
11
11
  # GraphQL-Ruby calls this when something goes wrong while running a query:
12
- def self.type_error(err)
12
+ def self.type_error(err, context)
13
13
  # if err.is_a?(GraphQL::InvalidNullError)
14
14
  # # report to your bug tracker here
15
15
  # return nil
@@ -19,7 +19,7 @@ class <%= schema_name %> < GraphQL::Schema
19
19
 
20
20
  # Union and Interface Resolution
21
21
  def self.resolve_type(abstract_type, obj, ctx)
22
- # TODO: Implement this function
22
+ # TODO: Implement this method
23
23
  # to return the correct GraphQL object type for `obj`
24
24
  raise(GraphQL::RequiredImplementationMissingError)
25
25
  end
@@ -13,7 +13,6 @@ module Graphql
13
13
 
14
14
  argument :type_name,
15
15
  type: :string,
16
- required: true,
17
16
  banner: "TypeName",
18
17
  desc: "Name of this object type (expressed as Ruby or GraphQL)"
19
18
 
@@ -36,12 +36,12 @@ module GraphQL
36
36
  end
37
37
 
38
38
  if argument.definition.type.kind.input_object?
39
- extract_deprecated_arguments(argument.value.arguments.argument_values)
39
+ extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
40
40
  elsif argument.definition.type.list? && !argument.value.nil?
41
41
  argument
42
42
  .value
43
43
  .select { |value| value.respond_to?(:arguments) }
44
- .each { |value| extract_deprecated_arguments(value.arguments.argument_values) }
44
+ .each { |value| extract_deprecated_arguments(value.arguments.argument_values) } # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
45
45
  end
46
46
  end
47
47
  end
@@ -23,18 +23,22 @@ module GraphQL
23
23
 
24
24
  attr_reader :field_definition, :response_path, :query
25
25
 
26
- # @param node [Language::Nodes::Field] The AST node; used for providing argument values when necessary
26
+ # @param parent_type [Class] The owner of `field_definition`
27
27
  # @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
28
28
  # @param query [GraphQL::Query] Used for `query.possible_types`
29
29
  # @param response_path [Array<String>] The path to the response key for the field
30
- def initialize(node, field_definition, query, response_path)
31
- @node = node
30
+ def initialize(parent_type, field_definition, query, response_path)
31
+ @parent_type = parent_type
32
32
  @field_definition = field_definition
33
33
  @query = query
34
34
  @response_path = response_path
35
35
  @scoped_children = nil
36
+ @nodes = []
36
37
  end
37
38
 
39
+ # @return [Array<GraphQL::Language::Nodes::Field>]
40
+ attr_reader :nodes
41
+
38
42
  # Returns true if this field has no selections, ie, it's a scalar.
39
43
  # We need a quick way to check whether we should continue traversing.
40
44
  def terminal?
@@ -50,16 +54,7 @@ module GraphQL
50
54
  end
51
55
 
52
56
  def own_complexity(child_complexity)
53
- defined_complexity = @field_definition.complexity
54
- case defined_complexity
55
- when Proc
56
- arguments = @query.arguments_for(@node, @field_definition)
57
- defined_complexity.call(@query.context, arguments.keyword_arguments, child_complexity)
58
- when Numeric
59
- defined_complexity + child_complexity
60
- else
61
- raise("Invalid complexity: #{defined_complexity.inspect} on #{@field_definition.name}")
62
- end
57
+ @field_definition.calculate_complexity(query: @query, nodes: @nodes, child_complexity: child_complexity)
63
58
  end
64
59
  end
65
60
 
@@ -79,7 +74,8 @@ module GraphQL
79
74
  # then the query would have been rejected as invalid.
80
75
  complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
81
76
 
82
- complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(node, visitor.field_definition, visitor.query, visitor.response_path)
77
+ complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(parent_type, visitor.field_definition, visitor.query, visitor.response_path)
78
+ complexity.nodes.push(node)
83
79
  # Push it on the stack.
84
80
  complexities_on_type.push(complexity)
85
81
  end
@@ -100,7 +100,7 @@ module GraphQL
100
100
  def on_field(node, parent)
101
101
  @response_path.push(node.alias || node.name)
102
102
  parent_type = @object_types.last
103
- field_definition = @schema.get_field(parent_type, node.name)
103
+ field_definition = @schema.get_field(parent_type, node.name, @query.context)
104
104
  @field_definitions.push(field_definition)
105
105
  if !field_definition.nil?
106
106
  next_object_type = field_definition.type.unwrap
@@ -138,14 +138,14 @@ module GraphQL
138
138
  argument_defn = if (arg = @argument_definitions.last)
139
139
  arg_type = arg.type.unwrap
140
140
  if arg_type.kind.input_object?
141
- arg_type.arguments[node.name]
141
+ arg_type.get_argument(node.name, @query.context)
142
142
  else
143
143
  nil
144
144
  end
145
145
  elsif (directive_defn = @directive_definitions.last)
146
- directive_defn.arguments[node.name]
146
+ directive_defn.get_argument(node.name, @query.context)
147
147
  elsif (field_defn = @field_definitions.last)
148
- field_defn.arguments[node.name]
148
+ field_defn.get_argument(node.name, @query.context)
149
149
  else
150
150
  nil
151
151
  end
@@ -89,7 +89,7 @@ module GraphQL
89
89
  "#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
90
90
  "#{context_entry.field.path}#{field_alias ? " as #{field_alias}" : ""}",
91
91
  "#{context_entry.object.object.inspect}",
92
- context_entry.arguments.to_h.inspect,
92
+ context_entry.arguments.to_h.inspect, # rubocop:disable Development/ContextIsPassedCop -- unrelated method
93
93
  Backtrace::InspectResult.inspect_result(value),
94
94
  ]
95
95
  if (parent = context_entry.parent_frame)
@@ -41,7 +41,9 @@ module GraphQL
41
41
  alias :graphql_name :name
42
42
  # Future-compatible alias
43
43
  # @see {GraphQL::SchemaMember}
44
- alias :graphql_definition :itself
44
+ def graphql_definition(silence_deprecation_warning: false)
45
+ itself
46
+ end
45
47
 
46
48
  def type_class
47
49
  metadata[:type_class]
@@ -194,7 +196,7 @@ module GraphQL
194
196
  resolve_related_type(Object.const_get(type_arg))
195
197
  else
196
198
  if type_arg.respond_to?(:graphql_definition)
197
- type_arg.graphql_definition
199
+ type_arg.graphql_definition(silence_deprecation_warning: true)
198
200
  else
199
201
  type_arg
200
202
  end
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::BOOLEAN_TYPE = GraphQL::Types::Boolean.graphql_definition
2
+ GraphQL::BOOLEAN_TYPE = GraphQL::Types::Boolean.graphql_definition(silence_deprecation_warning: true)
@@ -23,8 +23,18 @@ module GraphQL
23
23
  # end
24
24
  #
25
25
  class Dataloader
26
- def self.use(schema)
27
- schema.dataloader_class = self
26
+ class << self
27
+ attr_accessor :default_nonblocking
28
+ end
29
+
30
+ AsyncDataloader = Class.new(self) { self.default_nonblocking = true }
31
+
32
+ def self.use(schema, nonblocking: nil)
33
+ schema.dataloader_class = if nonblocking
34
+ AsyncDataloader
35
+ else
36
+ self
37
+ end
28
38
  end
29
39
 
30
40
  # Call the block with a Dataloader instance,
@@ -39,9 +49,16 @@ module GraphQL
39
49
  result
40
50
  end
41
51
 
42
- def initialize
52
+ def initialize(nonblocking: self.class.default_nonblocking)
43
53
  @source_cache = Hash.new { |h, k| h[k] = {} }
44
54
  @pending_jobs = []
55
+ if !nonblocking.nil?
56
+ @nonblocking = nonblocking
57
+ end
58
+ end
59
+
60
+ def nonblocking?
61
+ @nonblocking
45
62
  end
46
63
 
47
64
  # Get a Source instance from this dataloader, for calling `.load(...)` or `.request(...)` on.
@@ -50,7 +67,7 @@ module GraphQL
50
67
  # @param batch_parameters [Array<Object>]
51
68
  # @return [GraphQL::Dataloader::Source] An instance of {source_class}, initialized with `self, *batch_parameters`,
52
69
  # and cached for the lifetime of this {Multiplex}.
53
- if RUBY_VERSION < "3"
70
+ if RUBY_VERSION < "3" || RUBY_ENGINE != "ruby" # truffle-ruby wasn't doing well with the implementation below
54
71
  def with(source_class, *batch_args)
55
72
  batch_key = source_class.batch_key_for(*batch_args)
56
73
  @source_cache[source_class][batch_key] ||= begin
@@ -117,6 +134,9 @@ module GraphQL
117
134
 
118
135
  # @api private Move along, move along
119
136
  def run
137
+ if @nonblocking && !Fiber.scheduler
138
+ raise "`nonblocking: true` requires `Fiber.scheduler`, assign one with `Fiber.set_scheduler(...)` before executing GraphQL."
139
+ end
120
140
  # At a high level, the algorithm is:
121
141
  #
122
142
  # A) Inside Fibers, run jobs from the queue one-by-one
@@ -137,6 +157,8 @@ module GraphQL
137
157
  #
138
158
  pending_fibers = []
139
159
  next_fibers = []
160
+ pending_source_fibers = []
161
+ next_source_fibers = []
140
162
  first_pass = true
141
163
 
142
164
  while first_pass || (f = pending_fibers.shift)
@@ -174,31 +196,27 @@ module GraphQL
174
196
  # This is where an evented approach would be even better -- can we tell which
175
197
  # fibers are ready to continue, and continue execution there?
176
198
  #
177
- source_fiber_queue = if (first_source_fiber = create_source_fiber)
178
- [first_source_fiber]
179
- else
180
- nil
199
+ if (first_source_fiber = create_source_fiber)
200
+ pending_source_fibers << first_source_fiber
181
201
  end
182
202
 
183
- if source_fiber_queue
184
- while (outer_source_fiber = source_fiber_queue.shift)
203
+ while pending_source_fibers.any?
204
+ while (outer_source_fiber = pending_source_fibers.pop)
185
205
  resume(outer_source_fiber)
186
-
187
- # If this source caused more sources to become pending, run those before running this one again:
188
- next_source_fiber = create_source_fiber
189
- if next_source_fiber
190
- source_fiber_queue << next_source_fiber
191
- end
192
-
193
206
  if outer_source_fiber.alive?
194
- source_fiber_queue << outer_source_fiber
207
+ next_source_fibers << outer_source_fiber
208
+ end
209
+ if (next_source_fiber = create_source_fiber)
210
+ pending_source_fibers << next_source_fiber
195
211
  end
196
212
  end
213
+ join_queues(pending_source_fibers, next_source_fibers)
214
+ next_source_fibers.clear
197
215
  end
198
216
  # Move newly-enqueued Fibers on to the list to be resumed.
199
217
  # Clear out the list of next-round Fibers, so that
200
218
  # any Fibers that pause can be put on it.
201
- pending_fibers.concat(next_fibers)
219
+ join_queues(pending_fibers, next_fibers)
202
220
  next_fibers.clear
203
221
  end
204
222
  end
@@ -213,6 +231,14 @@ module GraphQL
213
231
  nil
214
232
  end
215
233
 
234
+ def join_queues(previous_queue, next_queue)
235
+ if @nonblocking
236
+ Fiber.scheduler.run
237
+ next_queue.select!(&:alive?)
238
+ end
239
+ previous_queue.concat(next_queue)
240
+ end
241
+
216
242
  private
217
243
 
218
244
  # If there are pending sources, return a fiber for running them.
@@ -266,9 +292,16 @@ module GraphQL
266
292
  fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
267
293
  end
268
294
 
269
- Fiber.new do
270
- fiber_locals.each { |k, v| Thread.current[k] = v }
271
- yield
295
+ if @nonblocking
296
+ Fiber.new(blocking: false) do
297
+ fiber_locals.each { |k, v| Thread.current[k] = v }
298
+ yield
299
+ end
300
+ else
301
+ Fiber.new do
302
+ fiber_locals.each { |k, v| Thread.current[k] = v }
303
+ yield
304
+ end
272
305
  end
273
306
  end
274
307
  end
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::DeprecatedDirective = GraphQL::Schema::Directive::Deprecated.graphql_definition
2
+ GraphQL::Directive::DeprecatedDirective = GraphQL::Schema::Directive::Deprecated.graphql_definition(silence_deprecation_warning: true)
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::IncludeDirective = GraphQL::Schema::Directive::Include.graphql_definition
2
+ GraphQL::Directive::IncludeDirective = GraphQL::Schema::Directive::Include.graphql_definition(silence_deprecation_warning: true)
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::Directive::SkipDirective = GraphQL::Schema::Directive::Skip.graphql_definition
2
+ GraphQL::Directive::SkipDirective = GraphQL::Schema::Directive::Skip.graphql_definition(silence_deprecation_warning: true)
@@ -105,7 +105,3 @@ module GraphQL
105
105
  end
106
106
  end
107
107
  end
108
-
109
- require "graphql/directive/include_directive"
110
- require "graphql/directive/skip_directive"
111
- require "graphql/directive/deprecated_directive"
@@ -34,10 +34,14 @@ module GraphQL
34
34
  end
35
35
 
36
36
  # @return [Hash<String => EnumValue>] `{name => value}` pairs contained in this type
37
- def values
37
+ def values(_context = nil)
38
38
  @values_by_name
39
39
  end
40
40
 
41
+ def enum_values(_context = nil)
42
+ values.values
43
+ end
44
+
41
45
  def kind
42
46
  GraphQL::TypeKinds::ENUM
43
47
  end
@@ -111,6 +111,7 @@ module GraphQL
111
111
  runtime_info = ctx.namespace(:interpreter) || {}
112
112
  obj = runtime_info[:current_object]
113
113
  args = runtime_info[:current_arguments]
114
+ args = args && args.keyword_arguments
114
115
  field = runtime_info[:current_field]
115
116
  if obj.is_a?(GraphQL::Schema::Object)
116
117
  obj = obj.object
@@ -59,7 +59,7 @@ module GraphQL
59
59
  @empty
60
60
  end
61
61
 
62
- def_delegators :keyword_arguments, :key?, :[], :fetch, :keys, :each, :values
62
+ def_delegators :keyword_arguments, :key?, :[], :fetch, :keys, :each, :values, :size, :to_h
63
63
  def_delegators :argument_values, :each_value
64
64
 
65
65
  def inspect
@@ -71,11 +71,11 @@ module GraphQL
71
71
  when Array
72
72
  ast_arg_or_hash_or_value.map { |v| prepare_args_hash(query, v) }
73
73
  when GraphQL::Language::Nodes::Field, GraphQL::Language::Nodes::InputObject, GraphQL::Language::Nodes::Directive
74
- if ast_arg_or_hash_or_value.arguments.empty?
74
+ if ast_arg_or_hash_or_value.arguments.empty? # rubocop:disable Development/ContextIsPassedCop -- AST-related
75
75
  return NO_ARGUMENTS
76
76
  end
77
77
  args_hash = {}
78
- ast_arg_or_hash_or_value.arguments.each do |arg|
78
+ ast_arg_or_hash_or_value.arguments.each do |arg| # rubocop:disable Development/ContextIsPassedCop -- AST-related
79
79
  v = prepare_args_hash(query, arg.value)
80
80
  if v != NO_VALUE_GIVEN
81
81
  args_hash[arg.name] = v
@@ -314,7 +314,7 @@ module GraphQL
314
314
  case node
315
315
  when GraphQL::Language::Nodes::InlineFragment
316
316
  if node.type
317
- type_defn = schema.get_type(node.type.name)
317
+ type_defn = schema.get_type(node.type.name, context)
318
318
 
319
319
  # Faster than .map{}.include?()
320
320
  query.warden.possible_types(type_defn).each do |t|
@@ -329,7 +329,7 @@ module GraphQL
329
329
  end
330
330
  when GraphQL::Language::Nodes::FragmentSpread
331
331
  fragment_def = query.fragments[node.name]
332
- type_defn = schema.get_type(fragment_def.type.name)
332
+ type_defn = query.get_type(fragment_def.type.name)
333
333
  possible_types = query.warden.possible_types(type_defn)
334
334
  possible_types.each do |t|
335
335
  if t == owner_type
@@ -384,7 +384,9 @@ module GraphQL
384
384
  ast_node = field_ast_nodes_or_ast_node
385
385
  end
386
386
  field_name = ast_node.name
387
- field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
387
+ # This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
388
+ # because of how `is_introspection` is used to call `.authorized_new` later on.
389
+ field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
388
390
  is_introspection = false
389
391
  if field_defn.nil?
390
392
  field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
@@ -419,10 +421,10 @@ module GraphQL
419
421
  object = authorized_new(field_defn.owner, object, context)
420
422
  end
421
423
 
422
- total_args_count = field_defn.arguments.size
424
+ total_args_count = field_defn.arguments(context).size
423
425
  if total_args_count == 0
424
- kwarg_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
425
- evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
426
+ resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
427
+ evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object)
426
428
  else
427
429
  # TODO remove all arguments(...) usages?
428
430
  @query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
@@ -431,10 +433,10 @@ module GraphQL
431
433
  end
432
434
  end
433
435
 
434
- def evaluate_selection_with_args(kwarg_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
436
+ def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object) # rubocop:disable Metrics/ParameterLists
435
437
  context.scoped_context = scoped_context
436
438
  return_type = field_defn.type
437
- after_lazy(kwarg_arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
439
+ after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
438
440
  if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
439
441
  continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
440
442
  next
@@ -485,7 +487,7 @@ module GraphQL
485
487
  resolved_arguments.keyword_arguments
486
488
  end
487
489
 
488
- set_all_interpreter_context(nil, nil, kwarg_arguments, nil)
490
+ set_all_interpreter_context(nil, nil, resolved_arguments, nil)
489
491
 
490
492
  # Optimize for the case that field is selected only once
491
493
  if field_ast_nodes.nil? || field_ast_nodes.size == 1
@@ -511,7 +513,7 @@ module GraphQL
511
513
  rescue GraphQL::ExecutionError => err
512
514
  err
513
515
  end
514
- after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: kwarg_arguments, result_name: result_name, result: selection_result) do |inner_result|
516
+ after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
515
517
  continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
516
518
  if HALT != continue_value
517
519
  continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, kwarg_arguments, result_name, selection_result)
@@ -637,14 +639,20 @@ module GraphQL
637
639
  when Array
638
640
  # It's an array full of execution errors; add them all.
639
641
  if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
642
+ list_type_at_all = (field && (field.type.list?))
640
643
  if selection_result.nil? || !dead_result?(selection_result)
641
644
  value.each_with_index do |error, index|
642
645
  error.ast_node ||= ast_node
643
- error.path ||= path + ((field && field.type.list?) ? [index] : [])
646
+ error.path ||= path + (list_type_at_all ? [index] : [])
644
647
  context.errors << error
645
648
  end
646
649
  if selection_result
647
- set_result(selection_result, result_name, nil)
650
+ if list_type_at_all
651
+ result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
652
+ set_result(selection_result, result_name, result_without_errors)
653
+ else
654
+ set_result(selection_result, result_name, nil)
655
+ end
648
656
  end
649
657
  end
650
658
  HALT
@@ -862,16 +870,20 @@ module GraphQL
862
870
  # but don't wrap the continuation below
863
871
  inner_obj = begin
864
872
  query.with_error_handling do
865
- if trace
866
- query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
873
+ begin
874
+ if trace
875
+ query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
876
+ schema.sync_lazy(lazy_obj)
877
+ end
878
+ else
867
879
  schema.sync_lazy(lazy_obj)
868
880
  end
869
- else
870
- schema.sync_lazy(lazy_obj)
881
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
882
+ err
871
883
  end
872
884
  end
873
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
874
- err
885
+ rescue GraphQL::ExecutionError => ex_err
886
+ ex_err
875
887
  end
876
888
  yield(inner_obj)
877
889
  end
@@ -254,14 +254,14 @@ module GraphQL
254
254
  subselections_on_type = selections_on_type
255
255
  if (t = ast_selection.type)
256
256
  # Assuming this is valid, that `t` will be found.
257
- on_type = @query.schema.get_type(t.name).type_class
257
+ on_type = @query.get_type(t.name).type_class
258
258
  subselections_on_type = subselections_by_type[on_type] ||= {}
259
259
  end
260
260
  find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
261
261
  when GraphQL::Language::Nodes::FragmentSpread
262
262
  frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
263
263
  # Again, assuming a valid AST
264
- on_type = @query.schema.get_type(frag_defn.type.name).type_class
264
+ on_type = @query.get_type(frag_defn.type.name).type_class
265
265
  subselections_on_type = subselections_by_type[on_type] ||= {}
266
266
  find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
267
267
  else
@@ -35,7 +35,7 @@ module GraphQL
35
35
  @queries = queries
36
36
  @queries.each { |q| q.multiplex = self }
37
37
  @context = context
38
- @context[:dataloader] = @dataloader = @schema.dataloader_class.new
38
+ @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
39
39
  @tracers = schema.tracers + (context[:tracers] || [])
40
40
  # Support `context: {backtrace: true}`
41
41
  if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
@@ -151,6 +151,9 @@ module GraphQL
151
151
 
152
152
  result
153
153
  end
154
+ if query.context.namespace?(:__query_result_extensions__)
155
+ query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
156
+ end
154
157
  end
155
158
 
156
159
  # use the old `query_execution_strategy` etc to run this query
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::FLOAT_TYPE = GraphQL::Types::Float.graphql_definition
2
+ GraphQL::FLOAT_TYPE = GraphQL::Types::Float.graphql_definition(silence_deprecation_warning: true)
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::ID_TYPE = GraphQL::Types::ID.graphql_definition
2
+ GraphQL::ID_TYPE = GraphQL::Types::ID.graphql_definition(silence_deprecation_warning: true)
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::INT_TYPE = GraphQL::Types::Int.graphql_definition
2
+ GraphQL::INT_TYPE = GraphQL::Types::Int.graphql_definition(silence_deprecation_warning: true)
@@ -10,7 +10,7 @@ module GraphQL
10
10
  "skipping a field. Directives provide this by describing additional information "\
11
11
  "to the executor."
12
12
  field :name, String, null: false, method: :graphql_name
13
- field :description, String, null: true
13
+ field :description, String
14
14
  field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false
15
15
  field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false do
16
16
  argument :include_deprecated, Boolean, required: false, default_value: false
@@ -3,8 +3,8 @@ module GraphQL
3
3
  module Introspection
4
4
  class EntryPoints < Introspection::BaseObject
5
5
  field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false
6
- field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", null: true do
7
- argument :name, String, required: true
6
+ field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system" do
7
+ argument :name, String
8
8
  end
9
9
 
10
10
  def __schema