graphql 2.4.0 → 2.4.2

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26e43b0bc48317698ed17f8a11498c19a7a4a0df9d33fdc9785edc47ae1147b6
4
- data.tar.gz: 74402e930ebe03a451bc2bdb82285642aeeba7222ab491dd9329400fc852eb41
3
+ metadata.gz: bbf9779d0fbc1da481a481d987489b980818c5ea6afb352f4e65f0654ea14163
4
+ data.tar.gz: 91528aabb657b8602c7f3143b6a9dc3a770a336f6a31af0f89fce1c27e5e00a0
5
5
  SHA512:
6
- metadata.gz: f1d97c4397ca8410f6b62c3ea2de9a0a4b18ca610adcb92de9676c5377b00a071cbff4fbd0396760eea9ab359e4b0b8cdfff1630c9fa83de51cfd31b0b96307f
7
- data.tar.gz: d9250a9ad1d57f40e7b0151484f77a66e11a361f8b0f50938f42219eb8322a9b4dc59c59102f4b49f62a28e90f03b8f9e0cf5b2799b5fd5080dbcf443cebac15
6
+ metadata.gz: c73cedf9cd1fc6ab4d91f026dfe627cb1490d5888d1252f591b944359e1f537a479c5d7f7ceed691ddd79883ae1ba027dfb952e969d14617e6a19becee792aa6
7
+ data.tar.gz: 4d429bb74350c9fbbaa0e33498ccb193acfa6691f62342b38923965e69bc23bb377e6ade8a56b8f5d5c785ea24a2f6e04a47a8384000a45140d320e7461c6832
@@ -18,17 +18,15 @@ module GraphQL
18
18
  extend Forwardable
19
19
 
20
20
  attr_reader :schema, :query, :warden, :dataloader
21
- def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?
21
+ def_delegators GraphQL::EmptyObjects::EMPTY_HASH, :[], :fetch, :dig, :key?, :to_h
22
22
 
23
23
  def initialize
24
24
  @query = NullQuery.new
25
25
  @dataloader = GraphQL::Dataloader::NullDataloader.new
26
26
  @schema = NullSchema
27
27
  @warden = Schema::Warden::NullWarden.new(context: self, schema: @schema)
28
- end
29
-
30
- def types
31
- @types ||= Schema::Warden::VisibilityProfile.new(@warden)
28
+ @types = @warden.visibility_profile
29
+ freeze
32
30
  end
33
31
  end
34
32
  end
@@ -86,10 +86,24 @@ module GraphQL
86
86
  def enum_values(context = GraphQL::Query::NullContext.instance)
87
87
  inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
88
88
  visible_values = []
89
- warden = Warden.from_context(context)
89
+ types = Warden.types_from_context(context)
90
90
  own_values.each do |key, values_entry|
91
- if (v = Warden.visible_entry?(:visible_enum_value?, values_entry, context, warden))
92
- visible_values << v
91
+ visible_value = nil
92
+ if values_entry.is_a?(Array)
93
+ values_entry.each do |v|
94
+ if types.visible_enum_value?(v, context)
95
+ if visible_value.nil?
96
+ visible_value = v
97
+ visible_values << v
98
+ else
99
+ raise DuplicateNamesError.new(
100
+ duplicated_name: v.path, duplicated_definition_1: visible_value.inspect, duplicated_definition_2: v.inspect
101
+ )
102
+ end
103
+ end
104
+ end
105
+ elsif types.visible_enum_value?(values_entry, context)
106
+ visible_values << values_entry
93
107
  end
94
108
  end
95
109
 
@@ -74,7 +74,7 @@ module GraphQL
74
74
  end
75
75
 
76
76
  def inspect
77
- "#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}>"
77
+ "#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}#{deprecation_reason ? " @deprecation_reason=#{deprecation_reason.inspect}" : ""}>"
78
78
  end
79
79
 
80
80
  def visible?(_ctx); true; end
@@ -122,7 +122,8 @@ module GraphQL
122
122
  :mutation_root,
123
123
  :possible_types,
124
124
  :subscription_root,
125
- :reachable_type?
125
+ :reachable_type?,
126
+ :visible_enum_value?,
126
127
  ]
127
128
 
128
129
  PUBLIC_PROFILE_METHODS.each do |profile_method|
@@ -22,9 +22,9 @@ module GraphQL
22
22
  end
23
23
  end
24
24
 
25
- def self.pass_thru(context:, schema:)
26
- profile = self.new(context: context, schema: schema)
27
- profile.instance_variable_set(:@cached_visible, Hash.new { |h,k| h[k] = true })
25
+ def self.null_profile(context:, schema:)
26
+ profile = self.new(name: "NullProfile", context: context, schema: schema)
27
+ profile.instance_variable_set(:@cached_visible, Hash.new { |k, v| k[v] = true }.compare_by_identity)
28
28
  profile
