graphql 2.0.28 → 2.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  3. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  4. data/lib/generators/graphql/install_generator.rb +3 -0
  5. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  6. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  7. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  8. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  9. data/lib/generators/graphql/templates/base_field.erb +2 -0
  10. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  11. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  12. data/lib/generators/graphql/templates/base_object.erb +2 -0
  13. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  14. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  15. data/lib/generators/graphql/templates/base_union.erb +2 -0
  16. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  17. data/lib/generators/graphql/templates/loader.erb +2 -0
  18. data/lib/generators/graphql/templates/mutation.erb +2 -0
  19. data/lib/generators/graphql/templates/node_type.erb +2 -0
  20. data/lib/generators/graphql/templates/query_type.erb +2 -0
  21. data/lib/generators/graphql/templates/schema.erb +2 -0
  22. data/lib/graphql/analysis/ast/analyzer.rb +7 -0
  23. data/lib/graphql/analysis/ast/field_usage.rb +32 -7
  24. data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
  25. data/lib/graphql/analysis/ast/query_depth.rb +7 -2
  26. data/lib/graphql/analysis/ast/visitor.rb +2 -2
  27. data/lib/graphql/analysis/ast.rb +21 -11
  28. data/lib/graphql/backtrace/trace.rb +12 -15
  29. data/lib/graphql/coercion_error.rb +1 -9
  30. data/lib/graphql/dataloader/async_dataloader.rb +85 -0
  31. data/lib/graphql/dataloader/request.rb +5 -0
  32. data/lib/graphql/dataloader/source.rb +11 -3
  33. data/lib/graphql/dataloader.rb +109 -142
  34. data/lib/graphql/duration_encoding_error.rb +16 -0
  35. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
  36. data/lib/graphql/execution/interpreter/runtime.rb +79 -248
  37. data/lib/graphql/execution/interpreter.rb +91 -157
  38. data/lib/graphql/execution/lookahead.rb +88 -21
  39. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  40. data/lib/graphql/introspection/entry_points.rb +11 -5
  41. data/lib/graphql/introspection/schema_type.rb +3 -1
  42. data/lib/graphql/language/block_string.rb +34 -18
  43. data/lib/graphql/language/definition_slice.rb +1 -1
  44. data/lib/graphql/language/document_from_schema_definition.rb +37 -37
  45. data/lib/graphql/language/lexer.rb +271 -177
  46. data/lib/graphql/language/nodes.rb +75 -57
  47. data/lib/graphql/language/parser.rb +707 -1986
  48. data/lib/graphql/language/printer.rb +303 -146
  49. data/lib/graphql/language/sanitized_printer.rb +20 -22
  50. data/lib/graphql/language/static_visitor.rb +167 -0
  51. data/lib/graphql/language/visitor.rb +20 -81
  52. data/lib/graphql/language.rb +1 -0
  53. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  54. data/lib/graphql/pagination/array_connection.rb +3 -3
  55. data/lib/graphql/pagination/connection.rb +28 -1
  56. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  57. data/lib/graphql/pagination/relation_connection.rb +3 -3
  58. data/lib/graphql/query/context/scoped_context.rb +101 -0
  59. data/lib/graphql/query/context.rb +36 -98
  60. data/lib/graphql/query/null_context.rb +4 -11
  61. data/lib/graphql/query/validation_pipeline.rb +2 -2
  62. data/lib/graphql/query/variables.rb +3 -3
  63. data/lib/graphql/query.rb +13 -22
  64. data/lib/graphql/railtie.rb +9 -6
  65. data/lib/graphql/rake_task.rb +3 -12
  66. data/lib/graphql/schema/argument.rb +6 -1
  67. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  68. data/lib/graphql/schema/build_from_definition.rb +0 -11
  69. data/lib/graphql/schema/directive/one_of.rb +12 -0
  70. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  71. data/lib/graphql/schema/directive.rb +1 -1
  72. data/lib/graphql/schema/enum.rb +3 -3
  73. data/lib/graphql/schema/field/connection_extension.rb +1 -15
  74. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  75. data/lib/graphql/schema/field.rb +39 -35
  76. data/lib/graphql/schema/has_single_input_argument.rb +156 -0
  77. data/lib/graphql/schema/input_object.rb +2 -2
  78. data/lib/graphql/schema/interface.rb +15 -11
  79. data/lib/graphql/schema/introspection_system.rb +2 -0
  80. data/lib/graphql/schema/loader.rb +0 -2
  81. data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
  82. data/lib/graphql/schema/member/has_arguments.rb +61 -38
  83. data/lib/graphql/schema/member/has_fields.rb +8 -5
  84. data/lib/graphql/schema/member/has_interfaces.rb +23 -9
  85. data/lib/graphql/schema/member/scoped.rb +19 -0
  86. data/lib/graphql/schema/member/validates_input.rb +3 -3
  87. data/lib/graphql/schema/object.rb +8 -0
  88. data/lib/graphql/schema/printer.rb +8 -7
  89. data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
  90. data/lib/graphql/schema/resolver.rb +16 -8
  91. data/lib/graphql/schema/scalar.rb +3 -3
  92. data/lib/graphql/schema/subscription.rb +11 -4
  93. data/lib/graphql/schema/union.rb +1 -1
  94. data/lib/graphql/schema/unique_within_type.rb +1 -1
  95. data/lib/graphql/schema/warden.rb +96 -94
  96. data/lib/graphql/schema.rb +252 -78
  97. data/lib/graphql/static_validation/all_rules.rb +1 -1
  98. data/lib/graphql/static_validation/base_visitor.rb +1 -1
  99. data/lib/graphql/static_validation/literal_validator.rb +2 -3
  100. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  101. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  102. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
  103. data/lib/graphql/static_validation/validation_context.rb +5 -5
  104. data/lib/graphql/static_validation/validator.rb +3 -0
  105. data/lib/graphql/static_validation.rb +0 -1
  106. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
  107. data/lib/graphql/subscriptions/event.rb +8 -2
  108. data/lib/graphql/subscriptions/serialize.rb +2 -0
  109. data/lib/graphql/subscriptions.rb +14 -12
  110. data/lib/graphql/testing/helpers.rb +129 -0
  111. data/lib/graphql/testing.rb +2 -0
  112. data/lib/graphql/tracing/appoptics_trace.rb +2 -2
  113. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  114. data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
  115. data/lib/graphql/tracing/platform_tracing.rb +2 -0
  116. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
  117. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  118. data/lib/graphql/tracing/trace.rb +1 -0
  119. data/lib/graphql/tracing.rb +3 -1
  120. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  121. data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
  122. data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
  123. data/lib/graphql/types.rb +1 -0
  124. data/lib/graphql/version.rb +1 -1
  125. data/lib/graphql.rb +6 -5
  126. data/readme.md +12 -2
  127. metadata +46 -38
  128. data/lib/graphql/deprecation.rb +0 -9
  129. data/lib/graphql/filter.rb +0 -59
  130. data/lib/graphql/language/parser.y +0 -560
  131. data/lib/graphql/schema/base_64_bp.rb +0 -26
  132. data/lib/graphql/static_validation/type_stack.rb +0 -216
  133. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
