graphql 2.4.8 → 2.4.9

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: 386222d0dc13d35729460ee8f3cd9fa30f90db9cb7dc2b2392489c9e0bc8939b
4
- data.tar.gz: 5229e7ee84d4c4e0a0c8afd10ef2b0185b0eedfc611a8db38047171d9c04a6e2
3
+ metadata.gz: 687da1733554d6abed2daad506c7f9d55d9126e373274572af5b255c3d7b4918
4
+ data.tar.gz: c2d2f58175df62c91ab31c3216b970a0bc09d1752250fc8192d1faa958cf1fe9
5
5
  SHA512:
6
- metadata.gz: 4cb91383b995e8714915d2c99d8c2d4fa15eaad91f0069766d7e8d379cae15c2aae6eb7a3961e440d62a961837a044aed604802960f199e8670ecf973c991fb2
7
- data.tar.gz: 8cd21fd942892100a0647bf9897e6e7d8651d184aee76a2988633fa3e9dc505163f330bb1f0ca3f4819cbb3248cd88e9ac252f516449ae169c1a3f47a982e11c
6
+ metadata.gz: f4001700112f8ed43e78b4c739daac90973f08035bef426d5611d91248dd60d4d0070f8fff7042ac57b0549ddac62ac1111262fe0a47c10a83cd3c7a9a0612b8
7
+ data.tar.gz: a6a1aaacf19bba38d2e0ee55ac9cdfa0d46b915706557c2dbac533d8fc58319d7c4ff8509019789fb39c46d6c01767f6cd27a02917fc7a3a7bdc30fc81eca5e4
@@ -36,7 +36,70 @@ module GraphQL
36
36
  private
37
37
 
38
38
  def rows
39
- @rows ||= build_rows(@context, rows: [HEADERS], top: true)
39
+ @rows ||= begin
40
+ query = @context.query
41
+ query_ctx = @context
42
+ runtime_inst = query_ctx.namespace(:interpreter_runtime)[:runtime]
43
+ result = runtime_inst.instance_variable_get(:@response)
44
+ rows = []
45
+ result_path = []
46
+ last_part = nil
47
+ path = @context.current_path
48
+ path.each do |path_part|
49
+ value = value_at(runtime_inst, result_path)
50
+
51
+ if result_path.empty?
52
+ name = query.selected_operation.operation_type || "query"
53
+ if (n = query.selected_operation_name)
54
+ name += " #{n}"
55
+ end
56
+ args = query.variables
57
+ else
58
+ name = result.graphql_field.path
59
+ args = result.graphql_arguments
60
+ end
61
+
62
+ object = result.graphql_parent ? result.graphql_parent.graphql_application_value : result.graphql_application_value
63
+ object = object.object.inspect
64
+
65
+ rows << [
66
+ result.ast_node.position.join(":"),
67
+ name,
68
+ "#{object}",
69
+ args.to_h.inspect,
70
+ inspect_result(value),
71
+ ]
72
+
73
+ result_path << path_part
74
+ if path_part == path.last
75
+ last_part = path_part
76
+ else
77
+ result = result[path_part]
78
+ end
79
+ end
80
+
81
+
82
+ object = result.graphql_application_value.object.inspect
83
+ ast_node = result.graphql_selections.find { |s| s.alias == last_part || s.name == last_part }
84
+ field_defn = query.get_field(result.graphql_result_type, ast_node.name)
85
+ args = query.arguments_for(ast_node, field_defn).to_h
86
+ field_path = field_defn.path
87
+ if ast_node.alias
88
+ field_path += " as #{ast_node.alias}"
89
+ end
90
+
91
+ rows << [
92
+ ast_node.position.join(":"),
93
+ field_path,
94
+ "#{object}",
95
+ args.inspect,
96
+ inspect_result(@override_value)
97
+ ]
98
+
99
+ rows << HEADERS
100
+ rows.reverse!
101
+ rows
102
+ end
40
103
  end
41
104
 
42
105
  # @return [String]
@@ -75,67 +138,44 @@ module GraphQL
75
138
  table
76
139
  end
77
140
 
