graphql 2.3.21 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +1 -2
  3. data/lib/graphql/analysis/visitor.rb +0 -2
  4. data/lib/graphql/language/nodes.rb +0 -3
  5. data/lib/graphql/language/static_visitor.rb +33 -37
  6. data/lib/graphql/language/visitor.rb +55 -59
  7. data/lib/graphql/query/null_context.rb +3 -5
  8. data/lib/graphql/schema/argument.rb +5 -3
  9. data/lib/graphql/schema/build_from_definition.rb +7 -8
  10. data/lib/graphql/schema/directive.rb +1 -1
  11. data/lib/graphql/schema/enum.rb +17 -3
  12. data/lib/graphql/schema/enum_value.rb +2 -2
  13. data/lib/graphql/schema/field.rb +2 -2
  14. data/lib/graphql/schema/input_object.rb +7 -6
  15. data/lib/graphql/schema/interface.rb +1 -1
  16. data/lib/graphql/schema/member/has_directives.rb +1 -1
  17. data/lib/graphql/schema/member/has_fields.rb +1 -1
  18. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  19. data/lib/graphql/schema/member/scoped.rb +1 -1
  20. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  21. data/lib/graphql/schema/printer.rb +1 -0
  22. data/lib/graphql/schema/visibility/migration.rb +3 -1
  23. data/lib/graphql/schema/visibility/profile.rb +8 -4
  24. data/lib/graphql/schema/warden.rb +75 -9
  25. data/lib/graphql/schema.rb +15 -1
  26. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +2 -1
  27. data/lib/graphql/static_validation/rules/directives_are_defined.rb +2 -1
  28. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +2 -1
  29. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +11 -1
  30. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +10 -1
  31. data/lib/graphql/static_validation/validation_context.rb +15 -0
  32. data/lib/graphql/testing/helpers.rb +1 -1
  33. data/lib/graphql/tracing/appoptics_trace.rb +0 -4
  34. data/lib/graphql/tracing/appsignal_trace.rb +0 -4
  35. data/lib/graphql/tracing/data_dog_trace.rb +0 -4
  36. data/lib/graphql/tracing/notifications_trace.rb +0 -4
  37. data/lib/graphql/tracing/platform_trace.rb +0 -5
  38. data/lib/graphql/tracing/prometheus_trace.rb +0 -4
  39. data/lib/graphql/tracing/scout_trace.rb +0 -3
  40. data/lib/graphql/tracing/sentry_trace.rb +0 -4
  41. data/lib/graphql/tracing/statsd_trace.rb +0 -4
  42. data/lib/graphql/types/relay/connection_behaviors.rb +1 -1
  43. data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
  44. data/lib/graphql/version.rb +1 -1
  45. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: adab188bde82d618e8c5f71b5318f32787926b4c9df8c68f20a0f747c0c5d870
4
- data.tar.gz: a03d9f8784d47b3374d5651b8ae127019009ae98ee1abb248138532e7db9d1ed
3
+ metadata.gz: 12a25d94ae9348527390bad889be7c918390c0a1b133735612ac5a80ca0c6633
4
+ data.tar.gz: de4bb61a31dc643d359157dff498410f70a5a7bd18d6c254463a2a5294706949
5
5
  SHA512:
6
- metadata.gz: 8ab82a64152e6abb6950591ec691be8212af5ecb2f9abf9672152f5c0d49591301774f83362a1b1646268eb821e86a0cd65bf6fb8c3203ada84d190173b31a11
7
- data.tar.gz: 6985b7afa97388783788c3396f3a02c23f7d46e0c7f4a1c045c6c446161b7e2091238813181c22cfeaa33abe971b5f8f830c34607217f13f4c90e996f21725d8
6
+ metadata.gz: f85c5a5e4ab0083862c990fe6cd3f0ed99d36a414721d4c3d0a7a3e5c59042cdc5611a6b74d10d0e18525d9e51a32dc6aa1a2349c789b04dae75a835f4bb322a
7
+ data.tar.gz: e0304dded75753771417672c704054a1daec13ab428ed92f97dbdcee2771f21874bfb6aedf9bd0054806ed97b4483b98376f03526b129d96628735c1933f0d35
@@ -42,7 +42,6 @@ module GraphQL
42
42
  raise GraphQL::RequiredImplementationMissingError
43
43
  end
44
44
 
45
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
46
45
  class << self
47
46
  private
48
47
 
@@ -73,7 +72,7 @@ module GraphQL
73
72
  build_visitor_hooks :variable_definition
74
73
  build_visitor_hooks :variable_identifier
75
74
  build_visitor_hooks :abstract_node
76
- # rubocop:enable Development/NoEvalCop
75
+
77
76
  protected
78
77
 
79
78
  # @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
@@ -64,7 +64,6 @@ module GraphQL
64
64
  @response_path.dup