@@ -4,37 +4,12 @@ require 'set'
4
4
 
5
5
  module GraphQL
6
6
  class Schema
7
- # Restrict access to a {GraphQL::Schema} with a user-defined filter.
7
+ # Restrict access to a {GraphQL::Schema} with a user-defined `visible?` implementations.
8
8
  #
9
9
  # When validating and executing a query, all access to schema members
10
10
  # should go through a warden. If you access the schema directly,
11
11
  # you may show a client something that it shouldn't be allowed to see.
12
12
  #
13
- # @example Hiding private fields
14
- # private_members = -> (member, ctx) { member.metadata[:private] }
15
- # result = Schema.execute(query_string, except: private_members)
16
- #
17
- # @example Custom filter implementation
18
- # # It must respond to `#call(member)`.
19
- # class MissingRequiredFlags
20
- # def initialize(user)
21
- # @user = user
22
- # end
23
- #
24
- # # Return `false` if any required flags are missing
25
- # def call(member, ctx)
26
- # member.metadata[:required_flags].any? do |flag|
27
- # !@user.has_flag?(flag)
28
- # end
29
- # end
30
- # end
31
- #
32
- # # Then, use the custom filter in query:
33
- # missing_required_flags = MissingRequiredFlags.new(current_user)
34
- #
35
- # # This query can only access members which match the user's flags
36
- # result = Schema.execute(query_string, except: missing_required_flags)
37
- #
38
13
  # @api private
