graphql 1.13.0 → 1.13.1

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/core.rb +3 -1
  3. data/lib/generators/graphql/install_generator.rb +9 -2
  4. data/lib/graphql/base_type.rb +4 -2
  5. data/lib/graphql/boolean_type.rb +1 -1
  6. data/lib/graphql/directive/deprecated_directive.rb +1 -1
  7. data/lib/graphql/directive/include_directive.rb +1 -1
  8. data/lib/graphql/directive/skip_directive.rb +1 -1
  9. data/lib/graphql/execution/interpreter/runtime.rb +10 -6
  10. data/lib/graphql/execution/multiplex.rb +3 -0
  11. data/lib/graphql/float_type.rb +1 -1
  12. data/lib/graphql/id_type.rb +1 -1
  13. data/lib/graphql/int_type.rb +1 -1
  14. data/lib/graphql/language/block_string.rb +2 -2
  15. data/lib/graphql/language/nodes.rb +10 -1
  16. data/lib/graphql/query/context.rb +10 -0
  17. data/lib/graphql/relay/global_id_resolve.rb +1 -1
  18. data/lib/graphql/relay/page_info.rb +1 -1
  19. data/lib/graphql/schema/argument.rb +2 -0
  20. data/lib/graphql/schema/directive.rb +5 -1
  21. data/lib/graphql/schema/enum.rb +3 -1
  22. data/lib/graphql/schema/enum_value.rb +2 -0
  23. data/lib/graphql/schema/field.rb +40 -24
  24. data/lib/graphql/schema/field_extension.rb +52 -2
  25. data/lib/graphql/schema/input_object.rb +3 -1
  26. data/lib/graphql/schema/interface.rb +3 -1
  27. data/lib/graphql/schema/introspection_system.rb +1 -1
  28. data/lib/graphql/schema/list.rb +3 -1
  29. data/lib/graphql/schema/member/accepts_definition.rb +7 -2
  30. data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
  31. data/lib/graphql/schema/non_null.rb +3 -1
  32. data/lib/graphql/schema/object.rb +3 -1
  33. data/lib/graphql/schema/scalar.rb +2 -0
  34. data/lib/graphql/schema/traversal.rb +1 -1
  35. data/lib/graphql/schema/union.rb +2 -0
  36. data/lib/graphql/schema/validator.rb +4 -7
  37. data/lib/graphql/schema.rb +19 -7
  38. data/lib/graphql/static_validation/rules/fields_will_merge.rb +14 -7
  39. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -0
  40. data/lib/graphql/string_type.rb +1 -1
  41. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -0
  42. data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
  43. data/lib/graphql/types/relay/default_relay.rb +5 -1
  44. data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
  45. data/lib/graphql/version.rb +1 -1
  46. data/lib/graphql.rb +1 -1
  47. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 853fa0482ef0c3bafe04dce77dff9bb685633a1e4c273fd234169f98d595352c
4
- data.tar.gz: 6cf800af8dfc469f4d7a375ee22f6d98e03207799f813f10176916f09a0a4064
3
+ metadata.gz: 4f950da72b7fb585a9135dc893b2c2cf41c8a6482a82a0f7401ba913d48c643f
4
+ data.tar.gz: 8d5cd615133ca107e38ab18ccc59902c6eaa1f8d7ffeef5c7af474f1e4c5cde9
5
5
  SHA512:
6
- metadata.gz: 7bcd2b94fa64ab65fb47995a4896b08629bfc9b66f7797dfd4959ad8ccdb5332bf63ae49f5139c7d1766b3d72cb500da257c611e628dc5db36ae26b472c5d51c
7
- data.tar.gz: 51a5c6791429c17940d8d0a5407efb852fd4ddbe7a5eaf706dd8fb074a6654f17b32b08f21956e4e450dff6cff05fb0020a7ff8a141fe38f3e09b8433ca1b4ea
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'
@@ -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)
@@ -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)
@@ -870,16 +870,20 @@ module GraphQL
870
870
  # but don't wrap the continuation below
871
871
  inner_obj = begin
872
872
  query.with_error_handling do
873
- if trace
874
- 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
875
879
  schema.sync_lazy(lazy_obj)
876
880
  end
877
- else
878
- schema.sync_lazy(lazy_obj)
881
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
882
+ err
879
883
  end
880
884
  end
881
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
882
- err
885
+ rescue GraphQL::ExecutionError => ex_err
886
+ ex_err
883
887
  end
884
888
  yield(inner_obj)
885
889
  end
@@ -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)
@@ -7,7 +7,7 @@ module GraphQL
7
7
  def self.trim_whitespace(str)