29
29
  end
30
30
 
@@ -123,7 +123,7 @@ module GraphQL
123
123
  end.compare_by_identity
124
124
 
125
125
  @cached_enum_values = Hash.new do |h, enum_t|
126
- values = non_duplicate_items(enum_t.all_enum_value_definitions, @cached_visible)
126
+ values = non_duplicate_items(enum_t.enum_values(@context), @cached_visible)
127
127
  if values.size == 0
128
128
  raise GraphQL::Schema::Enum::MissingValuesError.new(enum_t)
129
129
  end
@@ -327,6 +327,10 @@ module GraphQL
327
327
  !!@all_types[name]
328
328
  end
329
329
 
330
+ def visible_enum_value?(enum_value, _ctx = nil)
331
+ @cached_visible[enum_value]
332
+ end
333
+
330
334
  private
331
335
 
332
336
  def add_if_visible(t)
@@ -400,7 +404,7 @@ module GraphQL
400
404
  end
401
405
  end
402
406
 
403
- entry_point_types.compact! # TODO why is this necessary?!
407
+ entry_point_types.compact! # Root types might be nil
404
408
  entry_point_types.flatten! # handle multiple defns
405
409
  entry_point_types.each { |t| add_type(t, true) }
406
410
 
@@ -21,11 +21,27 @@ module GraphQL
21
21
  if migration_errors
22
22
  schema.visibility_profile_class = Migration
23
23
  end
24
+ @preload = preload
24
25
  @profiles = profiles
25
26
  @cached_profiles = {}
26
27
  @dynamic = dynamic
27
28
  @migration_errors = migration_errors
28
29
  if preload
30
+ # Traverse the schema now (and in the *_configured hooks below)
31
+ # To make sure things are loaded during boot
32
+ @preloaded_types = Set.new
33
+ types_to_visit = [
34
+ @schema.query,
35
+ @schema.mutation,
36
+ @schema.subscription,
37
+ *@schema.introspection_system.types.values,
38
+ *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
39
+ *@schema.orphan_types,
40
+ ]
41
+ # Root types may have been nil:
42
+ types_to_visit.compact!
43
+ ensure_all_loaded(types_to_visit)
44
+
29
45
  profiles.each do |profile_name, example_ctx|
30
46
  example_ctx[:visibility_profile] = profile_name
31
47
  prof = profile_for(example_ctx, profile_name)
@@ -34,6 +50,45 @@ module GraphQL
34
50
  end
35
51
  end
36
52
 
53
+ # @api private
54
+ def query_configured(query_type)
55
+ if @preload
56
+ ensure_all_loaded([query_type])
57
+ end
58
+ end
59
+
60
+ # @api private
61
+ def mutation_configured(mutation_type)
62
+ if @preload
63
+ ensure_all_loaded([mutation_type])
64
+ end
65
+ end
66
+
67
+ # @api private
68
+ def subscription_configured(subscription_type)
69
+ if @preload
70
+ ensure_all_loaded([subscription_type])
71
+ end
72
+ end
73
+
74
+ # @api private
75
+ def orphan_types_configured(orphan_types)
76
+ if @preload
77
+ ensure_all_loaded(orphan_types)
78
+ end
79
+ end
80
+
81
+ # @api private
82
+ def introspection_system_configured(introspection_system)
83
+ if @preload
84
+ introspection_types = [
85
+ *@schema.introspection_system.types.values,
86
+ *@schema.introspection_system.entry_points.map { |ep| ep.type.unwrap },
87
+ ]
88
+ ensure_all_loaded(introspection_types)
89
+ end
90
+ end
91
+
37
92
  # Make another Visibility for `schema` based on this one
38
93
  # @return [Visibility]
39
94
  # @api private
@@ -70,6 +125,19 @@ module GraphQL
70
125
  @schema.visibility_profile_class.new(context: context, schema: @schema)
71
126
  end
72
127
  end
128
+
129
+ private
130
+
131
+ def ensure_all_loaded(types_to_visit)
132
+ while (type = types_to_visit.shift)
133
+ if type.kind.fields? && @preloaded_types.add?(type)
134
+ type.all_field_definitions.each do |field_defn|
135
+ field_defn.ensure_loaded
136
+ types_to_visit << field_defn.type.unwrap
137
+ end
138
+ end
139
+ end
140
+ end
73
141
  end
74
142
  end
75
143
  end
@@ -19,6 +19,13 @@ module GraphQL
19
19
  PassThruWarden
20
20
  end
21
21
 
22
+ def self.types_from_context(context)
23
+ context.types || PassThruWarden
24
+ rescue NoMethodError
25
+ # this might be a hash which won't respond to #warden
26
+ PassThruWarden
27
+ end
28
+
22
29
  def self.use(schema)
23
30
  # no-op
