graphql 2.4.11 → 2.4.13

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/analyzer.rb +2 -1
  3. data/lib/graphql/analysis/visitor.rb +37 -40
  4. data/lib/graphql/analysis.rb +12 -9
  5. data/lib/graphql/execution/interpreter/runtime.rb +1 -1
  6. data/lib/graphql/invalid_null_error.rb +1 -5
  7. data/lib/graphql/language/lexer.rb +7 -3
  8. data/lib/graphql/language/nodes.rb +3 -0
  9. data/lib/graphql/language/parser.rb +1 -1
  10. data/lib/graphql/language/static_visitor.rb +37 -33
  11. data/lib/graphql/language/visitor.rb +59 -55
  12. data/lib/graphql/schema/argument.rb +3 -5
  13. data/lib/graphql/schema/build_from_definition.rb +8 -7
  14. data/lib/graphql/schema/directive.rb +1 -1
  15. data/lib/graphql/schema/enum.rb +2 -2
  16. data/lib/graphql/schema/enum_value.rb +1 -1
  17. data/lib/graphql/schema/field.rb +2 -2
  18. data/lib/graphql/schema/input_object.rb +7 -8
  19. data/lib/graphql/schema/interface.rb +1 -1
  20. data/lib/graphql/schema/member/has_directives.rb +1 -1
  21. data/lib/graphql/schema/member/has_fields.rb +1 -1
  22. data/lib/graphql/schema/member/has_interfaces.rb +1 -1
  23. data/lib/graphql/schema/member/scoped.rb +1 -1
  24. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  25. data/lib/graphql/schema/resolver.rb +5 -1
  26. data/lib/graphql/schema.rb +3 -3
  27. data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
  28. data/lib/graphql/tracing/appoptics_trace.rb +4 -0
  29. data/lib/graphql/tracing/appsignal_trace.rb +4 -0
  30. data/lib/graphql/tracing/data_dog_trace.rb +4 -0
  31. data/lib/graphql/tracing/new_relic_trace.rb +38 -23
  32. data/lib/graphql/tracing/notifications_trace.rb +4 -0
  33. data/lib/graphql/tracing/platform_trace.rb +5 -0
  34. data/lib/graphql/tracing/prometheus_trace.rb +4 -0
  35. data/lib/graphql/tracing/scout_trace.rb +3 -0
  36. data/lib/graphql/tracing/sentry_trace.rb +4 -0
  37. data/lib/graphql/tracing/statsd_trace.rb +4 -0
  38. data/lib/graphql/types/relay/connection_behaviors.rb +1 -1
  39. data/lib/graphql/types/relay/edge_behaviors.rb +1 -1
  40. data/lib/graphql/version.rb +1 -1
  41. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ba5296cae016dea52b006bebe1515d6eef54876ec9f6437f5414bfccd7d8cf40
4
- data.tar.gz: 75bf06fbb277d4e9c400f57998e0de5d2a6e4a41d562c71b0969fc30fe2bbb64
3
+ metadata.gz: 35661aff446f955116332f131cd6323839e0b5f0ea104512a8257a28d3232e43
4
+ data.tar.gz: 023eb7cff94490520342572da1b7dc2b017dfa99daa5c9d18503b51de7dccc66
5
5
  SHA512:
6
- metadata.gz: e05120bb3377cbda64651ef2128b97daedf1b58e5e4a36b90d01d8ba4435a26e0dce6e270dfbc22ae75d29abcbe4d6f50c853ad6e13225a71eafeadbaad05519
7
- data.tar.gz: 2caa24da0288b0efab47b94a441351f8bbd0d1d3cb8bac1c834102fedb2ea350787fb74e2fe1d502971b181308f2b17b1e5b5273a2bf2a868d4917b29fc27f9b
6
+ metadata.gz: 29645fc0f923d6e268888c43fbd703189160ac8be5ea8d0fc9a17683d7336c43b20cfcdfd1cafd90291840c59e2d06d3eb8b27b66bb41e26beea0f4388e7a839
7
+ data.tar.gz: 63f1a5c91cbd08948ffc96bcb0d581a3959c2d0f13be19402200d1e0209187ab70d6f8c27e2b6a62ac93a27a945792d93ecf03bb6f53b37778eb88c3570e2553
@@ -42,6 +42,7 @@ 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
45
46
  class << self