65
65
  end
66
66
 
67
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
68
67
  # Visitor Hooks
69
68
  [
70
69
  :operation_definition, :fragment_definition,
@@ -93,7 +92,6 @@ module GraphQL
93
92
 
94
93
  RUBY
95
94
  end
96
- # rubocop:enable Development/NoEvalCop
97
95
 
98
96
  def on_operation_definition(node, parent)
99
97
  object_type = @schema.root_type_for_operation(node.operation_type)
@@ -141,8 +141,6 @@ module GraphQL
141
141
  end
142
142
 
143
143
  class << self
144
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
145
-
146
144
  # Add a default `#visit_method` and `#children_method_name` using the class name
147
145
  def inherited(child_class)
148
146
  super
@@ -345,7 +343,6 @@ module GraphQL
345
343
  RUBY
346
344
  end
347
345
  end
348
- # rubocop:enable Development/NoEvalCop
349
346
  end
350
347
  end
351
348
 
@@ -22,6 +22,39 @@ module GraphQL
22
22
  end
23
23
  end
24
24
 
25
+ # We don't use `alias` here because it breaks `super`
26
+ def self.make_visit_methods(ast_node_class)
27
+ node_method = ast_node_class.visit_method
28
+ children_of_type = ast_node_class.children_of_type
29
+ child_visit_method = :"#{node_method}_children"
30
+
31
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
32
+ # The default implementation for visiting an AST node.
33
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
34
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
35
+ # in your subclasses.
36
+ #
37
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
38
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
39
+ # @return [void]
40
+ def #{node_method}(node, parent)
41
+ #{
42
+ if method_defined?(child_visit_method)
43
+ "#{child_visit_method}(node)"
44
+ elsif children_of_type
45
+ children_of_type.map do |child_accessor, child_class|
46
+ "node.#{child_accessor}.each do |child_node|
47
+ #{child_class.visit_method}(child_node, node)
48
+ end"
49
+ end.join("\n")
50
+ else
51
+ ""
52
+ end
53
+ }
54
+ end
55
+ RUBY
56
+ end
57
+
25
58
  def on_document_children(document_node)
26
59
  document_node.children.each do |child_node|
27
60
  visit_method = child_node.visit_method
@@ -90,41 +123,6 @@ module GraphQL
90
123
  end
91
124
  end
92
125
 
93
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
94
-
95
- # We don't use `alias` here because it breaks `super`
96
- def self.make_visit_methods(ast_node_class)
97
- node_method = ast_node_class.visit_method
98
- children_of_type = ast_node_class.children_of_type
99
- child_visit_method = :"#{node_method}_children"
100
-
101
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
102
- # The default implementation for visiting an AST node.
103
- # It doesn't _do_ anything, but it continues to visiting the node's children.
104
- # To customize this hook, override one of its make_visit_methods (or the base method?)
105
- # in your subclasses.
106
- #
107
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
108
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
109
- # @return [void]
110
- def #{node_method}(node, parent)
111
- #{
112
- if method_defined?(child_visit_method)
113
- "#{child_visit_method}(node)"
114
- elsif children_of_type
115
- children_of_type.map do |child_accessor, child_class|
116
- "node.#{child_accessor}.each do |child_node|
117
- #{child_class.visit_method}(child_node, node)
118
- end"
119
- end.join("\n")
120
- else
121
- ""
122
- end
123
- }
124
- end
125
- RUBY
126
- end
127
-
128
126
  [
129
127
  Language::Nodes::Argument,
130
128
  Language::Nodes::Directive,
@@ -164,8 +162,6 @@ module GraphQL
164
162
  ].each do |ast_node_class|
165
163
  make_visit_methods(ast_node_class)
166
164
  end
167
-
168
- # rubocop:disable Development/NoEvalCop
169
165
  end
170
166
  end
171
167
  end
@@ -61,6 +61,61 @@ module GraphQL
61
61
  end
62
62
  end
63
63
 