24
31
  end
@@ -80,24 +87,17 @@ module GraphQL
80
87
  # No-op, but for compatibility:
81
88
  attr_writer :skip_warning
82
89
 
83
- # @api private
84
- module NullVisibilityProfile
85
- def self.new(context:, schema:)
86
- NullWarden.new(context: context, schema: schema).visibility_profile
87
- end
88
- end
89
-
90
90
  attr_reader :visibility_profile
91
91
 
92
92
  def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
93
93
  def visible_argument?(arg_defn, _ctx = nil); true; end
94
94
  def visible_type?(type_defn, _ctx = nil); true; end
95
- def visible_enum_value?(enum_value, _ctx = nil); true; end
95
+ def visible_enum_value?(enum_value, _ctx = nil); enum_value.visible?(Query::NullContext.instance); end
96
96
  def visible_type_membership?(type_membership, _ctx = nil); true; end
97
97
  def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
98
98
  def get_type(type_name); @schema.get_type(type_name, Query::NullContext.instance, false); end # rubocop:disable Development/ContextIsPassedCop
99
99
  def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end
100
- def enum_values(enum_defn); enum_defn.enum_values; end # rubocop:disable Development/ContextIsPassedCop
100
+ def enum_values(enum_defn); enum_defn.enum_values(Query::NullContext.instance); end # rubocop:disable Development/ContextIsPassedCop
101
101
  def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
102
102
  def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop
103
103
  def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end
@@ -183,6 +183,10 @@ module GraphQL
183
183
  def reachable_type?(type_name)
184
184
  !!@warden.reachable_type?(type_name)
185
185
  end
186
+
187
+ def visible_enum_value?(enum_value, ctx = nil)
188
+ @warden.visible_enum_value?(enum_value, ctx)
189
+ end
186
190
  end
187
191
 
188
192
  # @param context [GraphQL::Query::Context]
@@ -445,7 +445,12 @@ module GraphQL
445
445
  dup_defn = new_query_object || yield
446
446
  raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
447
447
  elsif use_visibility_profile?
448
- @query_object = block_given? ? lazy_load_block : new_query_object
448
+ if block_given?
449
+ @query_object = lazy_load_block
450
+ else
451
+ @query_object = new_query_object
452
+ self.visibility.query_configured(@query_object)
453
+ end
449
454
  else
450
455
  @query_object = new_query_object || lazy_load_block.call
451
456
  add_type_and_traverse(@query_object, root: true)
@@ -453,6 +458,8 @@ module GraphQL
453
458
  nil
454
459
  elsif @query_object.is_a?(Proc)
455
460
  @query_object = @query_object.call
461
+ self.visibility&.query_configured(@query_object)
462
+ @query_object
456
463
  else
457
464
  @query_object || find_inherited_value(:query)
458
465
  end
@@ -472,7 +479,12 @@ module GraphQL
472
479
  dup_defn = new_mutation_object || yield
473
480
  raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
474
481
  elsif use_visibility_profile?
475
- @mutation_object = block_given? ? lazy_load_block : new_mutation_object
482
+ if block_given?
483
+ @mutation_object = lazy_load_block
484
+ else
485
+ @mutation_object = new_mutation_object
486
+ self.visibility.mutation_configured(@mutation_object)
487
+ end
476
488
  else
477
489
  @mutation_object = new_mutation_object || lazy_load_block.call
478
490
  add_type_and_traverse(@mutation_object, root: true)
@@ -480,6 +492,8 @@ module GraphQL
480
492
  nil
481
493
  elsif @mutation_object.is_a?(Proc)
482
494
  @mutation_object = @mutation_object.call
495
+ self.visibility&.mutation_configured(@query_object)
496
+ @mutation_object
483
497
  else
484
498
  @mutation_object || find_inherited_value(:mutation)
485
499
  end
@@ -499,7 +513,12 @@ module GraphQL
499
513
  dup_defn = new_subscription_object || yield
500
514
  raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
501
515
  elsif use_visibility_profile?
502
- @subscription_object = block_given? ? lazy_load_block : new_subscription_object
516
+ if block_given?
517
+ @subscription_object = lazy_load_block
518
+ else
519
+ @subscription_object = new_subscription_object
520
+ self.visibility.subscription_configured(@subscription_object)
521
+ end
503
522
  add_subscription_extension_if_necessary
504
523
  else
505
524
  @subscription_object = new_subscription_object || lazy_load_block.call
@@ -510,6 +529,7 @@ module GraphQL
510
529
  elsif @subscription_object.is_a?(Proc)
511
530
  @subscription_object = @subscription_object.call
512
531
  add_subscription_extension_if_necessary
532
+ self.visibility.subscription_configured(@subscription_object)
513
533
  @subscription_object
514
534
  else
515
535
  @subscription_object || find_inherited_value(:subscription)