46
47
  private
47
48
 
@@ -72,7 +73,7 @@ module GraphQL
72
73
  build_visitor_hooks :variable_definition
73
74
  build_visitor_hooks :variable_identifier
74
75
  build_visitor_hooks :abstract_node
75
-
76
+ # rubocop:enable Development/NoEvalCop
76
77
  protected
77
78
 
78
79
  # @return [GraphQL::Query, GraphQL::Execution::Multiplex] Whatever this analyzer is analyzing
@@ -10,7 +10,7 @@ module GraphQL
10
10
  #
11
11
  # @see {GraphQL::Analysis::Analyzer} AST Analyzers for queries
12
12
  class Visitor < GraphQL::Language::StaticVisitor
13
- def initialize(query:, analyzers:)
13
+ def initialize(query:, analyzers:, timeout:)
14
14
  @analyzers = analyzers
15
15
  @path = []
16
16
  @object_types = []
@@ -24,6 +24,11 @@ module GraphQL
24
24
  @types = query.types
25
25
  @response_path = []
26
26
  @skip_stack = [false]
27
+ @timeout_time = if timeout
28
+ Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) + timeout
29
+ else
30
+ Float::INFINITY
31
+ end
27
32
  super(query.selected_operation)
28
33
  end
29
34
 
@@ -64,6 +69,7 @@ module GraphQL
64
69
  @response_path.dup
65
70
  end
66
71
 
72
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
67
73
  # Visitor Hooks