64
+ # We don't use `alias` here because it breaks `super`
65
+ def self.make_visit_methods(ast_node_class)
66
+ node_method = ast_node_class.visit_method
67
+ children_of_type = ast_node_class.children_of_type
68
+ child_visit_method = :"#{node_method}_children"
69
+
70
+ class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
71
+ # The default implementation for visiting an AST node.
72
+ # It doesn't _do_ anything, but it continues to visiting the node's children.
73
+ # To customize this hook, override one of its make_visit_methods (or the base method?)
74
+ # in your subclasses.
75
+ #
76
+ # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
77
+ # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
78
+ # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
79
+ def #{node_method}(node, parent)
80
+ if node.equal?(DELETE_NODE)
81
+ # This might be passed to `super(DELETE_NODE, ...)`
82
+ # by a user hook, don't want to keep visiting in that case.
83
+ [node, parent]
84
+ else
85
+ new_node = node
86
+ #{
87
+ if method_defined?(child_visit_method)
88
+ "new_node = #{child_visit_method}(new_node)"
89
+ elsif children_of_type
90
+ children_of_type.map do |child_accessor, child_class|
91
+ "node.#{child_accessor}.each do |child_node|
92
+ new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
93
+ # Reassign `node` in case the child hook makes a modification
94
+ if new_child_and_node.is_a?(Array)
95
+ new_node = new_child_and_node[1]
96
+ end
97
+ end"
98
+ end.join("\n")
99
+ else
100
+ ""
101
+ end
102
+ }
103
+
104
+ if new_node.equal?(node)
105
+ [node, parent]
106
+ else
107
+ [new_node, parent]
108
+ end
109
+ end
110
+ end
111
+
112
+ def #{node_method}_with_modifications(node, parent)
113
+ new_node_and_new_parent = #{node_method}(node, parent)
114
+ apply_modifications(node, parent, new_node_and_new_parent)
115
+ end
116
+ RUBY
117
+ end
118
+
64
119
  def on_document_children(document_node)
65
120
  new_node = document_node
66
121
  document_node.children.each do |child_node|
@@ -161,63 +216,6 @@ module GraphQL
161
216
  new_node
162
217
  end
163
218
 
164
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
165
-
166
- # We don't use `alias` here because it breaks `super`
167
- def self.make_visit_methods(ast_node_class)
168
- node_method = ast_node_class.visit_method
169
- children_of_type = ast_node_class.children_of_type
170
- child_visit_method = :"#{node_method}_children"
171
-
172
- class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
173
- # The default implementation for visiting an AST node.
174
- # It doesn't _do_ anything, but it continues to visiting the node's children.
175
- # To customize this hook, override one of its make_visit_methods (or the base method?)
176
- # in your subclasses.
177
- #
178
- # @param node [GraphQL::Language::Nodes::AbstractNode] the node being visited
179
- # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
180
- # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
181
- def #{node_method}(node, parent)
182
- if node.equal?(DELETE_NODE)
183
- # This might be passed to `super(DELETE_NODE, ...)`
184
- # by a user hook, don't want to keep visiting in that case.
185
- [node, parent]
186
- else
187
- new_node = node
188
- #{
189
- if method_defined?(child_visit_method)
190
- "new_node = #{child_visit_method}(new_node)"
191
- elsif children_of_type
192
- children_of_type.map do |child_accessor, child_class|
193
- "node.#{child_accessor}.each do |child_node|
194
- new_child_and_node = #{child_class.visit_method}_with_modifications(child_node, new_node)
195
- # Reassign `node` in case the child hook makes a modification
196
- if new_child_and_node.is_a?(Array)
197
- new_node = new_child_and_node[1]
198
- end
199
- end"
200
- end.join("\n")
201
- else
202
- ""
203
- end
204
- }
205
-
206
- if new_node.equal?(node)
207
- [node, parent]
208
- else
209
- [new_node, parent]
210
- end
211
- end
212
- end
213
-
214
- def #{node_method}_with_modifications(node, parent)
215
- new_node_and_new_parent = #{node_method}(node, parent)
216
- apply_modifications(node, parent, new_node_and_new_parent)
217
- end
218
- RUBY
219
- end
220
-
221
219
  [
222
220
  Language::Nodes::Argument,
223
221
  Language::Nodes::Directive,
@@ -258,8 +256,6 @@ module GraphQL
258
256
  make_visit_methods(ast_node_class)
259
257
  end
260
258
 
261
- # rubocop:enable Development/NoEvalCop
262
-
263
259
  private
264
260
 
265
261
  def apply_modifications(node, parent, new_node_and_new_parent)
@@ -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
@@ -53,7 +53,6 @@ module GraphQL
53
53
  def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, comment: nil, ast_node: nil, default_value: NOT_CONFIGURED, as: nil, from_resolver: false, camelize: true, prepare: nil, owner:, validates: nil, directives: nil, deprecation_reason: nil, replace_null_with_default: false, &definition_block)
54
54
  arg_name ||= name
55
55
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
56
- NameValidator.validate!(@name)
57
56
  @type_expr = type_expr || type
58
57
  @description = desc || description
59
58
  @comment = comment
@@ -90,8 +89,11 @@ module GraphQL
90
89
  end
91
90
 
92
91
  if definition_block
93
- # `self` will still be self, it will also be the first argument to the block:
94
- instance_exec(self, &definition_block)
92
+ if definition_block.arity == 1
93
+ instance_exec(self, &definition_block)
94
+ else
95
+ instance_eval(&definition_block)
96
+ end
95
97
  end
96
98
  end
97
99
 