39
14
  class Warden
40
15
  def self.from_context(context)
@@ -85,6 +60,7 @@ module GraphQL
85
60
  def visible_type_membership?(tm, ctx); tm.visible?(ctx); end
86
61
  def interface_type_memberships(obj_t, ctx); obj_t.interface_type_memberships; end
87
62
  def arguments(owner, ctx); owner.arguments(ctx); end
63
+ def loadable?(type, ctx); type.visible?(ctx); end
88
64
  end
89
65
  end
90
66
 
@@ -109,33 +85,28 @@ module GraphQL
109
85
  def fields(type_defn); type_defn.all_field_definitions; end # rubocop:disable Development/ContextIsPassedCop
110
86
  def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
111
87
  def reachable_type?(type_name); true; end
88
+ def loadable?(type, _ctx); true; end
112
89
  def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
113
90
  def possible_types(type_defn); @schema.possible_types(type_defn); end
114
91
  def interfaces(obj_type); obj_type.interfaces; end
115
92
  end
116
93
 
117
- # @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
118
94
  # @param context [GraphQL::Query::Context]
119
95
  # @param schema [GraphQL::Schema]
120
- def initialize(filter = nil, context:, schema:)
96
+ def initialize(context:, schema:)
121
97
  @schema = schema
122
98
  # Cache these to avoid repeated hits to the inheritance chain when one isn't present
123
99
  @query = @schema.query
124
100
  @mutation = @schema.mutation
125
101
  @subscription = @schema.subscription
126
102
  @context = context
127
- @visibility_cache = if filter
128
- read_through { |m| filter.call(m, context) }
129
- else
130
- read_through { |m| schema.visible?(m, context) }
131
- end
132
-
103
+ @visibility_cache = read_through { |m| schema.visible?(m, context) }
133
104
  @visibility_cache.compare_by_identity
134
105
  # Initialize all ivars to improve object shape consistency:
135
106
  @types = @visible_types = @reachable_types = @visible_parent_fields =
136
107
  @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
137
108
  @visible_enum_values = @visible_interfaces = @type_visibility = @type_memberships =
138
- @visible_and_reachable_type = @unions = @unfiltered_interfaces = @references_to =
109
+ @visible_and_reachable_type = @unions = @unfiltered_interfaces =
139
110
  @reachable_type_set =
140
111
  nil
141
112
  end
@@ -153,6 +124,11 @@ module GraphQL
153
124
  end
154
125
  end
155
126
 
127
+ # @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
128
+ def loadable?(type, _ctx)
129
+ !reachable_type_set.include?(type) && visible_type?(type)
130
+ end
131
+
156
132
  # @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
157
133
  def get_type(type_name)
158
134
  @visible_types ||= read_through do |name|
@@ -219,7 +195,16 @@ module GraphQL
219
195
  # @param argument_owner [GraphQL::Field, GraphQL::InputObjectType]
220
196
  # @return [Array<GraphQL::Argument>] Visible arguments on `argument_owner`
221
197
  def arguments(argument_owner, ctx = nil)