68
74
  [
69
75
  :operation_definition, :fragment_definition,
@@ -72,28 +78,26 @@ module GraphQL
72
78
  module_eval <<-RUBY, __FILE__, __LINE__
73
79
  def call_on_enter_#{node_type}(node, parent)
74
80
  @analyzers.each do |a|
75
- begin
76
- a.on_enter_#{node_type}(node, parent, self)
77
- rescue AnalysisError => err
78
- @rescued_errors << err
79
- end
81
+ a.on_enter_#{node_type}(node, parent, self)
82
+ rescue AnalysisError => err
83
+ @rescued_errors << err
80
84
  end
81
85
  end
82
86
 
83
87
  def call_on_leave_#{node_type}(node, parent)
84
88
  @analyzers.each do |a|
85
- begin
86
- a.on_leave_#{node_type}(node, parent, self)
87
- rescue AnalysisError => err
88
- @rescued_errors << err
89
- end
89
+ a.on_leave_#{node_type}(node, parent, self)
90
+ rescue AnalysisError => err
91
+ @rescued_errors << err
90
92
  end
91
93
  end
92
94
 
93
95
  RUBY
94
96
  end
97
+ # rubocop:enable Development/NoEvalCop
95
98
 
96
99
  def on_operation_definition(node, parent)
100
+ check_timeout
97
101
  object_type = @schema.root_type_for_operation(node.operation_type)
98
102
  @object_types.push(object_type)
99
103
  @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
@@ -104,31 +108,27 @@ module GraphQL
104
108
  @path.pop
105
109
  end
106
110
 
107
- def on_fragment_definition(node, parent)
108
- on_fragment_with_type(node) do
109
- @path.push("fragment #{node.name}")
110
- @in_fragment_def = false
111
- call_on_enter_fragment_definition(node, parent)
112
- super
113
- @in_fragment_def = false
114
- call_on_leave_fragment_definition(node, parent)
115
- end
116
- end
117
-
118
111
  def on_inline_fragment(node, parent)
119
- on_fragment_with_type(node) do
120
- @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
121
- @skipping = @skip_stack.last || skip?(node)
122
- @skip_stack << @skipping
123
-
124
- call_on_enter_inline_fragment(node, parent)
125
- super
126
- @skipping = @skip_stack.pop
127
- call_on_leave_inline_fragment(node, parent)
112
+ check_timeout
113
+ object_type = if node.type
114
+ @types.type(node.type.name)
115
+ else
116
+ @object_types.last
128
117
  end
118
+ @object_types.push(object_type)
119
+ @path.push("...#{node.type ? " on #{node.type.name}" : ""}")
120
+ @skipping = @skip_stack.last || skip?(node)
121
+ @skip_stack << @skipping
122
+ call_on_enter_inline_fragment(node, parent)
123
+ super
124
+ @skipping = @skip_stack.pop
125
+ call_on_leave_inline_fragment(node, parent)
126
+ @object_types.pop
127
+ @path.pop
129
128
  end
130
129
 
131
130
  def on_field(node, parent)
131
+ check_timeout
132
132
  @response_path.push(node.alias || node.name)
133
133
  parent_type = @object_types.last
134
134
  # This could be nil if the previous field wasn't found:
@@ -156,6 +156,7 @@ module GraphQL
156
156
  end
157
157
 
158
158
  def on_directive(node, parent)
159
+ check_timeout
159
160
  directive_defn = @schema.directives[node.name]
160
161
  @directive_definitions.push(directive_defn)
161
162
  call_on_enter_directive(node, parent)
@@ -165,6 +166,7 @@ module GraphQL
165
166
  end
166
167
 
167
168
  def on_argument(node, parent)
169
+ check_timeout
168
170
  argument_defn = if (arg = @argument_definitions.last)
169
171
  arg_type = arg.type.unwrap
170
172
  if arg_type.kind.input_object?
@@ -190,6 +192,7 @@ module GraphQL
190
192
  end
191
193
 
192
194
  def on_fragment_spread(node, parent)
195
+ check_timeout
193
196
  @path.push("... #{node.name}")
194
197
  @skipping = @skip_stack.last || skip?(node)
195
198
  @skip_stack << @skipping
@@ -267,16 +270,10 @@ module GraphQL
267
270
  !dir.empty? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
268
271
  end
269
272
 
270
- def on_fragment_with_type(node)
271
- object_type = if node.type
272
- @types.type(node.type.name)
273
- else
274
- @object_types.last
273
+ def check_timeout
274
+ if Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) > @timeout_time
275
+ raise GraphQL::Analysis::TimeoutError
275
276
  end
276
- @object_types.push(object_type)
277
- yield(node)
278
- @object_types.pop
279
- @path.pop
280
277
  end
281
278
  end
282
279
  end
@@ -6,11 +6,16 @@ require "graphql/analysis/query_complexity"
6
6
  require "graphql/analysis/max_query_complexity"
7
7
  require "graphql/analysis/query_depth"
8
8
  require "graphql/analysis/max_query_depth"
9
- require "timeout"
10
-
11
9
  module GraphQL
12
10
  module Analysis
13
11
  AST = self
12
+
13
+ class TimeoutError < AnalysisError
14
+ def initialize(...)
15
+ super("Timeout on validation of query")
16
+ end
17
+ end
18
+
14
19
  module_function
15
20
  # Analyze a multiplex, and all queries within.
16
21
  # Multiplex analyzers are ran for all queries, keeping state.
@@ -61,13 +66,11 @@ module GraphQL
61
66
  if !analyzers_to_run.empty?
62
67
  visitor = GraphQL::Analysis::Visitor.new(
63
68
  query: query,
64
- analyzers: analyzers_to_run
69
+ analyzers: analyzers_to_run,
70
+ timeout: query.validate_timeout_remaining,
65
71
  )
66
72
 
67
- # `nil` or `0` causes no timeout
68
- Timeout::timeout(query.validate_timeout_remaining) do
69
- visitor.visit
70
- end
73
+ visitor.visit
71
74
 
72
75
  if !visitor.rescued_errors.empty?
73
76
  return visitor.rescued_errors
@@ -79,8 +82,8 @@ module GraphQL
79
82
  []
80
83
  end
81
84
  end
82
- rescue Timeout::Error
83
- [GraphQL::AnalysisError.new("Timeout on validation of query")]
85
+ rescue TimeoutError => err
86
+ [err]
84
87
  rescue GraphQL::UnauthorizedError, GraphQL::ExecutionError
85
88
  # This error was raised during analysis and will be returned the client before execution
86
89
  []
@@ -473,7 +473,7 @@ module GraphQL
473
473
  # When this comes from a list item, use the parent object:
474
474
  parent_type = selection_result.is_a?(GraphQLResultArray) ? selection_result.graphql_parent.graphql_result_type : selection_result.graphql_result_type
475
475
  # This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
476
- err = parent_type::InvalidNullError.new(parent_type, field, value, ast_node)
476
+ err = parent_type::InvalidNullError.new(parent_type, field, ast_node)
477
477
  schema.type_error(err, context)
478
478
  end
479
479
  else
@@ -9,16 +9,12 @@ module GraphQL
9
9
  # @return [GraphQL::Field] The field which failed to return a value
10
10
  attr_reader :field
11
11
 
12
- # @return [nil, GraphQL::ExecutionError] The invalid value for this field
13
- attr_reader :value
14
-
15
12
  # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
16
13
  attr_reader :ast_node
17
14
 
18
- def initialize(parent_type, field, value, ast_node)
15
+ def initialize(parent_type, field, ast_node)
19
16
  @parent_type = parent_type
20
17
  @field = field
21
- @value = value
22
18
  @ast_node = ast_node
23
19
  super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
24
20
  end
@@ -13,17 +13,21 @@ module GraphQL
13
13
  @pos = nil
14
14
  @max_tokens = max_tokens || Float::INFINITY
15
15
  @tokens_count = 0
16
+ @finished = false
16
17
  end
17
18
 
18
- def eos?
19
- @scanner.eos?
19
+ def finished?
20
+ @finished
20
21
  end
21
22
 
22
23
  attr_reader :pos, :tokens_count
23
24
 
24
25
  def advance
25
26
  @scanner.skip(IGNORE_REGEXP)
26
- return false if @scanner.eos?
27
+ if @scanner.eos?
28
+ @finished = true
29
+ return false
30
+ end
27
31
  @tokens_count += 1
28
32
  if @tokens_count > @max_tokens
29
33
  raise_parse_error("This query is too large to execute.")
@@ -141,6 +141,8 @@ 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
+
144
146
  # Add a default `#visit_method` and `#children_method_name` using the class name
145
147
  def inherited(child_class)
146
148
  super
@@ -343,6 +345,7 @@ module GraphQL
343
345
  RUBY
344
346
  end
345
347
  end
348
+ # rubocop:enable Development/NoEvalCop
346
349
  end
347
350
  end
348
351
 
@@ -110,7 +110,7 @@ module GraphQL
110
110
  # Only ignored characters is not a valid document
111
111
  raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @graphql_str)