78
- # @return [Array] 5 items for a backtrace table (not `key`)
79
- def build_rows(context_entry, rows:, top: false)
80
- case context_entry
81
- when Backtrace::Frame
82
- field_alias = context_entry.ast_node.respond_to?(:alias) && context_entry.ast_node.alias
83
- value = if top && @override_value
84
- @override_value
85
- else
86
- value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
87
- end
88
- rows << [
89
- "#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
90
- "#{context_entry.field.path}#{field_alias ? " as #{field_alias}" : ""}",
91
- "#{context_entry.object.object.inspect}",
92
- context_entry.arguments.to_h.inspect, # rubocop:disable Development/ContextIsPassedCop -- unrelated method
93
- Backtrace::InspectResult.inspect_result(value),
94
- ]
95
- if (parent = context_entry.parent_frame)
96
- build_rows(parent, rows: rows)
97
- else
98
- rows
99
- end
100
- when GraphQL::Query::Context
101
- query = context_entry.query
102
- op = query.selected_operation
103
- if op
104
- op_type = op.operation_type
105
- position = "#{op.line}:#{op.col}"
106
- else
107
- op_type = "query"
108
- position = "?:?"
109
- end
110
- op_name = query.selected_operation_name
111
- object = query.root_value
112
- if object.is_a?(GraphQL::Schema::Object)
113
- object = object.object
114
- end
115
- value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
116
- rows << [
117
- "#{position}",
118
- "#{op_type}#{op_name ? " #{op_name}" : ""}",
119
- "#{object.inspect}",
120
- query.variables.to_h.inspect,
121
- Backtrace::InspectResult.inspect_result(value),
122
- ]
123
- else
124
- raise "Unexpected get_rows subject #{context_entry.class} (#{context_entry.inspect})"
125
- end
126
- end
127
141
 
128
142
  def value_at(runtime, path)
129
143
  response = runtime.final_result
130
144
  path.each do |key|
131
- if response && (response = response[key])
132
- next
133
- else
134
- break
135
- end
145
+ response && (response = response[key])
136
146
  end
137
147
  response
138
148
  end
149
+
150
+ def inspect_result(obj)
151
+ case obj
152
+ when Hash
153
+ "{" +
154
+ obj.map do |key, val|
155
+ "#{key}: #{inspect_truncated(val)}"
156
+ end.join(", ") +
157
+ "}"
158
+ when Array
159
+ "[" +
160
+ obj.map { |v| inspect_truncated(v) }.join(", ") +
161
+ "]"
162
+ else
163
+ inspect_truncated(obj)
164
+ end
165
+ end
166
+
167
+ def inspect_truncated(obj)
168
+ case obj
169
+ when Hash
170
+ "{...}"
171
+ when Array
172
+ "[...]"
173
+ when GraphQL::Execution::Lazy
174
+ "(unresolved)"
175
+ else
176
+ "#{obj.inspect}"
177
+ end
178
+ end
139
179
  end
140
180
  end
141
181
  end
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/backtrace/inspect_result"
3
2
  require "graphql/backtrace/table"
4
3
  require "graphql/backtrace/traced_error"
5
- require "graphql/backtrace/tracer"
6
- require "graphql/backtrace/trace"
7
4
  module GraphQL
8
5
  # Wrap unhandled errors with {TracedError}.
9
6
  #
@@ -24,7 +21,7 @@ module GraphQL
24
21
  def_delegators :to_a, :each, :[]
25
22
 
26
23
  def self.use(schema_defn)
27
- schema_defn.trace_with(self::Trace)
24
+ schema_defn.using_backtrace = true
28
25
  end
29
26
 
30
27
  def initialize(context, value: nil)
@@ -40,20 +37,5 @@ module GraphQL
40
37
  def to_a
41
38
  @table.to_backtrace
42
39
  end
43
-
44
- # Used for internal bookkeeping
45
- # @api private
46
- class Frame
47
- attr_reader :path, :query, :ast_node, :object, :field, :arguments, :parent_frame
48
- def initialize(path:, query:, ast_node:, object:, field:, arguments:, parent_frame:)
49
- @path = path
50
- @query = query
51
- @ast_node = ast_node
52
- @field = field
53
- @object = object
54
- @arguments = arguments
55
- @parent_frame = parent_frame
56
- end
57
- end
58
40
  end
59
41
  end
@@ -5,7 +5,10 @@ module GraphQL
5
5
  class Interpreter
6
6
  class Runtime