@@ -467,18 +467,17 @@ module GraphQL
467
467
 
468
468
  # Don't do this for interfaces
469
469
  if default_resolve
470
- define_field_resolve_method(owner, resolve_method_name, field_definition.name)
470
+ owner.class_eval <<-RUBY, __FILE__, __LINE__
471
+ # frozen_string_literal: true
472
+ def #{resolve_method_name}(**args)
473
+ field_instance = self.class.get_field("#{field_definition.name}")
474
+ context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
475
+ end
476
+ RUBY
471
477
  end
472
478
  end
473
479
  end
474
480
 
475
- def define_field_resolve_method(owner, method_name, field_name)
476
- owner.define_method(method_name) { |**args|
477
- field_instance = self.class.get_field(field_name)
478
- context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
479
- }
480
- end
481
-
482
481
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
483
482
  resolve_type_proc = nil
484
483
  resolve_type_proc = ->(ast_node) {
@@ -99,7 +99,7 @@ module GraphQL
99
99
 
100
100
  def inherited(subclass)
101
101
  super
102
- subclass.class_exec do
102
+ subclass.class_eval do
103
103
  @default_graphql_name ||= nil
104
104
  end
105
105
  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
 
@@ -48,7 +48,7 @@ module GraphQL
48
48
  end
49
49
 
50
50
  if block_given?
51
- instance_exec(self, &block)
51
+ instance_eval(&block)
52
52
  end
53
53
  end
54
54
 
@@ -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
@@ -255,7 +255,7 @@ module GraphQL
255
255
 
256
256
  @underscored_name = -Member::BuildType.underscore(name_s)
257
257
  @name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
258
- NameValidator.validate!(@name)
258
+
259
259
  @description = description
260
260
  @comment = comment
261
261
  @type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
@@ -369,7 +369,7 @@ module GraphQL
369
369
  if @definition_block.arity == 1
370
370
  @definition_block.call(self)
371
371
  else
372
- instance_exec(self, &@definition_block)
372
+ instance_eval(&@definition_block)
373
373
  end
374
374
  self.extensions.each(&:after_define_apply)
375
375
  @call_after_define = true
@@ -132,8 +132,14 @@ module GraphQL
132
132
  end
133
133
  end
134
134
  # Add a method access
135
+ method_name = argument_defn.keyword
135
136
  suppress_redefinition_warning do
136
- define_accessor_method(argument_defn.keyword)
137
+ class_eval <<-RUBY, __FILE__, __LINE__
138
+ def #{method_name}
139
+ self[#{method_name.inspect}]
140
+ end
141
+ alias_method :#{method_name}, :#{method_name}
142
+ RUBY
137
143
  end
138
144
  argument_defn
139
145
  end
@@ -250,11 +256,6 @@ module GraphQL
250
256
  ensure
251
257
  $VERBOSE = verbose
252
258
  end
253
-
254
- def define_accessor_method(method_name)
255
- define_method(method_name) { self[method_name] }
256
- alias_method(method_name, method_name)
257
- end
258
259
  end
259
260
 
260
261
  private
@@ -29,7 +29,7 @@ module GraphQL
29
29
  const_set(:DefinitionMethods, defn_methods_module)
30
30
  extend(self::DefinitionMethods)
31
31
  end
32
- self::DefinitionMethods.module_exec(&block)
32
+ self::DefinitionMethods.module_eval(&block)
33
33
  end
34
34
 
35
35
  # @see {Schema::Warden} hides interfaces without visible implementations
@@ -6,7 +6,7 @@ module GraphQL
6
6
  module HasDirectives
7
7
  def self.extended(child_cls)
8
8
  super
9
- child_cls.module_exec { self.own_directives = nil }
9
+ child_cls.module_eval { self.own_directives = nil }
10
10
  end
11
11
 
12
12
  def inherited(child_cls)
@@ -185,7 +185,7 @@ module GraphQL
185
185
 
186
186
  def inherited(subclass)
187
187
  super
188
- subclass.class_exec do
188
+ subclass.class_eval do
189
189
  @own_fields ||= nil
190
190
  @field_class ||= nil
191
191
  end
@@ -133,7 +133,7 @@ module GraphQL
133
133
 
134
134
  def inherited(subclass)
135
135
  super
136
- subclass.class_exec do
136
+ subclass.class_eval do
137
137
  @own_interface_type_memberships ||= nil
138
138
  end
139
139
  end
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  def inherited(subclass)
32
32
  super
33
- subclass.class_exec do
33
+ subclass.class_eval do
34
34
  @reauthorize_scoped_objects = nil
35
35
  end
36
36
  end
@@ -42,7 +42,7 @@ module GraphQL
42
42
  private
43
43
 
44
44
  def inherited(subclass)
45
- subclass.class_exec do
45
+ subclass.class_eval do
46
46
  @to_non_null_type ||= nil
47
47
  @to_list_type ||= nil
48
48
  end
@@ -58,6 +58,7 @@ module GraphQL
58
58
  end
59
59
  end
60
60
  schema = Class.new(GraphQL::Schema) {
61
+ use GraphQL::Schema::Visibility
61
62
  query(query_root)
62
63
  def self.visible?(member, _ctx)
63
64
  member.graphql_name != "Root"
@@ -96,6 +96,7 @@ module GraphQL
96
96
  end
97
97
  warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
98
98
  warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
99
+ warden_ctx.warden.skip_warning = true
99
100
  warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile
100
101
  end
101
102
  end
@@ -121,7 +122,8 @@ module GraphQL
121
122
  :mutation_root,
122
123
  :possible_types,
123
124
  :subscription_root,
124
- :reachable_type?
125
+ :reachable_type?,
126
+ :visible_enum_value?,
125
127
  ]
126
128
 
127
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)
@@ -19,6 +19,17 @@ 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
+
29
+ def self.use(schema)
30
+ # no-op
31
+ end
32
+
22
33
  # @param visibility_method [Symbol] a Warden method to call for this entry
23
34
  # @param entry [Object, Array<Object>] One or more definitions for a given name in a GraphQL Schema
24
35
  # @param context [GraphQL::Query::Context]
@@ -73,24 +84,20 @@ module GraphQL
73
84
  @visibility_profile = Warden::VisibilityProfile.new(self)
74
85
  end
75
86
 
76
- # @api private
77
- module NullVisibilityProfile
78
- def self.new(context:, schema:)
79
- NullWarden.new(context: context, schema: schema).visibility_profile
80
- end
81
- end
87
+ # No-op, but for compatibility:
88
+ attr_writer :skip_warning
82
89
 
83
90
  attr_reader :visibility_profile
84
91
 
85
92
  def visible_field?(field_defn, _ctx = nil, owner = nil); true; end
86
93
  def visible_argument?(arg_defn, _ctx = nil); true; end
87
94
  def visible_type?(type_defn, _ctx = nil); true; end
88
- 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
89
96
  def visible_type_membership?(type_membership, _ctx = nil); true; end
90
97
  def interface_type_memberships(obj_type, _ctx = nil); obj_type.interface_type_memberships; end
91
98
  def get_type(type_name); @schema.get_type(type_name, Query::NullContext.instance, false); end # rubocop:disable Development/ContextIsPassedCop
92
99
  def arguments(argument_owner, ctx = nil); argument_owner.all_argument_definitions; end
93
- 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
94
101
  def get_argument(parent_type, argument_name); parent_type.get_argument(argument_name); end # rubocop:disable Development/ContextIsPassedCop
95
102
  def types; @schema.types; end # rubocop:disable Development/ContextIsPassedCop
96
103
  def root_type_for_operation(op_name); @schema.root_type_for_operation(op_name); end
@@ -176,6 +183,10 @@ module GraphQL
176
183
  def reachable_type?(type_name)
177
184
  !!@warden.reachable_type?(type_name)
178
185
  end
186
+
187
+ def visible_enum_value?(enum_value, ctx = nil)
188
+ @warden.visible_enum_value?(enum_value, ctx)
189
+ end
179
190
  end
180
191
 
181
192
  # @param context [GraphQL::Query::Context]
@@ -187,7 +198,7 @@ module GraphQL
187
198
  @mutation = @schema.mutation
188
199
  @subscription = @schema.subscription
189
200
  @context = context
190
- @visibility_cache = read_through { |m| schema.visible?(m, context) }
201
+ @visibility_cache = read_through { |m| check_visible(schema, m) }
191
202
  # Initialize all ivars to improve object shape consistency:
192
203
  @types = @visible_types = @reachable_types = @visible_parent_fields =
193
204
  @visible_possible_types = @visible_fields = @visible_arguments = @visible_enum_arrays =
@@ -195,8 +206,11 @@ module GraphQL
195
206
  @visible_and_reachable_type = @unions = @unfiltered_interfaces =
196
207
  @reachable_type_set = @visibility_profile =
197
208
  nil
209
+ @skip_warning = schema.plugins.any? { |(plugin, _opts)| plugin == GraphQL::Schema::Warden }
198
210
  end
199
211
 
212
+ attr_writer :skip_warning
213
+
200
214
  # @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
201
215
  def types
202
216
  @types ||= begin
@@ -465,6 +479,58 @@ module GraphQL
465
479
  Hash.new { |h, k| h[k] = yield(k) }.compare_by_identity
466
480
  end
467
481
 
482
+ def check_visible(schema, member)
483
+ if schema.visible?(member, @context)
484
+ true
485
+ elsif @skip_warning
486
+ false
487
+ else
488
+ member_s = member.respond_to?(:path) ? member.path : member.inspect
489
+ member_type = case member
490
+ when Module
491
+ if member.respond_to?(:kind)
492
+ member.kind.name.downcase
493
+ else
494
+ ""
495
+ end
496
+ when GraphQL::Schema::Field
497
+ "field"
498
+ when GraphQL::Schema::EnumValue
499
+ "enum value"
500
+ when GraphQL::Schema::Argument
501
+ "argument"
502
+ else
503
+ ""
504
+ end
505
+
506
+ schema_s = schema.name ? "#{schema.name}'s" : ""
507
+ schema_name = schema.name ? "#{schema.name}" : "your schema"
508
+ warn(ADD_WARDEN_WARNING % { schema_s: schema_s, schema_name: schema_name, member: member_s, member_type: member_type })
509
+ @skip_warning = true # only warn once per query
510
+ # If there's no schema name, add the backtrace for additional context:
511
+ if schema_s == ""
512
+ puts caller.map { |l| " #{l}"}
513
+ end
514
+ false
515
+ end
516
+ end
517
+
518
+ ADD_WARDEN_WARNING = <<~WARNING
519
+ DEPRECATION: %{schema_s} "%{member}" %{member_type} returned `false` for `.visible?` but `GraphQL::Schema::Visibility` isn't configured yet.
520
+
521
+ Address this warning by adding:
522
+
523
+ use GraphQL::Schema::Visibility
524
+
525
+ to the definition for %{schema_name}. (Future GraphQL-Ruby versions won't check `.visible?` methods by default.)
526
+
527
+ Alternatively, for legacy behavior, add:
528
+
529
+ use GraphQL::Schema::Warden # legacy visibility behavior
530
+
531
+ For more information see: https://graphql-ruby.org/authorization/visibility.html
532
+ WARNING
533
+
468
534
  def reachable_type_set
469
535
  return @reachable_type_set if @reachable_type_set
470
536
 
@@ -1162,7 +1162,7 @@ module GraphQL
1162
1162
  # @return [Object, nil] The application which `object_id` references, or `nil` if there is no object or the current operation shouldn't have access to the object
1163
1163
  # @see id_from_object which produces these IDs
1164
1164
  def object_from_id(object_id, context)
1165
- raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(object_id, context) must be implemented to load by ID (tried to load from id `#{node_id}`)"
1165
+ raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(object_id, context) must be implemented to load by ID (tried to load from id `#{object_id}`)"
1166
1166
  end
1167
1167
 
1168
1168
  # Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
@@ -1575,6 +1575,20 @@ module GraphQL
1575
1575
  end
1576
1576
  end
1577
1577
 
1578
+ # Returns `DidYouMean` if it's defined.
1579
+ # Override this to return `nil` if you don't want to use `DidYouMean`
1580
+ def did_you_mean(new_dym = NOT_CONFIGURED)
1581
+ if NOT_CONFIGURED.equal?(new_dym)
1582
+ if defined?(@did_you_mean)
1583
+ @did_you_mean
1584
+ else
1585
+ find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
1586
+ end
1587
+ else
1588
+ @did_you_mean = new_dym
1589
+ end
1590
+ end
1591
+
1578
1592
  private
1579
1593
 
1580
1594
  def add_trace_options_for(mode, new_options)
@@ -10,8 +10,9 @@ module GraphQL
10
10
  elsif parent_defn
11
11
  kind_of_node = node_type(parent)
12
12
  error_arg_name = parent_name(parent, parent_defn)
13
+ arg_names = context.types.arguments(parent_defn).map(&:graphql_name)
13
14
  add_error(GraphQL::StaticValidation::ArgumentsAreDefinedError.new(
14
- "#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'",
15
+ "#{kind_of_node} '#{error_arg_name}' doesn't accept argument '#{node.name}'#{context.did_you_mean_suggestion(node.name, arg_names)}",
15
16
  nodes: node,
16
17
  name: error_arg_name,
17
18
  type: kind_of_node,
@@ -10,8 +10,9 @@ module GraphQL
10
10
  if !@types.directive_exists?(node.name)
11
11
  @directives_are_defined_errors_by_name ||= {}
12
12
  error = @directives_are_defined_errors_by_name[node.name] ||= begin
13
+ @directive_names ||= @types.directives.map(&:graphql_name)
13
14
  err = GraphQL::StaticValidation::DirectivesAreDefinedError.new(
14
- "Directive @#{node.name} is not defined",
15
+ "Directive @#{node.name} is not defined#{context.did_you_mean_suggestion(node.name, @directive_names)}",
15
16
  nodes: [],
16
17
  directive: node.name
17
18
  )
@@ -14,8 +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
18
  add_error(GraphQL::StaticValidation::FieldsAreDefinedOnTypeError.new(
18
- "Field '#{node.name}' doesn't exist on type '#{parent_type.graphql_name}'",
19
+ message,
19
20
  nodes: node,
20
21
  field: node.name,
21
22
  type: parent_type.graphql_name
@@ -23,8 +23,18 @@ module GraphQL
23
23
  type_name = fragment_node.type.name
24
24
  type = @types.type(type_name)
25
25
  if type.nil?
26
+ @all_possible_fragment_type_names ||= begin
27
+ names = []
28
+ context.types.all_types.each do |type|
29
+ if type.kind.fields?
30
+ names << type.graphql_name
31
+ end
32
+ end
33
+ names
34
+ end
35
+
26
36
  add_error(GraphQL::StaticValidation::FragmentTypesExistError.new(
27
- "No such type #{type_name}, so it can't be a fragment condition",
37
+ "No such type #{type_name}, so it can't be a fragment condition#{context.did_you_mean_suggestion(type_name, @all_possible_fragment_type_names)}",
28
38
  nodes: fragment_node,
29
39
  type: type_name
30
40
  ))
@@ -7,8 +7,17 @@ module GraphQL
7
7
  type = context.query.types.type(type_name)
8
8
 
9
9
  if type.nil?
10
+ @all_possible_input_type_names ||= begin
11
+ names = []
12
+ context.types.all_types.each { |(t)|
13
+ if t.kind.input?
14
+ names << t.graphql_name
15
+ end
16
+ }
17
+ names
18
+ end
10
19
  add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new(
11
- "#{type_name} isn't a defined input type (on $#{node.name})",
20
+ "#{type_name} isn't a defined input type (on $#{node.name})#{context.did_you_mean_suggestion(type_name, @all_possible_input_type_names)}",
12
21
  nodes: node,
13
22
  name: node.name,
14
23
  type: type_name
@@ -48,6 +48,21 @@ module GraphQL
48
48
  def schema_directives
49
49
  @schema_directives ||= schema.directives
50
50
  end
51
+
52
+ def did_you_mean_suggestion(name, options)
53
+ if did_you_mean = schema.did_you_mean
54
+ suggestions = did_you_mean::SpellChecker.new(dictionary: options).correct(name)
55
+ case suggestions.size
56
+ when 0
57
+ ""
58
+ when 1
59
+ " (Did you mean `#{suggestions.first}`?)"
60
+ else
61
+ last_sugg = suggestions.pop
62
+ " (Did you mean #{suggestions.map {|s| "`#{s}`"}.join(", ")} or `#{last_sugg}`?)"
63
+ end
64
+ end
65
+ end
51
66
  end
52
67
  end
53
68
  end
@@ -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
@@ -28,8 +28,6 @@ module GraphQL
28
28
  Gem::Version.new('1.0.0')
29
29
  end
30
30
 
31
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
32
-
33
31
  [
34
32
  'lex',
35
33
  'parse',
@@ -57,8 +55,6 @@ module GraphQL
57
55
  RUBY
58
56
  end
59
57
 
60
- # rubocop:enable Development/NoEvalCop
61
-
62
58
  def execute_field(query:, field:, ast_node:, arguments:, object:)
63
59
  return_type = field.type.unwrap
64
60
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -13,8 +13,6 @@ module GraphQL
13
13
  super
14
14
  end
15
15
 
16
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
16
  {
19
17
  "lex" => "lex.graphql",
20
18
  "parse" => "parse.graphql",
@@ -45,8 +43,6 @@ module GraphQL
45
43
  RUBY
46
44
  end
47
45
 
48
- # rubocop:enable Development/NoEvalCop
49
-
50
46
  def platform_execute_field(platform_key)
51
47
  Appsignal.instrument(platform_key) do
52
48
  yield
@@ -20,8 +20,6 @@ module GraphQL
20
20
  super
21
21
  end
22
22
 
23
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
24
-
25
23
  {
26
24
  'lex' => 'lex.graphql',
27
25
  'parse' => 'parse.graphql',
@@ -71,8 +69,6 @@ module GraphQL
71
69
  RUBY
72
70
  end
73
71
 
74
- # rubocop:enable Development/NoEvalCop
75
-
76
72
  def execute_field_span(span_key, query, field, ast_node, arguments, object)
77
73
  return_type = field.type.unwrap
78
74
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -16,8 +16,6 @@ module GraphQL
16
16
  super
17
17
  end
18
18
 
19
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
-
21
19
  {
22
20
  "lex" => "lex.graphql",
23
21
  "parse" => "parse.graphql",
@@ -41,8 +39,6 @@ module GraphQL
41
39
  RUBY
42
40
  end
43
41
 
44
- # rubocop:enable Development/NoEvalCop
45
-
46
42
  include PlatformTrace
47
43
  end
48
44
  end
@@ -39,9 +39,6 @@ module GraphQL
39
39
  include(BaseKeyCache)
40
40
  }
41
41
  child_class.const_set(:KeyCache, key_methods_class)
42
-
43
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
44
-
45
42
  [:execute_field, :execute_field_lazy].each do |field_trace_method|
46
43
  if !child_class.method_defined?(field_trace_method)
47
44
  child_class.module_eval <<-RUBY, __FILE__, __LINE__
@@ -94,8 +91,6 @@ module GraphQL
94
91
  end
95
92
  RUBY
96
93
  end
97
-
98
- # rubocop:enable Development/NoEvalCop
99
94
  end
100
95
  end
101
96
 
@@ -13,8 +13,6 @@ module GraphQL
13
13
  super(**rest)
14
14
  end
15
15
 
16
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
17
-
18
16
  {
19
17
  'lex' => "graphql.lex",
20
18
  'parse' => "graphql.parse",
@@ -32,8 +30,6 @@ module GraphQL
32
30
  RUBY
33
31
  end
34
32
 
35
- # rubocop:enable Development/NoEvalCop
36
-
37
33
  def platform_execute_field(platform_key, &block)
38
34
  instrument_prometheus_execution(platform_key, "execute_field", &block)
39
35
  end
@@ -16,8 +16,6 @@ module GraphQL
16
16
  super
17
17
  end
18
18
 
19
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
-
21
19
  {
22
20
  "lex" => "lex.graphql",
23
21
  "parse" => "parse.graphql",
@@ -47,7 +45,6 @@ module GraphQL
47
45
  end
48
46
  RUBY
49
47
  end
50
- # rubocop:enable Development/NoEvalCop
51
48
 
52
49
  def platform_execute_field(platform_key, &block)
53
50
  self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
@@ -23,8 +23,6 @@ module GraphQL
23
23
  instrument_sentry_execution("graphql.execute", "execute_query", data) { super }
24
24
  end
25
25
 
26
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
27
-
28
26
  {
29
27
  "lex" => "graphql.lex",
30
28
  "parse" => "graphql.parse",
@@ -41,8 +39,6 @@ module GraphQL
41
39
  RUBY
42
40
  end
43
41
 
44
- # rubocop:enable Development/NoEvalCop
45
-
46
42
  def platform_execute_field(platform_key, &block)
47
43
  instrument_sentry_execution(platform_key, "execute_field", &block)
48
44
  end
@@ -11,8 +11,6 @@ module GraphQL
11
11
  super(**rest)
12
12
  end
13
13
 
14
- # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
15
-
16
14
  {
17
15
  'lex' => "graphql.lex",
18
16
  'parse' => "graphql.parse",
@@ -32,8 +30,6 @@ module GraphQL
32
30
  RUBY
33
31
  end
34
32
 
35
- # rubocop:enable Development/NoEvalCop
36
-
37
33
  def platform_execute_field(platform_key, &block)
38
34
  @statsd.time(platform_key, &block)
39
35
  end
@@ -13,7 +13,7 @@ module GraphQL
13
13
  child_class.node_nullable(true)
14
14
  child_class.edges_nullable(true)
15
15
  child_class.edge_nullable(true)
16
- child_class.module_exec {
16
+ child_class.module_eval {
17
17
  self.edge_type = nil
18
18
  self.node_type = nil
19
19
  self.edge_class = nil
@@ -8,7 +8,7 @@ module GraphQL
8
8
  child_class.description("An edge in a connection.")
9
9
  child_class.field(:cursor, String, null: false, description: "A cursor for use in pagination.")
10
10
  child_class.extend(ClassMethods)
11
- child_class.class_exec { self.node_type = nil }
11
+ child_class.class_eval { self.node_type = nil }
12
12
  child_class.node_nullable(true)
13
13
  child_class.default_broadcastable(nil)
14
14
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.3.21"
3
+ VERSION = "2.4.1"
4
4
  end
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.21
4
+ version: 2.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 2025-03-12 00:00:00.000000000 Z
11
+ date: 2024-11-04 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: base64
@@ -649,6 +650,7 @@ metadata:
649
650
  bug_tracker_uri: https://github.com/rmosolgo/graphql-ruby/issues
650
651
  mailing_list_uri: https://buttondown.email/graphql-ruby
651
652
  rubygems_mfa_required: 'true'
653
+ post_install_message:
652
654
  rdoc_options: []
653
655
  require_paths:
654
656
  - lib
@@ -663,7 +665,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
663
665
  - !ruby/object:Gem::Version
664
666
  version: '0'
665
667
  requirements: []
666
- rubygems_version: 3.6.3
668
+ rubygems_version: 3.5.12
669
+ signing_key:
667
670
  specification_version: 4
668
671
  summary: A GraphQL language and runtime for Ruby
669
672
  test_files: []