112
112
  end
113
- while !@lexer.eos?
113
+ while !@lexer.finished?
114
114
  defns << definition
115
115
  end
116
116
  Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
@@ -22,39 +22,6 @@ 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
-
58
25
  def on_document_children(document_node)
59
26
  document_node.children.each do |child_node|
60
27
  visit_method = child_node.visit_method
@@ -123,6 +90,41 @@ module GraphQL
123
90
  end
124
91
  end
125
92
 
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
+
126
128
  [
127
129
  Language::Nodes::Argument,
128
130
  Language::Nodes::Directive,
@@ -162,6 +164,8 @@ module GraphQL
162
164
  ].each do |ast_node_class|
163
165
  make_visit_methods(ast_node_class)
164
166
  end
167
+
168
+ # rubocop:disable Development/NoEvalCop
165
169
  end
166
170
  end
167
171
  end
@@ -61,61 +61,6 @@ 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
-
119
64
  def on_document_children(document_node)
120
65
  new_node = document_node
121
66
  document_node.children.each do |child_node|
@@ -216,6 +161,63 @@ module GraphQL
216
161
  new_node
217
162
  end
218
163
 
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
+
219
221
  [
220
222
  Language::Nodes::Argument,
221
223
  Language::Nodes::Directive,
@@ -256,6 +258,8 @@ module GraphQL
256
258
  make_visit_methods(ast_node_class)
257
259
  end
258
260
 
261
+ # rubocop:enable Development/NoEvalCop
262
+
259
263
  private
260
264
 
261
265
  def apply_modifications(node, parent, new_node_and_new_parent)
@@ -53,6 +53,7 @@ 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)
56
57
  @type_expr = type_expr || type
57
58
  @description = desc || description
58
59
  @comment = comment
@@ -89,11 +90,8 @@ module GraphQL
89
90
  end
90
91
 
91
92
  if definition_block
92
- if definition_block.arity == 1
93
- instance_exec(self, &definition_block)
94
- else
95
- instance_eval(&definition_block)
96
- end
93
+ # `self` will still be self, it will also be the first argument to the block:
94
+ instance_exec(self, &definition_block)
97
95
  end
98
96
  end
99
97
 
@@ -467,17 +467,18 @@ module GraphQL
467
467
 
468
468
  # Don't do this for interfaces