7
7
  module GraphQLResult
8
- def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager)
8
+ def initialize(result_name, result_type, application_value, parent_result, is_non_null_in_parent, selections, is_eager, ast_node, graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
9
+ @ast_node = ast_node
10
+ @graphql_arguments = graphql_arguments
11
+ @graphql_field = graphql_field
9
12
  @graphql_parent = parent_result
10
13
  @graphql_application_value = application_value
11
14
  @graphql_result_type = result_type
@@ -31,14 +34,14 @@ module GraphQL
31
34
 
32
35
  attr_accessor :graphql_dead
33
36
  attr_reader :graphql_parent, :graphql_result_name, :graphql_is_non_null_in_parent,
34
- :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager
37
+ :graphql_application_value, :graphql_result_type, :graphql_selections, :graphql_is_eager, :ast_node, :graphql_arguments, :graphql_field
35
38
 
36
39
  # @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
37
40
  attr_accessor :graphql_result_data
38
41
  end
39
42
 
40
43
  class GraphQLResultHash
41
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
44
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
42
45
  super
43
46
  @graphql_result_data = {}
44
47
  end
@@ -126,7 +129,7 @@ module GraphQL
126
129
  class GraphQLResultArray
127
130
  include GraphQLResult
128
131
 
129
- def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager)
132
+ def initialize(_result_name, _result_type, _application_value, _parent_result, _is_non_null_in_parent, _selections, _is_eager, _ast_node, _graphql_arguments, graphql_field) # rubocop:disable Metrics/ParameterLists
130
133
  super
131
134
  @graphql_result_data = []
132
135
  end
@@ -168,6 +171,10 @@ module GraphQL
168
171
  def values
169
172
  (@graphql_metadata || @graphql_result_data)
170
173
  end
174
+
175
+ def [](idx)
176
+ (@graphql_metadata || @graphql_result_data)[idx]
177
+ end
171
178
  end
172
179
  end
173
180
  end
@@ -74,7 +74,7 @@ module GraphQL
74
74
  runtime_object = root_type.wrap(query.root_value, context)
75
75
  runtime_object = schema.sync_lazy(runtime_object)
76
76
  is_eager = root_op_type == "mutation"
77
- @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
77
+ @response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
78
78
  st = get_current_runtime_state
79
79
  st.current_result = @response
80
80
 
@@ -85,7 +85,7 @@ module GraphQL
85
85
  call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
86
86
  each_gathered_selections(@response) do |selections, is_selection_array|
87
87
  if is_selection_array
88
- selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
88
+ selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
89
89
  final_response = @response
90
90
  else
91
91
  selection_response = @response
@@ -574,7 +574,7 @@ module GraphQL
574
574
  r = begin
575
575
  current_type.coerce_result(value, context)
576
576
  rescue StandardError => err
577
- schema.handle_or_reraise(context, err)
577
+ query.handle_or_reraise(err)
578
578
  end
579
579
  set_result(selection_result, result_name, r, false, is_non_null)
580
580
  r
@@ -609,11 +609,11 @@ module GraphQL
609
609
  after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
610
610
  continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
611
611
  if HALT != continue_value
612
- response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
612
+ response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
613
613
  set_result(selection_result, result_name, response_hash, true, is_non_null)
614
614
  each_gathered_selections(response_hash) do |selections, is_selection_array|
615
615
  if is_selection_array
616
- this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
616
+ this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
617
617
  final_result = response_hash
618
618
  else
619
619
  this_result = response_hash
@@ -634,35 +634,43 @@ module GraphQL
634
634
  # This is true for objects, unions, and interfaces
635
635
  use_dataloader_job = !inner_type.unwrap.kind.input?
636
636
  inner_type_non_null = inner_type.non_null?
637
- response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
637
+ response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
638
638
  set_result(selection_result, result_name, response_list, true, is_non_null)
639
639
  idx = nil
640
640
  list_value = begin
641
- value.each do |inner_value|
642
- idx ||= 0
643
- this_idx = idx
644
- idx += 1
645
- if use_dataloader_job
646
- @dataloader.append_job do
641
+ begin
642
+ value.each do |inner_value|
643
+ idx ||= 0
644
+ this_idx = idx
645
+ idx += 1
646
+ if use_dataloader_job
647
+ @dataloader.append_job do
648
+ resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
649
+ end
650
+ else
647
651
  resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
