graphql 2.4.0 → 2.4.2

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