469
469
  if default_resolve
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
470
+ define_field_resolve_method(owner, resolve_method_name, field_definition.name)
477
471
  end
478
472
  end
479
473
  end
480
474
 
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
+
481
482
  def build_resolve_type(lookup_hash, directives, missing_type_handler)
482
483
  resolve_type_proc = nil
483
484
  resolve_type_proc = ->(ast_node) {
@@ -99,7 +99,7 @@ module GraphQL
99
99
 
100
100
  def inherited(subclass)
101
101
  super
102
- subclass.class_eval do
102
+ subclass.class_exec do
103
103
  @default_graphql_name ||= nil
104
104
  end
105
105
  end
@@ -234,7 +234,7 @@ module GraphQL
234
234
  # because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
235
235
  child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
236
236
  end
237
- child_class.class_eval { @value_methods = nil }
237
+ child_class.class_exec { @value_methods = nil }
238
238
  super
239
239
  end
240
240
 
@@ -256,7 +256,7 @@ module GraphQL
256
256
  return
257
257
  end
258
258
 
259
- instance_eval("def #{value_method_name}; #{value.graphql_name.inspect}; end;", __FILE__, __LINE__)
259
+ define_singleton_method(value_method_name) { value.graphql_name }
260
260
  end
261
261
  end
262
262
 
@@ -48,7 +48,7 @@ module GraphQL
48
48
  end
49
49
 
50
50
  if block_given?
51
- instance_eval(&block)
51
+ instance_exec(self, &block)
52
52
  end
53
53
  end
54
54
 
@@ -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
-
258
+ NameValidator.validate!(@name)
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_eval(&@definition_block)
372
+ instance_exec(self, &@definition_block)
373
373
  end
374
374
  self.extensions.each(&:after_define_apply)
375
375
  @call_after_define = true
@@ -59,7 +59,7 @@ module GraphQL
59
59
  else
60
60
  new_h = {}
61
61
  keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
62
- new_h
62
+ new_h
63
63
  end
64
64
  end
65
65
 
@@ -150,14 +150,8 @@ module GraphQL
150
150
  end
151
151
  end
152
152
  # Add a method access
153
- method_name = argument_defn.keyword
154
153
  suppress_redefinition_warning do
155
- class_eval <<-RUBY, __FILE__, __LINE__
156
- def #{method_name}
157
- self[#{method_name.inspect}]
158
- end
159
- alias_method #{method_name.inspect}, #{method_name.inspect}
160
- RUBY
154
+ define_accessor_method(argument_defn.keyword)
161
155
  end
162
156
  argument_defn
163
157
  end
@@ -293,6 +287,11 @@ module GraphQL
293
287
  ensure
294
288
  $VERBOSE = verbose
295
289
  end
290
+
291
+ def define_accessor_method(method_name)
292
+ define_method(method_name) { self[method_name] }
293
+ alias_method(method_name, method_name)
294
+ end
296
295
  end
297
296
 
298
297
  private
@@ -30,7 +30,7 @@ module GraphQL
30
30
  const_set(:DefinitionMethods, defn_methods_module)
31
31
  extend(self::DefinitionMethods)
32
32
  end
33
- self::DefinitionMethods.module_eval(&block)
33
+ self::DefinitionMethods.module_exec(&block)
34
34
  end
35
35
 
36
36
  # @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_eval { self.own_directives = nil }
9
+ child_cls.module_exec { self.own_directives = nil }
10
10
  end
11
11
 
12
12
  def inherited(child_cls)
@@ -202,7 +202,7 @@ module GraphQL
202
202
 
203
203
  def inherited(subclass)
204
204
  super
205
- subclass.class_eval do
205
+ subclass.class_exec do
206
206
  @own_fields ||= nil
207
207
  @field_class ||= nil
208
208
  @has_no_fields ||= false
@@ -133,7 +133,7 @@ module GraphQL
133
133
 
134
134
  def inherited(subclass)
135
135
  super
136
- subclass.class_eval do
136
+ subclass.class_exec 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_eval do
33
+ subclass.class_exec 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_eval do
45
+ subclass.class_exec do
46
46
  @to_non_null_type ||= nil
47
47
  @to_list_type ||= nil
48
48
  end
@@ -22,7 +22,6 @@ module GraphQL
22
22
  include Schema::Member::GraphQLTypeNames
23
23
  # Really we only need description & comment from here, but:
24
24
  extend Schema::Member::BaseDSLMethods
25
- extend Member::BaseDSLMethods::ConfigurationExtension
26
25
  extend GraphQL::Schema::Member::HasArguments
27
26
  extend GraphQL::Schema::Member::HasValidators
28
27
  include Schema::Member::HasPath
@@ -404,6 +403,11 @@ module GraphQL
404
403
  end
405
404
  end
406
405
 
406
+ def inherited(child_class)
407
+ child_class.description(description)
408
+ super
409
+ end
410
+
407
411
  private
408
412
 
409
413
  attr_reader :own_extensions
@@ -821,13 +821,13 @@ module GraphQL
821
821
 
822
822
  attr_writer :validate_timeout
823
823
 
824
- def validate_timeout(new_validate_timeout = nil)
825
- if new_validate_timeout
824
+ def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
825
+ if !NOT_CONFIGURED.equal?(new_validate_timeout)
826
826
  @validate_timeout = new_validate_timeout
827
827
  elsif defined?(@validate_timeout)
828
828
  @validate_timeout
829
829
  else
830
- find_inherited_value(:validate_timeout)
830
+ find_inherited_value(:validate_timeout) || 3
831
831
  end
832
832
  end
833
833
 
@@ -345,7 +345,7 @@ module GraphQL
345
345
  fields << Field.new(node, definition, owner_type, parents)
346
346
  when GraphQL::Language::Nodes::InlineFragment
347
347
  fragment_type = node.type ? @types.type(node.type.name) : owner_type
348
- find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: owner_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
348
+ find_fields_and_fragments(node.selections, parents: [*parents, fragment_type], owner_type: fragment_type, fields: fields, fragment_spreads: fragment_spreads) if fragment_type
349
349
  when GraphQL::Language::Nodes::FragmentSpread
350
350
  fragment_spreads << FragmentSpread.new(node.name, parents)
351
351
  end
@@ -32,6 +32,8 @@ module GraphQL
32
32
  Gem::Version.new('1.0.0')
33
33
  end
34
34
 
35
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
36
+
35
37
  [
36
38
  'lex',
37
39
  'parse',
@@ -59,6 +61,8 @@ module GraphQL
59
61
  RUBY
60
62
  end
61
63
 
64
+ # rubocop:enable Development/NoEvalCop
65
+
62
66
  def execute_field(query:, field:, ast_node:, arguments:, object:)
63
67
  return_type = field.type.unwrap
64
68
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -21,6 +21,8 @@ module GraphQL
21
21
  super
22
22
  end
23
23
 
24
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
25
+
24
26
  {
25
27
  "lex" => "lex.graphql",
26
28
  "parse" => "parse.graphql",
@@ -51,6 +53,8 @@ module GraphQL
51
53
  RUBY
52
54
  end
53
55
 
56
+ # rubocop:enable Development/NoEvalCop
57
+
54
58
  def platform_execute_field(platform_key)
55
59
  Appsignal.instrument(platform_key) do
56
60
  yield
@@ -27,6 +27,8 @@ module GraphQL
27
27
  super
28
28
  end
29
29
 
30
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
31
+
30
32
  {
31
33
  'lex' => 'lex.graphql',
32
34
  'parse' => 'parse.graphql',
@@ -76,6 +78,8 @@ module GraphQL
76
78
  RUBY
77
79
  end
78
80
 
81
+ # rubocop:enable Development/NoEvalCop
82
+
79
83
  def execute_field_span(span_key, query, field, ast_node, arguments, object)
80
84
  return_type = field.type.unwrap
81
85
  trace_field = if return_type.kind.scalar? || return_type.kind.enum?
@@ -13,16 +13,21 @@ module GraphQL
13
13
  # # Optional, use the operation name to set the new relic transaction name:
14
14
  # # trace_with GraphQL::Tracing::NewRelicTrace, set_transaction_name: true
15
15
  # end
16
+ #
17
+ # @example Installing without trace events for `authorized?` or `resolve_type` calls
18
+ # trace_with GraphQL::Tracing::NewRelicTrace, trace_authorized: false, trace_resolve_type: false
16
19
  module NewRelicTrace
17
20
  # @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
18
21
  # This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
19
22
  # It can also be specified per-query with `context[:set_new_relic_transaction_name]`.
20
23
  # @param trace_authorized [Boolean] If `false`, skip tracing `authorized?` calls
21
24
  # @param trace_resolve_type [Boolean] If `false`, skip tracing `resolve_type?` calls
22
- def initialize(set_transaction_name: false, trace_authorized: true, trace_resolve_type: true, **_rest)
25
+ # @param trace_scalars [Boolean] If `true`, Enum and Scalar fields will be traced by default
26
+ def initialize(set_transaction_name: false, trace_authorized: true, trace_resolve_type: true, trace_scalars: false, **_rest)
23
27
  @set_transaction_name = set_transaction_name
24
28
  @trace_authorized = trace_authorized
25
29
  @trace_resolve_type = trace_resolve_type
30
+ @trace_scalars = trace_scalars
26
31
  @nr_field_names = Hash.new do |h, field|
27
32
  h[field] = "GraphQL/#{field.owner.graphql_name}/#{field.graphql_name}"
28
33
  end.compare_by_identity
@@ -89,78 +94,88 @@ module GraphQL
89
94
  end
90
95
 
91
96
  def begin_execute_field(field, object, arguments, query)
92
- nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_field_names[field], category: :web)
97
+ return_type = field.type.unwrap
98
+ trace_field = if return_type.kind.scalar? || return_type.kind.enum?
99
+ (field.trace.nil? && @trace_scalars) || field.trace
100
+ else
101
+ true
102
+ end
103
+ if trace_field
104
+ start_segment(partial_name: @nr_field_names[field], category: :web)
105
+ end
93
106
  super
94
107
  end
95
108
 
96
109
  def end_execute_field(field, objects, arguments, query, result)
97
- nr_segment_stack.pop.finish
110
+ finish_segment
98
111
  super
99
112
  end
100
113
 
101
114
  def begin_authorized(type, obj, ctx)
102
115
  if @trace_authorized
103
- nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_authorized_names[type], category: :web)
116
+ start_segment(partial_name: @nr_authorized_names[type], category: :web)
104
117
  end
105
118
  super
106
119
  end
107
120
 
108
121
  def end_authorized(type, obj, ctx, is_authed)
109
122
  if @trace_authorized
110
- nr_segment_stack.pop.finish
123
+ finish_segment
111
124
  end
112
125
  super
113
126
  end
114
127
 
115
128
  def begin_resolve_type(type, value, context)
116
129
  if @trace_resolve_type
117
- nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_resolve_type_names[type], category: :web)
130
+ start_segment(partial_name: @nr_resolve_type_names[type], category: :web)
118
131
  end
119
132
  super
120
133
  end
121
134
 
122
135
  def end_resolve_type(type, value, context, resolved_type)
123
136
  if @trace_resolve_type
124
- nr_segment_stack.pop.finish
137
+ finish_segment
125
138
  end
126
139
  super
127
140
  end
128
141
 
129
- def begin_dataloader(dl)
130
- super
131
- end
132
-
133
- def end_dataloader(dl)
134
- super
135
- end
136
-
137
142
  def begin_dataloader_source(source)
138
- nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: @nr_source_names[source], category: :web)
143
+ start_segment(partial_name: @nr_source_names[source], category: :web)
139
144
  super
140
145
  end
141
146
 
142
147
  def end_dataloader_source(source)
143
- nr_segment_stack.pop.finish
148
+ finish_segment
144
149
  super
145
150
  end
146
151
 
147
152
  def dataloader_fiber_yield(source)
148
- current_segment = nr_segment_stack.last
149
- current_segment.finish
153
+ prev_segment = finish_segment
154
+ Fiber[:graphql_nr_previous_segment] = prev_segment
150
155
  super
151
156
  end
152
157
 
153
158
  def dataloader_fiber_resume(source)
154
- prev_segment = nr_segment_stack.pop
159
+ prev_segment = Fiber[:graphql_nr_previous_segment]
160
+ Fiber[:graphql_nr_previous_segment] = nil
155
161
  seg_partial_name = prev_segment.name.sub(/^.*(GraphQL.*)$/, '\1')
156
- nr_segment_stack << NewRelic::Agent::Tracer.start_transaction_or_segment(partial_name: seg_partial_name, category: :web)
162
+ start_segment(partial_name: seg_partial_name, category: :web)
157
163
  super
158
164
  end
159
165
 
160
166
  private
161
167
 
162
- def nr_segment_stack
163
- Fiber[:graphql_nr_segment_stack] ||= []
168
+ def start_segment(...)
169
+ Fiber[:graphql_nr_segment] = NewRelic::Agent::Tracer.start_transaction_or_segment(...)
170
+ end
171
+
172
+ def finish_segment
173
+ segment = Fiber[:graphql_nr_segment]
174
+ if segment
175
+ segment.finish
176
+ Fiber[:graphql_nr_segment] = nil
177
+ segment
178
+ end
164
179
  end
165
180
 
166
181
  def transaction_name(query)
@@ -16,6 +16,8 @@ module GraphQL
16
16
  super
17
17
  end
18
18
 
19
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
20
+
19
21
  {
20
22
  "lex" => "lex.graphql",
21
23
  "parse" => "parse.graphql",
@@ -39,6 +41,8 @@ module GraphQL
39
41
  RUBY
40
42
  end
41
43
 
44
+ # rubocop:enable Development/NoEvalCop
45
+
42
46
  include PlatformTrace
43
47
  end
44
48
  end
@@ -39,6 +39,9 @@ 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
+
42
45
  [:execute_field, :execute_field_lazy].each do |field_trace_method|
43
46
  if !child_class.method_defined?(field_trace_method)
44
47
  child_class.module_eval <<-RUBY, __FILE__, __LINE__
@@ -91,6 +94,8 @@ module GraphQL
91
94
  end
92
95
  RUBY
93
96
  end
97
+
98
+ # rubocop:enable Development/NoEvalCop
94
99
  end
95
100
  end
96
101
 
@@ -40,6 +40,8 @@ module GraphQL
40
40
  super(**rest)
41
41
  end
42
42
 
43
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
44
+
43
45
  {
44
46
  'lex' => "graphql.lex",
45
47
  'parse' => "graphql.parse",
@@ -57,6 +59,8 @@ module GraphQL
57
59
  RUBY
58
60
  end
59
61
 
62
+ # rubocop:enable Development/NoEvalCop
63
+
60
64
  def platform_execute_field(platform_key, &block)
61
65
  instrument_prometheus_execution(platform_key, "execute_field", &block)
62
66
  end
@@ -24,6 +24,8 @@ module GraphQL
24
24
  super
25
25
  end
26
26
 
27
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
28
+
27
29
  {
28
30
  "lex" => "lex.graphql",
29
31
  "parse" => "parse.graphql",
@@ -53,6 +55,7 @@ module GraphQL
53
55
  end
54
56
  RUBY
55
57
  end
58
+ # rubocop:enable Development/NoEvalCop
56
59
 
57
60
  def platform_execute_field(platform_key, &block)
58
61
  self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS, &block)
@@ -30,6 +30,8 @@ module GraphQL
30
30
  instrument_sentry_execution("graphql.execute", "execute_query", data) { super }
31
31
  end
32
32
 
33
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
34
+
33
35
  {
34
36
  "lex" => "graphql.lex",
35
37
  "parse" => "graphql.parse",
@@ -46,6 +48,8 @@ module GraphQL
46
48
  RUBY
47
49
  end
48
50
 
51
+ # rubocop:enable Development/NoEvalCop
52
+
49
53
  def platform_execute_field(platform_key, &block)
50
54
  instrument_sentry_execution(platform_key, "execute_field", &block)
51
55
  end
@@ -22,6 +22,8 @@ module GraphQL
22
22
  super(**rest)
23
23
  end
24
24
 
25
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
26
+
25
27
  {
26
28
  'lex' => "graphql.lex",
27
29
  'parse' => "graphql.parse",
@@ -41,6 +43,8 @@ module GraphQL
41
43
  RUBY
42
44
  end
43
45
 
46
+ # rubocop:enable Development/NoEvalCop
47
+
44
48
  def platform_execute_field(platform_key, &block)
45
49
  @statsd.time(platform_key, &block)
46
50
  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_eval {
16
+ child_class.module_exec {
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_eval { self.node_type = nil }
11
+ child_class.class_exec { 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.4.11"
3
+ VERSION = "2.4.13"
4
4
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.11
4
+ version: 2.4.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-02-28 00:00:00.000000000 Z
10
+ date: 2025-03-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: base64