648
652
  end
649
- else
650
- resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
651
653
  end
652
- end
653
654
 
654
- response_list
655
- rescue NoMethodError => err
656
- # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
657
- if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
658
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
659
- raise ListResultFailedError.new(value: value, field: field, path: current_path)
660
- else
661
- # This was some other NoMethodError -- let it bubble to reveal the real error.
662
- raise
655
+ response_list
656
+ rescue NoMethodError => err
657
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
658
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
659
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
660
+ raise ListResultFailedError.new(value: value, field: field, path: current_path)
661
+ else
662
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
663
+ raise
664
+ end
665
+ rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
666
+ ex_err
667
+ rescue StandardError => err
668
+ begin
669
+ query.handle_or_reraise(err)
670
+ rescue GraphQL::ExecutionError => ex_err
671
+ ex_err
672
+ end
663
673
  end
664
- rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
665
- ex_err
666
674
  rescue StandardError => err
667
675
  begin
668
676
  query.handle_or_reraise(err)
@@ -35,10 +35,6 @@ module GraphQL
35
35
  @current_trace = @context[:trace] || schema.new_trace(multiplex: self)
36
36
  @dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
37
37
  @tracers = schema.tracers + (context[:tracers] || [])
38
- # Support `context: {backtrace: true}`
39
- if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
40
- @tracers << GraphQL::Backtrace::Tracer
41
- end
42
38
  @max_complexity = max_complexity
43
39
  end
44
40
  end
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a __DirectiveLocation describes one such possible adjacencies."
8
8
 
9
9
  GraphQL::Schema::Directive::LOCATIONS.each do |location|
10
- value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
10
+ value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
11
11
  end
12
12
  introspection true
13
13
  end
data/lib/graphql/query.rb CHANGED
@@ -127,14 +127,6 @@ module GraphQL
127
127
  context_tracers = (context ? context.fetch(:tracers, []) : [])
128
128
  @tracers = schema.tracers + context_tracers
129
129
 
130
- # Support `ctx[:backtrace] = true` for wrapping backtraces
131
- if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
132
- if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
133
- context_tracers += [GraphQL::Backtrace::Tracer]
134
- @tracers << GraphQL::Backtrace::Tracer
135
- end
136
- end
137
-
138
130
  if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
139
131
  raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
140
132
  end
@@ -298,6 +298,7 @@ module GraphQL
298
298
  description: enum_value_definition.description,
299
299
  directives: builder.prepare_directives(enum_value_definition, type_resolver),
300
300
  ast_node: enum_value_definition,
301
+ value_method: GraphQL::Schema::Enum.respond_to?(enum_value_definition.name.downcase) ? false : nil,
301
302
  )
302
303
  end
303
304
  end
@@ -61,12 +61,17 @@ module GraphQL
61
61
  # @option kwargs [String] :description, the GraphQL description for this value, present in documentation
62
62
  # @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
63
63
  # @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
64
+ # @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
64
65
  # @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
66
+ # @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation
65
67
  # @return [void]
66
68
  # @see {Schema::EnumValue} which handles these inputs by default
67
- def value(*args, **kwargs, &block)
69
+ def value(*args, value_method: nil, **kwargs, &block)
68
70
  kwargs[:owner] = self
69
71
  value = enum_value_class.new(*args, **kwargs, &block)
72
+
73
+ generate_value_method(value, value_method)
74
+
70
75
  key = value.graphql_name
71
76
  prev_value = own_values[key]
72
77
  case prev_value
@@ -223,6 +228,21 @@ module GraphQL
223
228
  def own_values
224
229
  @own_values ||= {}
225
230
  end
231
+
232
+ def generate_value_method(value, configured_value_method)
233
+ return if configured_value_method == false
234
+
235
+ value_method_name = configured_value_method || value.graphql_name.downcase
236
+
237
+ if respond_to?(value_method_name.to_sym)
238
+ warn "Failed to define value method for :#{value_method_name}, because " \
239
+ "#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \
240
+ "or `value_method: false` to disable Enum value method generation."
241
+ return
242
+ end
243
+
244
+ instance_eval("def #{value_method_name}; #{value.graphql_name.inspect}; end;")
245
+ end
226
246
  end