222
- @visible_arguments ||= read_through { |o| o.arguments(@context).each_value.select { |a| visible_argument?(a, @context) } }
198
+ @visible_arguments ||= read_through { |o|
199
+ args = o.arguments(@context)
200
+ if args.any?
201
+ args = args.values
202
+ args.select! { |a| visible_argument?(a, @context) }
203
+ args
204
+ else
205
+ EmptyObjects::EMPTY_ARRAY
206
+ end
207
+ }
223
208
  @visible_arguments[argument_owner]
224
209
  end
225
210
 
@@ -242,7 +227,13 @@ module GraphQL
242
227
 
243
228
  # @return [Array<GraphQL::InterfaceType>] Visible interfaces implemented by `obj_type`
244
229
  def interfaces(obj_type)
245
- @visible_interfaces ||= read_through { |t| t.interfaces(@context).select { |i| visible_type?(i) } }
230
+ @visible_interfaces ||= read_through { |t|
231
+ ints = t.interfaces(@context)
232
+ if ints.any?
233
+ ints.select! { |i| visible_type?(i) }
234
+ end
235
+ ints
236
+ }
246
237
  @visible_interfaces[obj_type]
247
238
  end
248
239
 
@@ -298,11 +289,26 @@ module GraphQL
298
289
  next true if root_type?(type_defn) || type_defn.introspection?
299
290
 
300
291
  if type_defn.kind.union?
301
- visible_possible_types?(type_defn) && (referenced?(type_defn) || orphan_type?(type_defn))
292
+ possible_types(type_defn).any? && (referenced?(type_defn) || orphan_type?(type_defn))
302
293
  elsif type_defn.kind.interface?
303
- visible_possible_types?(type_defn)
294
+ if possible_types(type_defn).any?
295
+ true
296
+ else
297
+ if @context.respond_to?(:logger) && (logger = @context.logger)
298
+ logger.debug { "Interface `#{type_defn.graphql_name}` hidden because it has no visible implementors" }
299
+ end
300
+ false
301
+ end
304
302
  else
305
- referenced?(type_defn) || visible_abstract_type?(type_defn)
303
+ if referenced?(type_defn)
304
+ true
305
+ elsif type_defn.kind.object?
306
+ # Show this object if it belongs to ...
307
+ interfaces(type_defn).any? { |t| referenced?(t) } || # an interface which is referenced in the schema
308
+ union_memberships(type_defn).any? { |t| referenced?(t) || orphan_type?(t) } # or a union which is referenced or added via orphan_types
309
+ else
310
+ false
311
+ end
306
312
  end
307
313
  end
308
314
 
@@ -357,35 +363,23 @@ module GraphQL
357
363
  end
358
364
 
359
365
  def referenced?(type_defn)
360
- @references_to ||= @schema.references_to
361
366
  graphql_name = type_defn.unwrap.graphql_name
362
- members = @references_to[graphql_name] || NO_REFERENCES
367
+ members = @schema.references_to(graphql_name)
363
368
  members.any? { |m| visible?(m) }
364
369
  end
365
370
 
366
- NO_REFERENCES = [].freeze
367
-
368
371
  def orphan_type?(type_defn)
369
372
  @schema.orphan_types.include?(type_defn)
370
373
  end
371
374
 
372
- def visible_abstract_type?(type_defn)
373
- type_defn.kind.object? && (
374
- interfaces(type_defn).any? ||
375
- union_memberships(type_defn).any?
376
- )
377
- end
378
-
379
- def visible_possible_types?(type_defn)
380
- possible_types(type_defn).any? { |t| visible_and_reachable_type?(t) }
381
- end
382
-
383
375
  def visible?(member)
384
376
  @visibility_cache[member]
385
377
  end
386
378
 
387
379
  def read_through
388
- Hash.new { |h, k| h[k] = yield(k) }
380
+ h = Hash.new { |h, k| h[k] = yield(k) }
381
+ h.compare_by_identity
382
+ h
389
383
  end
390
384
 
391
385
  def reachable_type_set
