graphql 1.13.0 → 1.13.1

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