227
247
 
228
248
  enum_value_class(GraphQL::Schema::EnumValue)
@@ -22,6 +22,7 @@ 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
25
26
  extend GraphQL::Schema::Member::HasArguments
26
27
  extend GraphQL::Schema::Member::HasValidators
27
28
  include Schema::Member::HasPath
@@ -51,19 +51,36 @@ module GraphQL
51
51
  end
52
52
 
53
53
  def validate(_object, context, value)
54
- matched_conditions = 0
54
+ fully_matched_conditions = 0
55
+ partially_matched_conditions = 0
55
56
 
56
57
  if !value.nil?
57
58
  @one_of.each do |one_of_condition|
58
59
  case one_of_condition
59
60
  when Symbol
60
61
  if value.key?(one_of_condition)
61
- matched_conditions += 1
62
+ fully_matched_conditions += 1
62
63
  end
63
64
  when Array
64
- if one_of_condition.all? { |k| value.key?(k) }
65
- matched_conditions += 1
66
- break
65
+ any_match = false
66
+ full_match = true
67
+
68
+ one_of_condition.each do |k|
69
+ if value.key?(k)
70
+ any_match = true
71
+ else
72
+ full_match = false
73
+ end
74
+ end
75
+
76
+ partial_match = !full_match && any_match
77
+
78
+ if full_match
79
+ fully_matched_conditions += 1
80
+ end
81
+
82
+ if partial_match
83
+ partially_matched_conditions += 1
67
84
  end
68
85
  else
69
86
  raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
@@ -71,7 +88,7 @@ module GraphQL
71
88
  end
72
89
  end
73
90
 
74
- if matched_conditions == 1
91
+ if fully_matched_conditions == 1 && partially_matched_conditions == 0
75
92
  nil # OK
76
93
  else
77
94
  @message || build_message(context)
@@ -159,7 +159,7 @@ module GraphQL
159
159
  end
160
160
  end
161
161
  end
162
- visible_f.ensure_loaded
162
+ visible_f&.ensure_loaded
163
163
  elsif f && @cached_visible_fields[owner][f.ensure_loaded]
164
164
  f
165
165
  else
@@ -167,9 +167,6 @@ module GraphQL
167
167
  mods.each { |mod| new_class.include(mod) }
168
168
  new_class.include(DefaultTraceClass)
169
169
  trace_mode(:default, new_class)
170
- backtrace_class = Class.new(new_class)
171
- backtrace_class.include(GraphQL::Backtrace::Trace)
172
- trace_mode(:default_backtrace, backtrace_class)
173
170
  end
174
171
  trace_class_for(:default, build: true)
175
172
  end
@@ -216,11 +213,6 @@ module GraphQL
216
213
  const_set(:DefaultTrace, Class.new(base_class) do
217
214
  include DefaultTraceClass
218
215
  end)
219
- when :default_backtrace
220
- schema_base_class = trace_class_for(:default, build: true)
221
- const_set(:DefaultTraceBacktrace, Class.new(schema_base_class) do
222
- include(GraphQL::Backtrace::Trace)
223
- end)
224
216
  else
225
217
  # First, see if the superclass has a custom-defined class for this.
226
218
  # Then, if it doesn't, use this class's default trace
@@ -1118,8 +1110,14 @@ module GraphQL
1118
1110
  }
1119
1111
  end
1120
1112
 
1113
+ # @api private
1114
+ attr_accessor :using_backtrace
1115
+
1121
1116
  # @api private
1122
1117
  def handle_or_reraise(context, err)
1118
+ if context[:backtrace] || using_backtrace
1119
+ err = GraphQL::Backtrace::TracedError.new(err, context)
1120
+ end
1123
1121
  handler = Execution::Errors.find_handler_for(self, err.class)
1124
1122
  if handler
1125
1123
  obj = context[:current_object]
@@ -1449,22 +1447,8 @@ module GraphQL
1449
1447
  target = options[:query] || options[:multiplex]
1450
1448
  mode ||= target && target.context[:trace_mode]
1451
1449
 