@@ -416,54 +410,62 @@ module GraphQL
416
410
  end
417
411
  end
418
412
 
413
+ included_interface_possible_types_set = Set.new
414
+
419
415
  until unvisited_types.empty?
420
416
  type = unvisited_types.pop
421
- if @reachable_type_set.add?(type)
422
- type_by_name = rt_hash[type.graphql_name] ||= type
423
- if type_by_name != type
424
- raise DuplicateNamesError.new(
425
- duplicated_name: type.graphql_name, duplicated_definition_1: type.inspect, duplicated_definition_2: type_by_name.inspect
426
- )
417
+ visit_type(type, unvisited_types, @reachable_type_set, rt_hash, included_interface_possible_types_set, include_interface_possible_types: false)
418
+ end
419
+
420
+ @reachable_type_set
421
+ end
422
+
423
+ def visit_type(type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types:)
424
+ if visited_type_set.add?(type) || (include_interface_possible_types && type.kind.interface? && included_interface_possible_types_set.add?(type))
425
+ type_by_name = type_by_name_hash[type.graphql_name] ||= type
426
+ if type_by_name != type
427
+ name_1, name_2 = [type.inspect, type_by_name.inspect].sort
428
+ raise DuplicateNamesError.new(
429
+ duplicated_name: type.graphql_name, duplicated_definition_1: name_1, duplicated_definition_2: name_2
430
+ )
431
+ end
432
+ if type.kind.input_object?
433
+ # recurse into visible arguments
434
+ arguments(type).each do |argument|
435
+ argument_type = argument.type.unwrap
436
+ unvisited_types << argument_type
427
437
  end
428
- if type.kind.input_object?
429
- # recurse into visible arguments
430
- arguments(type).each do |argument|
431
- argument_type = argument.type.unwrap
432
- unvisited_types << argument_type
433
- end
434
- elsif type.kind.union?
435
- # recurse into visible possible types
436
- possible_types(type).each do |possible_type|
437
- unvisited_types << possible_type
438
+ elsif type.kind.union?
439
+ # recurse into visible possible types
440
+ possible_types(type).each do |possible_type|
441
+ unvisited_types << possible_type
442
+ end
443
+ elsif type.kind.fields?
444
+ if type.kind.object?
445
+ # recurse into visible implemented interfaces
446
+ interfaces(type).each do |interface|
447
+ unvisited_types << interface
438
448
  end
439
- elsif type.kind.fields?
440
- if type.kind.interface?
441
- # recurse into visible possible types
442
- possible_types(type).each do |possible_type|
443
- unvisited_types << possible_type
444
- end
445
- elsif type.kind.object?
446
- # recurse into visible implemented interfaces
447
- interfaces(type).each do |interface|
448
- unvisited_types << interface
449
- end
449
+ elsif include_interface_possible_types
450
+ possible_types(type).each do |pt|
451
+ unvisited_types << pt
450
452
  end
453
+ end
454
+ # Don't visit interface possible types -- it's not enough to justify visibility
451
455
 
452
- # recurse into visible fields
453
- fields(type).each do |field|
454
- field_type = field.type.unwrap
455
- unvisited_types << field_type
456
- # recurse into visible arguments
457
- arguments(field).each do |argument|
458
- argument_type = argument.type.unwrap
459
- unvisited_types << argument_type
460
- end
456
+ # recurse into visible fields
457
+ fields(type).each do |field|
458
+ field_type = field.type.unwrap
459
+ # In this case, if it's an interface, we want to include
460
+ visit_type(field_type, unvisited_types, visited_type_set, type_by_name_hash, included_interface_possible_types_set, include_interface_possible_types: true)
461
+ # recurse into visible arguments
462
+ arguments(field).each do |argument|
463
+ argument_type = argument.type.unwrap
464
+ unvisited_types << argument_type
461
465
  end
462
466
  end
463
467
  end
464
468
  end
465
-
466
- @reachable_type_set
467
469
  end
468
470
  end
469
471
  end