@@ -695,20 +715,27 @@ module GraphQL
695
715
  type.fields(context)
696
716
  end
697
717
 
718
+ # Pass a custom introspection module here to use it for this schema.
719
+ # @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
720
+ # @return [Module, nil] The configured namespace, if there is one
698
721
  def introspection(new_introspection_namespace = nil)
699
722
  if new_introspection_namespace
700
723
  @introspection = new_introspection_namespace
701
724
  # reset this cached value:
702
725
  @introspection_system = nil
726
+ introspection_system
727
+ @introspection
703
728
  else
704
729
  @introspection || find_inherited_value(:introspection)
705
730
  end
706
731
  end
707
732
 
733
+ # @return [Schema::IntrospectionSystem] Based on {introspection}
708
734
  def introspection_system
709
735
  if !@introspection_system
710
736
  @introspection_system = Schema::IntrospectionSystem.new(self)
711
737
  @introspection_system.resolve_late_bindings
738
+ self.visibility&.introspection_system_configured(@introspection_system)
712
739
  end
713
740
  @introspection_system
714
741
  end
@@ -952,6 +979,13 @@ module GraphQL
952
979
  end
953
980
  end
954
981
 
982
+ # Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
983
+ #
984
+ # This method must be used when an object type is connected to the schema as an interface implementor but
985
+ # not as a return type of a field. In that case, if the object type isn't registered here, GraphQL-Ruby won't be able to find it.
986
+ #
987
+ # @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
988
+ # @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
955
989
  def orphan_types(*new_orphan_types)
956
990
  if new_orphan_types.any?
957
991
  new_orphan_types = new_orphan_types.flatten
@@ -968,6 +1002,7 @@ module GraphQL
968
1002
  end
969
1003
  add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
970
1004
  own_orphan_types.concat(new_orphan_types.flatten)
1005
+ self.visibility&.orphan_types_configured(new_orphan_types)
971
1006
  end
972
1007
 
973
1008
  inherited_ot = find_inherited_value(:orphan_types, nil)
@@ -14,7 +14,9 @@ module GraphQL
14
14
  node_name: parent_type.graphql_name
15
15
  ))
16
16
  else
17
- message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{context.did_you_mean_suggestion(node.name, context.types.fields(parent_type).map(&:graphql_name))}"
17
+ possible_fields = possible_fields(context, parent_type)
18
+ suggestion = context.did_you_mean_suggestion(node.name, possible_fields)
19
+ message = "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'#{suggestion}"
18
20
  add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
19
21
  message,
20
22
  nodes: node,
@@ -26,6 +28,13 @@ module GraphQL
26
28
  super
27
29
  end
28
30
  end
31
+
32
+ private
33
+
34
+ def possible_fields(context, parent_type)
35
+ return EmptyObjects::EMPTY_ARRAY if parent_type.kind.leaf?
36
+ context.types.fields(parent_type).map(&:graphql_name)
37
+ end
29
38
  end
30
39
  end
31
40
  end
@@ -25,7 +25,7 @@ module GraphQL
25
25
  def validate_field_selections(ast_node, resolved_type)
26
26
  msg = if resolved_type.nil?
27
27
  nil
28
- elsif resolved_type.kind.scalar? && ast_node.selections.any?
28
+ elsif ast_node.selections.any? && resolved_type.kind.leaf?
29
29
  selection_strs = ast_node.selections.map do |n|
30
30
  case n
31
31
  when GraphQL::Language::Nodes::InlineFragment
@@ -38,7 +38,7 @@ module GraphQL
38
38
  raise "Invariant: unexpected selection node: #{n}"
39
39
  end
40
40
  end
41
- "Selections can't be made on scalars (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{selection_strs.join(", ")}])"
41
+ "Selections can't be made on #{resolved_type.kind.name.sub("_", " ").downcase}s (%{node_name} returns #{resolved_type.graphql_name} but has selections [#{selection_strs.join(", ")}])"
42
42
  elsif resolved_type.kind.fields? && ast_node.selections.empty?
43
43
  "Field must have selections (%{node_name} returns #{resolved_type.graphql_name} but has no selections. Did you mean '#{ast_node.name} { ... }'?)"
44
44
  else
@@ -92,7 +92,7 @@ module GraphQL
92
92
  end
93
93
  graphql_result
94
94
  else
95
- unfiltered_type = Schema::Visibility::Profile.pass_thru(schema: schema, context: context).type(type_name)
95
+ unfiltered_type = Schema::Visibility::Profile.null_profile(schema: schema, context: context).type(type_name)
96
96
  if unfiltered_type
97
97
  raise TypeNotVisibleError.new(type_name: type_name)
98
98
  else
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.4.0"
3
+ VERSION = "2.4.2"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-31 00:00:00.000000000 Z
11
+ date: 2024-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64