1452
- trace_mode = if mode
1453
- mode
1454
- elsif target && target.context[:backtrace]
1455
- if default_trace_mode != :default
1456
- raise ArgumentError, "Can't use `context[:backtrace]` with a custom default trace mode (`#{dm.inspect}`)"
1457
- else
1458
- own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
1459
- options_trace_mode = :default
1460
- :default_backtrace
1461
- end
1462
- else
1463
- default_trace_mode
1464
- end
1465
-
1466
- options_trace_mode ||= trace_mode
1467
- base_trace_options = trace_options_for(options_trace_mode)
1450
+ trace_mode = mode || default_trace_mode
1451
+ base_trace_options = trace_options_for(trace_mode)
1468
1452
  trace_options = base_trace_options.merge(options)
1469
1453
  trace_class_for_mode = trace_class_for(trace_mode, build: true)
1470
1454
  trace_class_for_mode.new(**trace_options)
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  require "set"
3
- require "ostruct"
4
-
5
3
  module GraphQL
6
4
  class Subscriptions
7
5
  # Serialization helpers for passing subscription data around.
@@ -148,7 +146,7 @@ module GraphQL
148
146
  elsif obj.is_a?(Date) || obj.is_a?(Time)
149
147
  # DateTime extends Date; for TimeWithZone, call `.utc` first.
150
148
  { TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
151
- elsif obj.is_a?(OpenStruct)
149
+ elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
152
150
  { OPEN_STRUCT_KEY => dump_value(obj.to_h) }
153
151
  elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
154
152
  dump_value(obj.to_a)
@@ -8,7 +8,7 @@ module GraphQL
8
8
  # "Trace modes" are subclasses of this with custom tracing modules mixed in.
9
9
  #
10
10
  # A trace module may implement any of the methods on `Trace`, being sure to call `super`
11
- # to continue any tracing hooks and call the actual runtime behavior. See {GraphQL::Backtrace::Trace} for example.
11
+ # to continue any tracing hooks and call the actual runtime behavior.
12
12
  #
13
13
  class Trace
14
14
  # @param multiplex [GraphQL::Execution::Multiplex, nil]
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.4.8"
3
+ VERSION = "2.4.9"
4
4
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.8
4
+ version: 2.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-10 00:00:00.000000000 Z
10
+ date: 2025-01-29 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: base64
@@ -38,6 +37,20 @@ dependencies:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
39
  version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: logger
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
41
54
  - !ruby/object:Gem::Dependency
42
55
  name: benchmark-ips
43
56
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +191,76 @@ dependencies:
178
191
  - - ">="
179
192
  - !ruby/object:Gem::Version
180
193
  version: '0'
194
+ - !ruby/object:Gem::Dependency
195
+ name: simplecov
196
+ requirement: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - ">="
199
+ - !ruby/object:Gem::Version
200
+ version: '0'
201
+ type: :development
202
+ prerelease: false
203
+ version_requirements: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ - !ruby/object:Gem::Dependency
209
+ name: simplecov-lcov
210
+ requirement: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: '0'
215
+ type: :development
216
+ prerelease: false
217
+ version_requirements: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ - !ruby/object:Gem::Dependency
223
+ name: undercover
224
+ requirement: !ruby/object:Gem::Requirement
225
+ requirements:
226
+ - - ">="
227
+ - !ruby/object:Gem::Version
228
+ version: '0'
229
+ type: :development
230
+ prerelease: false
231
+ version_requirements: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - ">="
234
+ - !ruby/object:Gem::Version
235
+ version: '0'
236
+ - !ruby/object:Gem::Dependency
237
+ name: pronto
238
+ requirement: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: '0'
243
+ type: :development
244
+ prerelease: false
245
+ version_requirements: !ruby/object:Gem::Requirement
246
+ requirements:
247
+ - - ">="
248
+ - !ruby/object:Gem::Version
249
+ version: '0'
250
+ - !ruby/object:Gem::Dependency
251
+ name: pronto-undercover
252
+ requirement: !ruby/object:Gem::Requirement
253
+ requirements:
254
+ - - ">="
255
+ - !ruby/object:Gem::Version
256
+ version: '0'
257
+ type: :development
258
+ prerelease: false
259
+ version_requirements: !ruby/object:Gem::Requirement
260
+ requirements:
261
+ - - ">="
262
+ - !ruby/object:Gem::Version
263
+ version: '0'
181
264
  - !ruby/object:Gem::Dependency
182
265
  name: jekyll
183
266
  requirement: !ruby/object:Gem::Requirement
@@ -248,6 +331,20 @@ dependencies:
248
331
  - - "~>"
249
332
  - !ruby/object:Gem::Version
250
333
  version: 1.5.0
334
+ - !ruby/object:Gem::Dependency
335
+ name: mutex_m
336
+ requirement: !ruby/object:Gem::Requirement
337
+ requirements:
338
+ - - ">="
339
+ - !ruby/object:Gem::Version
340
+ version: '0'
341
+ type: :development
342
+ prerelease: false
343
+ version_requirements: !ruby/object:Gem::Requirement
344
+ requirements:
345
+ - - ">="
346
+ - !ruby/object:Gem::Version
347
+ version: '0'
251
348
  - !ruby/object:Gem::Dependency
252
349
  name: webrick
253
350
  requirement: !ruby/object:Gem::Requirement
@@ -330,11 +427,8 @@ files:
330
427
  - lib/graphql/analysis_error.rb
331
428
  - lib/graphql/autoload.rb
332
429
  - lib/graphql/backtrace.rb
333
- - lib/graphql/backtrace/inspect_result.rb
334
430
  - lib/graphql/backtrace/table.rb
335
- - lib/graphql/backtrace/trace.rb
336
431
  - lib/graphql/backtrace/traced_error.rb
337
- - lib/graphql/backtrace/tracer.rb
338
432
  - lib/graphql/coercion_error.rb
339
433
  - lib/graphql/current.rb
340
434
  - lib/graphql/dataloader.rb
@@ -654,7 +748,6 @@ metadata:
654
748
  bug_tracker_uri: https://github.com/rmosolgo/graphql-ruby/issues
655
749
  mailing_list_uri: https://buttondown.email/graphql-ruby
656
750
  rubygems_mfa_required: 'true'
657
- post_install_message:
658
751
  rdoc_options: []
659
752
  require_paths:
660
753
  - lib
@@ -669,8 +762,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
669
762
  - !ruby/object:Gem::Version
670
763
  version: '0'
671
764
  requirements: []
672
- rubygems_version: 3.5.12
673
- signing_key:
765
+ rubygems_version: 3.6.3
674
766
  specification_version: 4
675
767
  summary: A GraphQL language and runtime for Ruby
676
768
  test_files: []
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- class Backtrace
4
- module InspectResult
5
- module_function
6
-
7
- def inspect_result(obj)
8
- case obj
9
- when Hash
10
- "{" +
11
- obj.map do |key, val|
12
- "#{key}: #{inspect_truncated(val)}"
13
- end.join(", ") +
14
- "}"
15
- when Array
16
- "[" +
17
- obj.map { |v| inspect_truncated(v) }.join(", ") +
18
- "]"
19
- else
20
- inspect_truncated(obj)
21
- end
22
- end
23
-
24
- def inspect_truncated(obj)
25
- case obj
26
- when Hash
27
- "{...}"
28
- when Array
29
- "[...]"
30
- when GraphQL::Execution::Lazy
31
- "(unresolved)"
32
- else
33
- "#{obj.inspect}"
34
- end
35
- end
36
- end
37
- end
38
- end
@@ -1,93 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- class Backtrace
4
- module Trace
5
- def initialize(*args, **kwargs, &block)
6
- @__backtrace_contexts = {}
7
- @__backtrace_last_context = nil
8
- super
9
- end
10
-
11
- def validate(query:, validate:)
12
- if query.multiplex
13
- push_query_backtrace_context(query)
14
- end
15
- super
16
- end
17
-
18
- def analyze_query(query:)
19
- if query.multiplex # missing for stand-alone static validation
20
- push_query_backtrace_context(query)
21
- end
22
- super
23
- end
24
-
25
- def execute_query(query:)
26
- push_query_backtrace_context(query)
27
- super
28
- end
29
-
30
- def execute_query_lazy(query:, multiplex:)
31
- query ||= multiplex.queries.first
32
- push_query_backtrace_context(query)
33
- super
34
- end
35
-
36
- def execute_field(field:, query:, ast_node:, arguments:, object:)
37
- push_field_backtrace_context(field, query, ast_node, arguments, object)
38
- super
39
- end
40
-
41
- def execute_field_lazy(field:, query:, ast_node:, arguments:, object:)
42
- push_field_backtrace_context(field, query, ast_node, arguments, object)
43
- super
44
- end
45
-
46
- def execute_multiplex(multiplex:)
47
- super
48
- rescue StandardError => err
49
- # This is an unhandled error from execution,
50
- # Re-raise it with a GraphQL trace.
51
- potential_context = @__backtrace_last_context
52
- if potential_context.is_a?(GraphQL::Query::Context) ||
53
- potential_context.is_a?(Backtrace::Frame)
54
- raise TracedError.new(err, potential_context)
55
- else
56
- raise
57
- end
58
- end
59
-
60
- private
61
-
62
- def push_query_backtrace_context(query)
63
- push_data = query
64
- push_key = []
65
- @__backtrace_contexts[push_key] = push_data
66
- @__backtrace_last_context = push_data
67
- end
68
-
69
- def push_field_backtrace_context(field, query, ast_node, arguments, object)
70
- push_key = query.context[:current_path]
71
- push_storage = @__backtrace_contexts
72
- parent_frame = push_storage[push_key[0..-2]]
73
-
74
- if parent_frame.is_a?(GraphQL::Query)
75
- parent_frame = parent_frame.context
76
- end
77
-
78
- push_data = Frame.new(
79
- query: query,
80
- path: push_key,
81
- ast_node: ast_node,
82
- field: field,
83
- object: object,
84
- arguments: arguments,
85
- parent_frame: parent_frame,
86
- )
87
- push_storage[push_key] = push_data
88
- @__backtrace_last_context = push_data
89
- end
90
-
91
- end
92
- end
93
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- class Backtrace
4
- # TODO this is not fiber-friendly
5
- module Tracer
6
- module_function
7
-
8
- # Implement the {GraphQL::Tracing} API.
9
- def trace(key, metadata)
10
- case key
11
- when "lex", "parse"
12
- # No context here, don't have a query yet
13
- nil
14
- when "execute_multiplex", "analyze_multiplex"
15
- # No query context yet
16
- nil
17
- when "validate", "analyze_query", "execute_query", "execute_query_lazy"
18
- push_key = []
19
- if (query = metadata[:query]) || ((queries = metadata[:queries]) && (query = queries.first))
20
- push_data = query
21
- multiplex = query.multiplex
22
- elsif (multiplex = metadata[:multiplex])
23
- push_data = multiplex.queries.first
24
- end
25
- when "execute_field", "execute_field_lazy"
26
- query = metadata[:query]
27
- multiplex = query.multiplex
28
- push_key = query.context[:current_path]
29
- parent_frame = multiplex.context[:graphql_backtrace_contexts][push_key[0..-2]]
30
-
31
- if parent_frame.is_a?(GraphQL::Query)
32
- parent_frame = parent_frame.context
33
- end
34
-
35
- push_data = Frame.new(
36
- query: query,
37
- path: push_key,
38
- ast_node: metadata[:ast_node],
39
- field: metadata[:field],
40
- object: metadata[:object],
41
- arguments: metadata[:arguments],
42
- parent_frame: parent_frame,
43
- )
44
- else
45
- # Custom key, no backtrace data for this
46
- nil
47
- end
48
-
49
- if push_data && multiplex
50
- push_storage = multiplex.context[:graphql_backtrace_contexts] ||= {}
51
- push_storage[push_key] = push_data
52
- multiplex.context[:last_graphql_backtrace_context] = push_data
53
- end
54
-
55
- if key == "execute_multiplex"
56
- multiplex_context = metadata[:multiplex].context
57
- begin
58
- yield
59
- rescue StandardError => err
60
- # This is an unhandled error from execution,
61
- # Re-raise it with a GraphQL trace.
62
- potential_context = multiplex_context[:last_graphql_backtrace_context]
63
-
64
- if potential_context.is_a?(GraphQL::Query::Context) ||
65
- potential_context.is_a?(Backtrace::Frame)
66
- raise TracedError.new(err, potential_context)
67
- else
68
- raise
69
- end
70
- ensure
71
- multiplex_context.delete(:graphql_backtrace_contexts)
72
- multiplex_context.delete(:last_graphql_backtrace_context)
73
- end
74
- else
75
- yield
76
- end
77
- end
78
- end
79
- end
80
- end