8
8
  # Early return for the most common cases:
9
9
  if str == ""
10
- return ""
10
+ return "".dup
11
11
  elsif !(has_newline = str.include?("\n")) && !(str.start_with?(" "))
12
12
  return str
13
13
  end
@@ -55,7 +55,7 @@ module GraphQL
55
55
  end
56
56
 
57
57
  # Rebuild the string
58
- lines.size > 1 ? lines.join("\n") : (lines.first || "")
58
+ lines.size > 1 ? lines.join("\n") : (lines.first || "".dup)
59
59
  end
60
60
 
61
61
  def self.print(str, indent: '')
@@ -197,7 +197,16 @@ module GraphQL
197
197
  else
198
198
  module_eval <<-RUBY, __FILE__, __LINE__
199
199
  def children
200
- @children ||= (#{children_of_type.keys.map { |k| "@#{k}" }.join(" + ")}).freeze
200
+ @children ||= begin
201
+ if #{children_of_type.keys.map { |k| "@#{k}.any?" }.join(" || ")}
202
+ new_children = []
203
+ #{children_of_type.keys.map { |k| "new_children.concat(@#{k})" }.join("; ")}
204
+ new_children.freeze
205
+ new_children
206
+ else
207
+ NO_CHILDREN
208
+ end
209
+ end
201
210
  end
202
211
  RUBY
203
212
  end
@@ -156,6 +156,11 @@ module GraphQL
156
156
  @scoped_context = {}
157
157
  end
158
158
 
159
+ # @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
160
+ def response_extensions
161
+ namespace(:__query_result_extensions__)
162
+ end
163
+
159
164
  def dataloader
160
165
  @dataloader ||= self[:dataloader] || (query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new)
161
166
  end
@@ -236,6 +241,11 @@ module GraphQL
236
241
  @storage[ns]
237
242
  end
238
243
 
244
+ # @return [Boolean] true if this namespace was accessed before
245
+ def namespace?(ns)
246
+ @storage.key?(ns)
247
+ end
248
+
239
249
  def inspect
240
250
  "#<Query::Context ...>"
241
251
  end
@@ -10,7 +10,7 @@ module GraphQL
10
10
  if obj.is_a?(GraphQL::Schema::Object)
11
11
  obj = obj.object
12
12
  end
13
- type = @type.respond_to?(:graphql_definition) ? @type.graphql_definition : @type
13
+ type = @type.respond_to?(:graphql_definition) ? @type.graphql_definition(silence_deprecation_warning: true) : @type
14
14
  ctx.query.schema.id_from_object(obj, type, ctx)
15
15
  end
16
16
  end
@@ -2,6 +2,6 @@
2
2
  module GraphQL
3
3
  module Relay
4
4
  # Wrap a Connection and expose its page info
5
- PageInfo = GraphQL::Types::Relay::PageInfo.graphql_definition
5
+ PageInfo = GraphQL::Types::Relay::PageInfo.graphql_definition(silence_deprecation_warning: true)
6
6
  end
7
7
  end
@@ -161,6 +161,8 @@ module GraphQL
161
161
  true
162
162
  end
163
163
 
164
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
165
+
164
166
  def to_graphql
165
167
  argument = GraphQL::Argument.new
166
168
  argument.name = @name
@@ -8,6 +8,8 @@ module GraphQL
8
8
  # - {.resolve}: Wraps field resolution (so it should call `yield` to continue)
9
9
  class Directive < GraphQL::Schema::Member
10
10
  extend GraphQL::Schema::Member::HasArguments
11
+ extend GraphQL::Schema::Member::AcceptsDefinition
12
+
11
13
  class << self
12
14
  # Directives aren't types, they don't have kinds.
13
15
  undef_method :kind
@@ -53,6 +55,8 @@ module GraphQL
53
55
  default_directive
54
56
  end
55
57
 
58
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
59
+
56
60
  def to_graphql
57
61
  defn = GraphQL::Directive.new
58
62
  defn.name = self.graphql_name
@@ -62,7 +66,7 @@ module GraphQL
62
66
  defn.ast_node = ast_node
63
67
  defn.metadata[:type_class] = self
64
68
  all_argument_definitions.each do |arg_defn|
65
- arg_graphql = arg_defn.to_graphql
69
+ arg_graphql = arg_defn.to_graphql(silence_deprecation_warning: true)
66
70
  defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
67
71
  end
68
72
  # Make a reference to a classic-style Arguments class
@@ -108,6 +108,8 @@ module GraphQL
108
108
  enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
109
109
  end
110
110
 
111
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
112
+
111
113
  # @return [GraphQL::EnumType]
112
114
  def to_graphql
113
115
  enum_type = GraphQL::EnumType.new
@@ -116,7 +118,7 @@ module GraphQL
116
118
  enum_type.introspection = introspection
117
119
  enum_type.ast_node = ast_node
118
120
  values.each do |name, val|
119
- enum_type.add_value(val.to_graphql)
121
+ enum_type.add_value(val.deprecated_to_graphql)
120
122
  end
121
123
  enum_type.metadata[:type_class] = self
122
124
  enum_type
@@ -73,6 +73,8 @@ module GraphQL
73
73
  @value
74
74
  end
75
75
 
76
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
77
+
76
78
  # @return [GraphQL::EnumType::EnumValue] A runtime-ready object derived from this object
77
79
  def to_graphql
78
80
  enum_value = GraphQL::EnumType::EnumValue.new
@@ -290,6 +290,7 @@ module GraphQL
290
290
  @subscription_scope = subscription_scope
291
291
 
292
292
  @extensions = EMPTY_ARRAY
293
+ @call_after_define = false
293
294
  # This should run before connection extension,
294
295
  # but should it run after the definition block?
295
296
  if scoped?
@@ -322,6 +323,9 @@ module GraphQL
322
323
  instance_eval(&definition_block)
323
324
  end
324
325
  end
326
+
327
+ self.extensions.each(&:after_define_apply)
328
+ @call_after_define = true
325
329
  end
326
330
 
327
331
  # If true, subscription updates with this field can be shared between viewers
@@ -354,27 +358,20 @@ module GraphQL
354
358
  # @example adding an extension with options
355
359
  # extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
356
360
  #
357
- # @param extensions [Array<Class, Hash<Class => Object>>] Add extensions to this field. For hash elements, only the first key/value is used.
361
+ # @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
358
362
  # @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
359
363
  def extensions(new_extensions = nil)
360
- if new_extensions.nil?
361
- # Read the value
362
- @extensions
363
- else
364
- if @extensions.frozen?
365
- @extensions = @extensions.dup
366
- end
367
- new_extensions.each do |extension|
368
- if extension.is_a?(Hash)
369
- extension = extension.to_a[0]
370
- extension_class, options = *extension
371
- @extensions << extension_class.new(field: self, options: options)
364
+ if new_extensions
365
+ new_extensions.each do |extension_config|
366
+ if extension_config.is_a?(Hash)
367
+ extension_class, options = *extension_config.to_a[0]
368
+ self.extension(extension_class, options)
372
369
  else
373
- extension_class = extension
374
- @extensions << extension_class.new(field: self, options: nil)
370
+ self.extension(extension_config)
375
371
  end
376
372
  end
377
373
  end
374
+ @extensions
378
375
  end
379
376
 
380
377
  # Add `extension` to this field, initialized with `options` if provided.
@@ -385,10 +382,19 @@ module GraphQL
385
382
  # @example adding an extension with options
386
383
  # extension(MyExtensionClass, filter: true)
387
384
  #
388
- # @param extension [Class] subclass of {Schema::Fieldextension}
389
- # @param options [Object] if provided, given as `options:` when initializing `extension`.
390
- def extension(extension, options = nil)
391
- extensions([{extension => options}])
385
+ # @param extension_class [Class] subclass of {Schema::FieldExtension}
386
+ # @param options [Hash] if provided, given as `options:` when initializing `extension`.
387
+ # @return [void]
388
+ def extension(extension_class, options = nil)
389
+ extension_inst = extension_class.new(field: self, options: options)
390
+ if @extensions.frozen?
391
+ @extensions = @extensions.dup
392
+ end
393
+ if @call_after_define
394
+ extension_inst.after_define_apply
395
+ end
396
+ @extensions << extension_inst
397
+ nil
392
398
  end
393
399
 
394
400
  # Read extras (as symbols) from this field,
@@ -441,10 +447,15 @@ module GraphQL
441
447
  if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
442
448
  metadata_complexity += 1
443
449
  end
450
+
451
+ nodes_edges_complexity = 0
452
+ nodes_edges_complexity += 1 if lookahead.selects?(:edges)
453
+ nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
454
+
444
455
  # Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
445
- items_complexity = child_complexity - metadata_complexity
456
+ items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
446
457
  # Add 1 for _this_ field
447
- 1 + (max_possible_page_size * items_complexity) + metadata_complexity
458
+ 1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
448
459
  end
449
460
  else
450
461
  defined_complexity = complexity
@@ -488,6 +499,8 @@ module GraphQL
488
499
  # @return [Integer, nil] Applied to connections if {#has_max_page_size?}
489
500
  attr_reader :max_page_size
490
501
 
502
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
503
+
491
504
  # @return [GraphQL::Field]
492
505
  def to_graphql
493
506
  field_defn = if @field
@@ -543,7 +556,7 @@ module GraphQL
543
556
  field_defn.ast_node = ast_node
544
557
 
545
558
  all_argument_definitions.each do |defn|
546
- arg_graphql = defn.to_graphql
559
+ arg_graphql = defn.deprecated_to_graphql
547
560
  field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
548
561
  end
549
562
 
@@ -604,10 +617,13 @@ module GraphQL
604
617
  end
605
618
 
606
619
  def authorized?(object, args, context)
607
- if @resolver_class
608
- # The resolver will check itself during `resolve()`
620
+ resolver_authed = if @resolver_class
621
+ # The resolver _instance_ will check itself during `resolve()`
609
622
  @resolver_class.authorized?(object, context)
610
623
  else
624
+ true
625
+ end
626
+ resolver_authed && begin
611
627
  if (arg_values = context[:current_arguments])
612
628
  # ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
613
629
  using_arg_values = true
@@ -15,15 +15,40 @@ module GraphQL
15
15
  # @return [Object]
16
16
  attr_reader :options
17
17
 
18
+ # @return [Array<Symbol>, nil] `default_argument`s added, if any were added (otherwise, `nil`)
19
+ attr_reader :added_default_arguments
20
+
18
21
  # Called when the extension is mounted with `extension(name, options)`.
19
- # The instance is frozen to avoid improper use of state during execution.
22
+ # The instance will be frozen to avoid improper use of state during execution.
20
23
  # @param field [GraphQL::Schema::Field] The field where this extension was mounted
21
24
  # @param options [Object] The second argument to `extension`, or `{}` if nothing was passed.
22
25
  def initialize(field:, options:)
23
26
  @field = field
24
27
  @options = options || {}
28
+ @added_default_arguments = nil
25
29
  apply
26
- freeze
30
+ end
31
+
32
+ class << self
33
+ # @return [Array(Array, Hash), nil] A list of default argument configs, or `nil` if there aren't any
34
+ def default_argument_configurations
35
+ args = superclass.respond_to?(:default_argument_configurations) ? superclass.default_argument_configurations : nil
36
+ if @own_default_argument_configurations
37
+ if args
38
+ args.concat(@own_default_argument_configurations)
39
+ else
40
+ args = @own_default_argument_configurations.dup
41
+ end
42
+ end
43
+ args
44
+ end
45
+
46
+ # @see Argument#initialize
47
+ # @see HasArguments#argument
48
+ def default_argument(*argument_args, **argument_kwargs)
49
+ configs = @own_default_argument_configurations ||= []
50
+ configs << [argument_args, argument_kwargs]
51
+ end
27
52
  end
28
53
 
29
54
  # Called when this extension is attached to a field.
@@ -32,6 +57,31 @@ module GraphQL
32
57
  def apply
33
58
  end
34
59
 
60
+ # Called after the field's definition block has been executed.
61
+ # (Any arguments from the block are present on `field`)
62
+ # @return [void]
63
+ def after_define
64
+ end
65
+
66
+ # @api private
67
+ def after_define_apply
68
+ after_define
69
+ if (configs = self.class.default_argument_configurations)
70
+ existing_keywords = field.all_argument_definitions.map(&:keyword)
71
+ existing_keywords.uniq!
72
+ @added_default_arguments = []
73
+ configs.each do |config|
74
+ argument_args, argument_kwargs = config
75
+ arg_name = argument_args[0]
76
+ if !existing_keywords.include?(arg_name)
77
+ @added_default_arguments << arg_name
78
+ field.argument(*argument_args, **argument_kwargs)
79
+ end
80
+ end
81
+ end
82
+ freeze
83
+ end
84
+
35
85
  # Called before resolving {#field}. It should either:
36
86
  #
37
87
  # - `yield` values to continue execution; OR
@@ -132,6 +132,8 @@ module GraphQL
132
132
  argument_defn
133
133
  end
134
134
 
135
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
136
+
135
137
  def to_graphql
136
138
  type_defn = GraphQL::InputObjectType.new
137
139
  type_defn.name = graphql_name
@@ -140,7 +142,7 @@ module GraphQL
140
142
  type_defn.mutation = mutation
141
143
  type_defn.ast_node = ast_node
142
144
  all_argument_definitions.each do |arg|
143
- type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition # rubocop:disable Development/ContextIsPassedCop -- legacy-related
145
+ type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
144
146
  end
145
147
  # Make a reference to a classic-style Arguments class
146
148
  self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
@@ -100,6 +100,8 @@ module GraphQL
100
100
  end
101
101
  end
102
102
 
103
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
104
+
103
105
  def to_graphql
104
106
  type_defn = GraphQL::InterfaceType.new
105
107
  type_defn.name = graphql_name
@@ -108,7 +110,7 @@ module GraphQL
108
110
  type_defn.type_membership_class = self.type_membership_class
109
111
  type_defn.ast_node = ast_node
110
112
  fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
111
- field_defn = field_inst.graphql_definition
113
+ field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
112
114
  type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
113
115
  end
114
116
  type_defn.metadata[:type_class] = self
@@ -107,7 +107,7 @@ module GraphQL
107
107
  dup_type_class(const)
108
108
  else
109
109
  # Use `.to_graphql` to get a freshly-made version, not shared between schemas
110
- const.to_graphql
110
+ const.deprecated_to_graphql
111
111
  end
112
112
  rescue NameError
113
113
  # Dup the built-in so that the cached fields aren't shared
@@ -8,8 +8,10 @@ module GraphQL
8
8
  class List < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
11
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
12
+
11
13
  def to_graphql
12
- @of_type.graphql_definition.to_list_type
14
+ @of_type.graphql_definition(silence_deprecation_warning: true).to_list_type
13
15
  end
14
16
 
15
17
  # @return [GraphQL::TypeKinds::LIST]
@@ -123,8 +123,13 @@ module GraphQL
123
123
  end
124
124
 
125
125
  module ToGraphQLExtension
126
- def to_graphql
127
- defn = super
126
+ def to_graphql(*args, **kwargs)
127
+
128
+ defn = if args.empty? && kwargs.empty?
129
+ super()
130
+ else
131
+ super
132
+ end
128
133
  accepts_definition_methods.each do |method_name|
129
134
  value = public_send(method_name)
130
135
  if !value.nil?
@@ -11,8 +11,24 @@ module GraphQL
11
11
  # A cached result of {.to_graphql}.
12
12
  # It's cached here so that user-overridden {.to_graphql} implementations
13
13
  # are also cached
14
- def graphql_definition
15
- @graphql_definition ||= to_graphql
14
+ def graphql_definition(silence_deprecation_warning: false)
15
+ @graphql_definition ||= begin
16
+ unless silence_deprecation_warning
17
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.graphql_definition` to use a class-based definition instead."
18
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
19
+ GraphQL::Deprecation.warn(message + caller_message)
20
+ end
21
+ deprecated_to_graphql
22
+ end
23
+ end
24
+
25
+ def deprecated_to_graphql
26
+ case method(:to_graphql).arity
27
+ when 0
28
+ to_graphql
29
+ else
30
+ to_graphql(silence_deprecation_warning: true)
31
+ end
16
32
  end
17
33
 
18
34
  # This is for a common interface with .define-based types
@@ -25,6 +41,17 @@ module GraphQL
25
41
  super
26
42
  @graphql_definition = nil
27
43
  end
44
+
45
+ module DeprecatedToGraphQL
46
+ def to_graphql(silence_deprecation_warning: false)
47
+ unless silence_deprecation_warning
48
+ message = "Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead."
49
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
50
+ GraphQL::Deprecation.warn(message + caller_message)
51
+ end
52
+ super()
53
+ end
54
+ end
28
55
  end
29
56
  end
30
57
  end
@@ -8,8 +8,10 @@ module GraphQL
8
8
  class NonNull < GraphQL::Schema::Wrapper
9
9
  include Schema::Member::ValidatesInput
10
10
 
11
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
12
+
11
13
  def to_graphql
12
- @of_type.graphql_definition.to_non_null_type
14
+ @of_type.graphql_definition(silence_deprecation_warning: true).to_non_null_type
13
15
  end
14
16
 
15
17
  # @return [GraphQL::TypeKinds::NON_NULL]
@@ -122,6 +122,8 @@ module GraphQL
122
122
  all_fields
123
123
  end
124
124
 
125
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
126
+
125
127
  # @return [GraphQL::ObjectType]
126
128
  def to_graphql
127
129
  obj_type = GraphQL::ObjectType.new
@@ -132,7 +134,7 @@ module GraphQL
132
134
  obj_type.mutation = mutation
133
135
  obj_type.ast_node = ast_node
134
136
  fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
135
- field_defn = field_inst.to_graphql
137
+ field_defn = field_inst.to_graphql(silence_deprecation_warning: true)
136
138
  obj_type.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
137
139
  end
138
140
 
@@ -14,6 +14,8 @@ module GraphQL
14
14
  val
15
15
  end
16
16
 
17
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
18
+
17
19
  def to_graphql
18
20
  type_defn = GraphQL::ScalarType.new
19
21
  type_defn.name = graphql_name
@@ -173,7 +173,7 @@ Some late-bound types couldn't be resolved:
173
173
  end
174
174
  when Class
175
175
  if member.respond_to?(:graphql_definition)
176
- graphql_member = member.graphql_definition
176
+ graphql_member = member.graphql_definition(silence_deprecation_warning: true)
177
177
  visit(schema, graphql_member, context_description)
178
178
  else
179
179
  raise GraphQL::Schema::InvalidTypeError.new("Unexpected traversal member: #{member} (#{member.class.name})")
@@ -33,6 +33,8 @@ module GraphQL
33
33
  type_memberships.map(&:object_type)
34
34
  end
35
35
 
36
+ prepend GraphQL::Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
37
+
36
38
  def to_graphql
37
39
  type_defn = GraphQL::UnionType.new
38
40
  type_defn.name = graphql_name
@@ -48,16 +48,13 @@ module GraphQL
48
48
  EMPTY_ARRAY
49
49
  else
50
50
  validates_hash = validates_hash.dup
51
- allow_null = validates_hash.delete(:allow_null)
52
- allow_blank = validates_hash.delete(:allow_blank)
53
51
 
54
- # This could be {...}.compact on Ruby 2.4+
55
52
  default_options = {}
56
- if !allow_null.nil?
57
- default_options[:allow_null] = allow_null
53
+ if validates_hash[:allow_null]
54
+ default_options[:allow_null] = validates_hash.delete(:allow_null)
58
55
  end
59
- if !allow_blank.nil?
60
- default_options[:allow_blank] = allow_blank
56
+ if validates_hash[:allow_blank]
57
+ default_options[:allow_blank] = validates_hash.delete(:allow_blank)
61
58
  end
62
59
 
63
60
  # allow_nil or allow_blank are the _only_ validations:
@@ -845,7 +845,7 @@ module GraphQL
845
845
  # - Cause the Schema instance to be created, if it hasn't been created yet
846
846
  # - Delegate to that instance
847
847
  # Eventually, the methods will be moved into this class, removing the need for the singleton.
848
- def_delegators :graphql_definition,
848
+ def_delegators :deprecated_graphql_definition,
849
849
  # Execution
850
850
  :execution_strategy_for_operation,
851
851
  # Configuration
@@ -854,6 +854,10 @@ module GraphQL
854
854
  :id_from_object=, :object_from_id=,
855
855
  :remove_handler
856
856
 
857
+ def deprecated_graphql_definition
858
+ graphql_definition(silence_deprecation_warning: true)
859
+ end
860
+
857
861
  # @return [GraphQL::Subscriptions]
858
862
  attr_accessor :subscriptions
859
863
 
@@ -896,8 +900,15 @@ module GraphQL
896
900
  @find_cache[path] ||= @finder.find(path)
897
901
  end
898
902
 
899
- def graphql_definition
900
- @graphql_definition ||= to_graphql
903
+ def graphql_definition(silence_deprecation_warning: false)
904
+ @graphql_definition ||= begin
905
+ unless silence_deprecation_warning
906
+ message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Use a class-based definition instead."
907
+ caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
908
+ GraphQL::Deprecation.warn(message + caller_message)
909
+ end
910
+ to_graphql(silence_deprecation_warning: silence_deprecation_warning)
911
+ end
901
912
  end
902
913
 
903
914
  def default_filter
@@ -929,19 +940,20 @@ module GraphQL
929
940
  find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
930
941
  end
931
942
 
943
+ prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
932
944
  def to_graphql
933
945
  schema_defn = self.new
934
946
  schema_defn.raise_definition_error = true
935
- schema_defn.query = query && query.graphql_definition
936
- schema_defn.mutation = mutation && mutation.graphql_definition
937
- schema_defn.subscription = subscription && subscription.graphql_definition
947
+ schema_defn.query = query && query.graphql_definition(silence_deprecation_warning: true)
948
+ schema_defn.mutation = mutation && mutation.graphql_definition(silence_deprecation_warning: true)
949
+ schema_defn.subscription = subscription && subscription.graphql_definition(silence_deprecation_warning: true)
938
950
  schema_defn.validate_timeout = validate_timeout
939
951
  schema_defn.validate_max_errors = validate_max_errors
940
952
  schema_defn.max_complexity = max_complexity
941
953
  schema_defn.error_bubbling = error_bubbling
942
954
  schema_defn.max_depth = max_depth
943
955
  schema_defn.default_max_page_size = default_max_page_size
944
- schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
956
+ schema_defn.orphan_types = orphan_types.map { |t| t.graphql_definition(silence_deprecation_warning: true) }
945
957
  schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
946
958
  schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
947
959
  schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
@@ -50,15 +50,15 @@ module GraphQL
50
50
  @arg_conflicts = nil
51
51
 
52
52
  yield
53
-
54
- field_conflicts.each_value { |error| add_error(error) }
55
- arg_conflicts.each_value { |error| add_error(error) }
53
+ # don't initialize these if they weren't initialized in the block:
54
+ @field_conflicts && @field_conflicts.each_value { |error| add_error(error) }
55
+ @arg_conflicts && @arg_conflicts.each_value { |error| add_error(error) }
56
56
  end
57
57
 
58
58
  def conflicts_within_selection_set(node, parent_type)
59
59
  return if parent_type.nil?
60
60
 
61
- fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: [])
61
+ fields, fragment_spreads = fields_and_fragments_from_selection(node, owner_type: parent_type, parents: nil)
62
62
 
63
63
  # (A) Find find all conflicts "within" the fields of this selection set.
64
64
  find_conflicts_within(fields)
@@ -198,10 +198,14 @@ module GraphQL
198
198
  response_keys.each do |key, fields|
199
199
  next if fields.size < 2
200
200
  # find conflicts within nodes
201
- for i in 0..fields.size - 1
202
- for j in i + 1..fields.size - 1
201
+ i = 0
202
+ while i < fields.size
203
+ j = i + 1
204
+ while j < fields.size
203
205
  find_conflict(key, fields[i], fields[j])
206
+ j += 1
204
207
  end
208
+ i += 1
205
209
  end
206
210
  end
207
211
  end
@@ -243,7 +247,9 @@ module GraphQL
243
247
  end
244
248
 
245
249
  def find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive:)
246
- return if field1.definition.nil? || field2.definition.nil?
250
+ return if field1.definition.nil? ||
251
+ field2.definition.nil? ||
252
+ (field1.node.selections.empty? && field2.node.selections.empty?)
247
253
 
248
254
  return_type1 = field1.definition.type.unwrap
249
255
  return_type2 = field2.definition.type.unwrap
@@ -323,6 +329,7 @@ module GraphQL
323
329
  if node.selections.empty?
324
330
  NO_SELECTIONS
325
331
  else
332
+ parents ||= []
326
333
  fields, fragment_spreads = find_fields_and_fragments(node.selections, owner_type: owner_type, parents: parents, fields: [], fragment_spreads: [])
327
334
  response_keys = fields.group_by { |f| f.node.alias || f.node.name }
328
335
  [response_keys, fragment_spreads]
@@ -16,6 +16,8 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def assert_required_args(ast_node, defn)
19
+ args = defn.arguments(context.query.context)
20
+ return if args.empty?
19
21
  present_argument_names = ast_node.arguments.map(&:name)
20
22
  required_argument_names = context.warden.arguments(defn)
21
23
  .select { |a| a.type.kind.non_null? && !a.default_value? && context.warden.get_argument(defn, a.name) }
@@ -1,2 +1,2 @@
1
1
  # frozen_string_literal: true
2
- GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition
2
+ GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition(silence_deprecation_warning: true)
@@ -216,6 +216,8 @@ module GraphQL
216
216
  # The channel was closed, forget about it.
217
217
  def delete_subscription(subscription_id)
218
218
  query = @subscriptions.delete(subscription_id)
219
+ # In case this came from the server, tell the client to unsubscribe:
220
+ @action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false })
219
221
  # This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
220
222
  # see https://github.com/rmosolgo/graphql-ruby/issues/2478
221
223
  if query
@@ -35,7 +35,8 @@ module GraphQL
35
35
  # It's called when you subclass this base connection, trying to use the
36
36
  # class name to set defaults. You can call it again in the class definition
37
37
  # to override the default (or provide a value, if the default lookup failed).
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
+ # @param field_options [Hash] Any extra keyword arguments to pass to the `field :edges, ...` and `field :nodes, ...` configurations
39
+ 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, field_options: nil)
39
40
  # Set this connection's graphql name
40
41
  node_type_name = node_type.graphql_name
41
42
 
@@ -43,13 +44,22 @@ module GraphQL
43
44
  @edge_type = edge_type_class
44
45
  @edge_class = edge_class
45
46
 
46
- field :edges, [edge_type_class, null: edge_nullable],
47
+ base_field_options = {
48
+ name: :edges,
49
+ type: [edge_type_class, null: edge_nullable],
47
50
  null: edges_nullable,
48
51
  description: "A list of edges.",
49
52
  legacy_edge_class: edge_class, # This is used by the old runtime only, for EdgesInstrumentation
50
- connection: false
53
+ connection: false,
54
+ }
51
55
 
52
- define_nodes_field(node_nullable) if nodes_field
56
+ if field_options
57
+ base_field_options.merge!(field_options)
58
+ end
59
+
60
+ field(**base_field_options)
61
+
62
+ define_nodes_field(node_nullable, field_options: field_options) if nodes_field
53
63
 
54
64
  description("The connection type for #{node_type_name}.")
55
65
  end
@@ -60,8 +70,8 @@ module GraphQL
60
70
  end
61
71
 
62
72
  # Add the shortcut `nodes` field to this connection and its subclasses
63
- def nodes_field(node_nullable: self.node_nullable)
64
- define_nodes_field(node_nullable)
73
+ def nodes_field(node_nullable: self.node_nullable, field_options: nil)
74
+ define_nodes_field(node_nullable, field_options: field_options)
65
75
  end
66
76
 
67
77
  def authorized?(obj, ctx)
@@ -118,11 +128,18 @@ module GraphQL
118
128
 
119
129
  private
120
130
 
121
- def define_nodes_field(nullable)
122
- field :nodes, [@node_type, null: nullable],
131
+ def define_nodes_field(nullable, field_options: nil)
132
+ base_field_options = {
133
+ name: :nodes,
134
+ type: [@node_type, null: nullable],
123
135
  null: nullable,
124
136
  description: "A list of nodes.",
125
- connection: false
137
+ connection: false,
138
+ }
139
+ if field_options
140
+ base_field_options.merge!(field_options)
141
+ end
142
+ field(**base_field_options)
126
143
  end
127
144
  end
128
145
 
@@ -17,7 +17,11 @@ module GraphQL
17
17
  end
18
18
 
19
19
  def to_graphql
20
- type_defn = super
20
+ type_defn = if method(:to_graphql).super_method.arity
21
+ super(silence_deprecation_warning: true)
22
+ else
23
+ super
24
+ end
21
25
  type_defn.default_relay = default_relay?
22
26
  type_defn
23
27
  end
@@ -16,11 +16,22 @@ module GraphQL
16
16
  #
17
17
  # @param node_type [Class] A `Schema::Object` subclass
18
18
  # @param null [Boolean]
19
- def node_type(node_type = nil, null: self.node_nullable)
19
+ # @param field_options [Hash] Any extra arguments to pass to the `field :node` configuration
20
+ def node_type(node_type = nil, null: self.node_nullable, field_options: nil)
20
21
  if node_type
21
22
  @node_type = node_type
22
23
  # Add a default `node` field
23
- field :node, node_type, null: null, description: "The item at the end of the edge.", connection: false
24
+ base_field_options = {
25
+ name: :node,
26
+ type: node_type,
27
+ null: null,
28
+ description: "The item at the end of the edge.",
29
+ connection: false,
30
+ }
31
+ if field_options
32
+ base_field_options.merge!(field_options)
33
+ end
34
+ field(**base_field_options)
24
35
  end
25
36
  @node_type
26
37
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.13.0"
3
+ VERSION = "1.13.1"
4
4
  end
data/lib/graphql.rb CHANGED
@@ -107,6 +107,7 @@ require "graphql/internal_representation"
107
107
  require "graphql/directive"
108
108
  require "graphql/static_validation"
109
109
  require "graphql/execution"
110
+ require "graphql/deprecation"
110
111
  require "graphql/boolean_type"
111
112
  require "graphql/float_type"
112
113
  require "graphql/id_type"
@@ -130,7 +131,6 @@ require "graphql/authorization"
130
131
  require "graphql/unauthorized_error"
131
132
  require "graphql/unauthorized_field_error"
132
133
  require "graphql/load_application_object_failed_error"
133
- require "graphql/deprecation"
134
134
  require "graphql/directive/include_directive"
135
135
  require "graphql/directive/skip_directive"
136
136
  require "graphql/directive/deprecated_directive"
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.13.0
4
+ version: 1.13.1
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-11-24 00:00:00.000000000 Z
11
+ date: 2021-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips