graphql 1.11.8 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install_generator.rb +5 -5
  3. data/lib/generators/graphql/relay_generator.rb +63 -0
  4. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  5. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  6. data/lib/generators/graphql/templates/node_type.erb +9 -0
  7. data/lib/generators/graphql/templates/object.erb +1 -1
  8. data/lib/generators/graphql/templates/query_type.erb +1 -3
  9. data/lib/generators/graphql/templates/schema.erb +8 -35
  10. data/lib/graphql.rb +38 -4
  11. data/lib/graphql/analysis/analyze_query.rb +7 -0
  12. data/lib/graphql/analysis/ast.rb +11 -2
  13. data/lib/graphql/analysis/ast/visitor.rb +9 -1
  14. data/lib/graphql/backtrace.rb +28 -19
  15. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  16. data/lib/graphql/backtrace/table.rb +22 -2
  17. data/lib/graphql/backtrace/tracer.rb +40 -9
  18. data/lib/graphql/backwards_compatibility.rb +1 -0
  19. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  20. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  21. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  22. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  23. data/lib/graphql/dataloader.rb +197 -0
  24. data/lib/graphql/dataloader/null_dataloader.rb +21 -0
  25. data/lib/graphql/dataloader/request.rb +24 -0
  26. data/lib/graphql/dataloader/request_all.rb +22 -0
  27. data/lib/graphql/dataloader/source.rb +93 -0
  28. data/lib/graphql/define/assign_global_id_field.rb +1 -1
  29. data/lib/graphql/define/instance_definable.rb +32 -2
  30. data/lib/graphql/define/type_definer.rb +5 -5
  31. data/lib/graphql/deprecated_dsl.rb +5 -0
  32. data/lib/graphql/enum_type.rb +2 -0
  33. data/lib/graphql/execution/errors.rb +4 -0
  34. data/lib/graphql/execution/execute.rb +7 -0
  35. data/lib/graphql/execution/interpreter.rb +10 -6
  36. data/lib/graphql/execution/interpreter/arguments.rb +51 -14
  37. data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
  38. data/lib/graphql/execution/interpreter/runtime.rb +210 -124
  39. data/lib/graphql/execution/multiplex.rb +20 -6
  40. data/lib/graphql/function.rb +4 -0
  41. data/lib/graphql/input_object_type.rb +2 -0
  42. data/lib/graphql/interface_type.rb +3 -1
  43. data/lib/graphql/language/document_from_schema_definition.rb +50 -23
  44. data/lib/graphql/object_type.rb +2 -0
  45. data/lib/graphql/pagination/connection.rb +5 -1
  46. data/lib/graphql/pagination/connections.rb +6 -16
  47. data/lib/graphql/query.rb +2 -0
  48. data/lib/graphql/query/context.rb +4 -0
  49. data/lib/graphql/query/serial_execution.rb +1 -0
  50. data/lib/graphql/relay/base_connection.rb +7 -0
  51. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  52. data/lib/graphql/relay/connection_type.rb +1 -1
  53. data/lib/graphql/relay/mutation.rb +1 -0
  54. data/lib/graphql/relay/node.rb +3 -0
  55. data/lib/graphql/relay/type_extensions.rb +2 -0
  56. data/lib/graphql/scalar_type.rb +2 -0
  57. data/lib/graphql/schema.rb +69 -32
  58. data/lib/graphql/schema/argument.rb +25 -7
  59. data/lib/graphql/schema/build_from_definition.rb +139 -51
  60. data/lib/graphql/schema/directive.rb +76 -0
  61. data/lib/graphql/schema/directive/flagged.rb +57 -0
  62. data/lib/graphql/schema/enum.rb +3 -0
  63. data/lib/graphql/schema/enum_value.rb +12 -6
  64. data/lib/graphql/schema/field.rb +28 -9
  65. data/lib/graphql/schema/field/connection_extension.rb +3 -2
  66. data/lib/graphql/schema/input_object.rb +33 -22
  67. data/lib/graphql/schema/interface.rb +1 -0
  68. data/lib/graphql/schema/member.rb +4 -0
  69. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -0
  70. data/lib/graphql/schema/member/build_type.rb +3 -3
  71. data/lib/graphql/schema/member/has_arguments.rb +24 -6
  72. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  73. data/lib/graphql/schema/member/has_directives.rb +98 -0
  74. data/lib/graphql/schema/member/has_validators.rb +31 -0
  75. data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
  76. data/lib/graphql/schema/object.rb +11 -0
  77. data/lib/graphql/schema/printer.rb +5 -4
  78. data/lib/graphql/schema/resolver.rb +7 -0
  79. data/lib/graphql/schema/resolver/has_payload_type.rb +2 -0
  80. data/lib/graphql/schema/subscription.rb +19 -1
  81. data/lib/graphql/schema/timeout_middleware.rb +2 -0
  82. data/lib/graphql/schema/validation.rb +2 -0
  83. data/lib/graphql/schema/validator.rb +163 -0
  84. data/lib/graphql/schema/validator/exclusion_validator.rb +31 -0
  85. data/lib/graphql/schema/validator/format_validator.rb +49 -0
  86. data/lib/graphql/schema/validator/inclusion_validator.rb +33 -0
  87. data/lib/graphql/schema/validator/length_validator.rb +57 -0
  88. data/lib/graphql/schema/validator/numericality_validator.rb +71 -0
  89. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  90. data/lib/graphql/static_validation/validator.rb +2 -0
  91. data/lib/graphql/subscriptions.rb +17 -20
  92. data/lib/graphql/tracing.rb +2 -2
  93. data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
  94. data/lib/graphql/tracing/platform_tracing.rb +3 -1
  95. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  96. data/lib/graphql/types/relay.rb +11 -3
  97. data/lib/graphql/types/relay/base_connection.rb +2 -92
  98. data/lib/graphql/types/relay/base_edge.rb +2 -35
  99. data/lib/graphql/types/relay/connection_behaviors.rb +123 -0
  100. data/lib/graphql/types/relay/default_relay.rb +27 -0
  101. data/lib/graphql/types/relay/edge_behaviors.rb +42 -0
  102. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  103. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  104. data/lib/graphql/types/relay/node.rb +2 -4
  105. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  106. data/lib/graphql/types/relay/node_field.rb +1 -19
  107. data/lib/graphql/types/relay/nodes_field.rb +1 -19
  108. data/lib/graphql/types/relay/page_info.rb +2 -14
  109. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  110. data/lib/graphql/union_type.rb +2 -0
  111. data/lib/graphql/upgrader/member.rb +1 -0
  112. data/lib/graphql/upgrader/schema.rb +1 -0
  113. data/lib/graphql/version.rb +1 -1
  114. metadata +34 -9
  115. data/lib/graphql/types/relay/base_field.rb +0 -22
  116. data/lib/graphql/types/relay/base_interface.rb +0 -29
  117. data/lib/graphql/types/relay/base_object.rb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b72615f5488c1aa58c921128dd067d9f098b4617c954ac267e37efa72a9107c
4
- data.tar.gz: 4690738202ecd5d048802c80f1fc87095e0c1de4a02adcba26e88415f401908c
3
+ metadata.gz: 433f1170eb17ab192761efcd8c27cf9efe5f952ab47b501baca270e33d6007cd
4
+ data.tar.gz: c56045f746385ae37ebd0f82116aad10782e9265c13e0e85e39488ef3db02c74
5
5
  SHA512:
6
- metadata.gz: 744ca52fc03919a071013a27139112c2c18e487f333ee335ea374feaef06bb904079d4c54e0ddf088d2b5dfa345172efb3063faefa792c1c8ade38616752fa1a
7
- data.tar.gz: 90fffd6928d128f2516e4f49c9d729a26a57101683e19d0a90818c45f431bae221e4b8b1bf8d7bc374e20c8e98e3f0a0963877e818ae946f999d40942182cddd
6
+ metadata.gz: 6d580c78b6b659602067fcfd3043eff7ef2b20da1d50c1ca2b234614d864e1a232cb65e7456cfdf85764523f30bd5232823ea19b4664208aef875cc68e2e5fc9
7
+ data.tar.gz: 172bc16fadfeee1f022646498dd2ce359bd885fb96ddbeeab50f7583d27811501e583194dd582a7cf47d63545c2fe00c4fa4625b6b247155f537ff4a6de60ac1
@@ -43,9 +43,6 @@ module Graphql
43
43
  # post "/graphql", to: "graphql#execute"
44
44
  # ```
45
45
  #
46
- # Accept a `--relay` option which adds
47
- # The root `node(id: ID!)` field.
48
- #
49
46
  # Accept a `--batch` option which adds `GraphQL::Batch` setup.
50
47
  #
51
48
  # Use `--no-graphiql` to skip `graphiql-rails` installation.
@@ -79,8 +76,8 @@ module Graphql
79
76
 
80
77
  class_option :relay,
81
78
  type: :boolean,
82
- default: false,
83
- desc: "Include GraphQL::Relay installation"
79
+ default: true,
80
+ desc: "Include installation of Relay conventions (nodes, connections, edges)"
84
81
 
85
82
  class_option :batch,
86
83
  type: :boolean,
@@ -164,7 +161,10 @@ if Rails.env.development?
164
161
  RUBY
165
162
  end
166
163
  end
164
+ end
167
165
 
166
+ if options[:relay]
167
+ generate("graphql:relay")
168
168
  end
169
169
 
170
170
  if gemfile_modified?
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/base'
3
+ require_relative 'core'
4
+
5
+ module Graphql
6
+ module Generators
7
+ class RelayGenerator < Rails::Generators::Base
8
+ include Core
9
+
10
+ desc "Add base types and fields for Relay-style nodes and connections"
11
+ source_root File.expand_path('../templates', __FILE__)
12
+
13
+ def install_relay
14
+ # Add Node, `node(id:)`, and `nodes(ids:)`
15
+ template("node_type.erb", "#{options[:directory]}/types/node_type.rb")
16
+ in_root do
17
+ fields = " # Add `node(id: ID!) and `nodes(ids: [ID!]!)`\n include GraphQL::Types::Relay::HasNodeField\n include GraphQL::Types::Relay::HasNodesField\n\n"
18
+ inject_into_file "#{options[:directory]}/types/query_type.rb", fields, after: /class .*QueryType\s*<\s*[^\s]+?\n/m, force: false
19
+ end
20
+
21
+ # Add connections and edges
22
+ template("base_connection.erb", "#{options[:directory]}/types/base_connection.rb")
23
+ template("base_edge.erb", "#{options[:directory]}/types/base_edge.rb")
24
+ connectionable_type_files = {
25
+ "#{options[:directory]}/types/base_object.rb" => /class .*BaseObject\s*<\s*[^\s]+?\n/m,
26
+ "#{options[:directory]}/types/base_union.rb" => /class .*BaseUnion\s*<\s*[^\s]+?\n/m,
27
+ "#{options[:directory]}/types/base_interface.rb" => /include GraphQL::Schema::Interface\n/m,
28
+ }
29
+ in_root do
30
+ connectionable_type_files.each do |type_class_file, sentinel|
31
+ inject_into_file type_class_file, " connection_type_class(Types::BaseConnection)\n", after: sentinel, force: false
32
+ inject_into_file type_class_file, " edge_type_class(Types::BaseEdge)\n", after: sentinel, force: false
33
+ end
34
+ end
35
+
36
+ # Add object ID hooks & connection plugin
37
+ schema_code = <<-RUBY
38
+
39
+ # Relay-style Object Identification:
40
+
41
+ # Return a string UUID for `object`
42
+ def self.id_from_object(object, type_definition, query_ctx)
43
+ # Here's a simple implementation which:
44
+ # - joins the type name & object.id
45
+ # - encodes it with base64:
46
+ # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
47
+ end
48
+
49
+ # Given a string UUID, find the object
50
+ def self.object_from_id(id, query_ctx)
51
+ # For example, to decode the UUIDs generated above:
52
+ # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
53
+ #
54
+ # Then, based on `type_name` and `id`
55
+ # find an object in your application
56
+ # ...
57
+ end
58
+ RUBY
59
+ inject_into_file schema_file_path, schema_code, before: /^end\n/m, force: false
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,8 @@
1
+ <% module_namespacing_when_supported do -%>
2
+ module Types
3
+ class BaseConnection < Types::BaseObject
4
+ # add `nodes` and `pageInfo` fields, as well as `edge_type(...)` and `node_nullable(...)` overrides
5
+ include GraphQL::Types::Relay::ConnectionBehaviors
6
+ end
7
+ end
8
+ <% end -%>
@@ -0,0 +1,8 @@
1
+ <% module_namespacing_when_supported do -%>
2
+ module Types
3
+ class BaseEdge < Types::BaseObject
4
+ # add `node` and `cursor` fields, as well as `node_type(...)` override
5
+ include GraphQL::Types::Relay::EdgeBehaviors
6
+ end
7
+ end
8
+ <% end -%>
@@ -0,0 +1,9 @@
1
+ <% module_namespacing_when_supported do -%>
2
+ module Types
3
+ module NodeType
4
+ include Types::BaseInterface
5
+ # Add the `id` field
6
+ include GraphQL::Types::Relay::NodeBehaviors
7
+ end
8
+ end
9
+ <% end -%>
@@ -1,7 +1,7 @@
1
1
  <% module_namespacing_when_supported do -%>
2
2
  module Types
3
3
  class <%= type_ruby_name.split('::')[-1] %> < Types::BaseObject
4
- <% if options.node %> implements GraphQL::Relay::Node.interface
4
+ <% if options.node %> implements GraphQL::Types::Relay::Node
5
5
  <% end %><% normalized_fields.each do |f| %> <%= f.to_ruby %>
6
6
  <% end %> end
7
7
  end
@@ -10,8 +10,6 @@ module Types
10
10
  def test_field
11
11
  "Hello World!"
12
12
  end
13
- <% if options[:relay] %>
14
- field :node, field: GraphQL::Relay::Node.field
15
- <% end %> end
13
+ end
16
14
  end
17
15
  <% end -%>
@@ -1,42 +1,15 @@
1
1
  <% module_namespacing_when_supported do -%>
2
2
  class <%= schema_name %> < GraphQL::Schema
3
3
  query(Types::QueryType)
4
-
5
- # Opt in to the new runtime (default in future graphql-ruby versions)
6
- use GraphQL::Execution::Interpreter
7
- use GraphQL::Analysis::AST
8
-
9
- # Add built-in connections for pagination
10
- use GraphQL::Pagination::Connections
11
- <% if options[:relay] %>
12
- # Relay Object Identification:
13
-
14
- # Return a string UUID for `object`
15
- def self.id_from_object(object, type_definition, query_ctx)
16
- # Here's a simple implementation which:
17
- # - joins the type name & object.id
18
- # - encodes it with base64:
19
- # GraphQL::Schema::UniqueWithinType.encode(type_definition.name, object.id)
20
- end
21
-
22
- # Given a string UUID, find the object
23
- def self.object_from_id(id, query_ctx)
24
- # For example, to decode the UUIDs generated above:
25
- # type_name, item_id = GraphQL::Schema::UniqueWithinType.decode(id)
26
- #
27
- # Then, based on `type_name` and `id`
28
- # find an object in your application
29
- # ...
30
- end
31
-
32
- # Object Resolution
33
- def self.resolve_type(type, obj, ctx)
4
+ <% if options[:batch] %>
5
+ # GraphQL::Batch setup:
6
+ use GraphQL::Batch
7
+ <% end %>
8
+ # Union and Interface Resolution
9
+ def self.resolve_type(abstract_type, obj, ctx)
34
10
  # TODO: Implement this function
35
- # to return the correct type for `obj`
11
+ # to return the correct object type for `obj`
36
12
  raise(GraphQL::RequiredImplementationMissingError)
37
13
  end
38
- <% end %><% if options[:batch] %>
39
- # GraphQL::Batch setup:
40
- use GraphQL::Batch
41
- <% end %>end
14
+ end
42
15
  <% end -%>
data/lib/graphql.rb CHANGED
@@ -110,6 +110,7 @@ require "graphql/execution"
110
110
  require "graphql/runtime_type_error"
111
111
  require "graphql/unresolved_type_error"
112
112
  require "graphql/invalid_null_error"
113
+ require "graphql/pagination"
113
114
  require "graphql/schema"
114
115
  require "graphql/query"
115
116
  require "graphql/directive"
@@ -124,6 +125,9 @@ require "graphql/string_type"
124
125
  require "graphql/schema/built_in_types"
125
126
  require "graphql/schema/loader"
126
127
  require "graphql/schema/printer"
128
+ require "graphql/filter"
129
+ require "graphql/internal_representation"
130
+ require "graphql/static_validation"
127
131
  require "graphql/introspection"
128
132
 
129
133
  require "graphql/analysis_error"
@@ -132,12 +136,9 @@ require "graphql/invalid_name_error"
132
136
  require "graphql/integer_decoding_error"
133
137
  require "graphql/integer_encoding_error"
134
138
  require "graphql/string_encoding_error"
135
- require "graphql/internal_representation"
136
- require "graphql/static_validation"
137
139
  require "graphql/version"
138
140
  require "graphql/compatibility"
139
141
  require "graphql/function"
140
- require "graphql/filter"
141
142
  require "graphql/subscriptions"
142
143
  require "graphql/parse_error"
143
144
  require "graphql/backtrace"
@@ -147,4 +148,37 @@ require "graphql/authorization"
147
148
  require "graphql/unauthorized_error"
148
149
  require "graphql/unauthorized_field_error"
149
150
  require "graphql/load_application_object_failed_error"
150
- require "graphql/pagination"
151
+ require "graphql/dataloader"
152
+
153
+
154
+ module GraphQL
155
+ # Ruby has `deprecate_constant`,
156
+ # but I don't see a way to give a nice error message in that case,
157
+ # so I'm doing this instead.
158
+ DEPRECATED_INT_TYPE = INT_TYPE
159
+ DEPRECATED_FLOAT_TYPE = FLOAT_TYPE
160
+ DEPRECATED_STRING_TYPE = STRING_TYPE
161
+ DEPRECATED_BOOLEAN_TYPE = BOOLEAN_TYPE
162
+ DEPRECATED_ID_TYPE = ID_TYPE
163
+
164
+ remove_const :INT_TYPE
165
+ remove_const :FLOAT_TYPE
166
+ remove_const :STRING_TYPE
167
+ remove_const :BOOLEAN_TYPE
168
+ remove_const :ID_TYPE
169
+
170
+ def self.const_missing(const_name)
171
+ deprecated_const_name = :"DEPRECATED_#{const_name}"
172
+ if const_defined?(deprecated_const_name)
173
+ deprecated_type = const_get(deprecated_const_name)
174
+ deprecated_caller = caller(1, 1).first
175
+ # Don't warn about internal uses, like `types.Int`
176
+ if !deprecated_caller.include?("lib/graphql")
177
+ warn "GraphQL::#{const_name} is deprecated and will be removed in GraphQL-Ruby 2.0, use GraphQL::Types::#{deprecated_type.graphql_name} instead. (from #{deprecated_caller})"
178
+ end
179
+ deprecated_type
180
+ else
181
+ super
182
+ end
183
+ end
184
+ end
@@ -3,6 +3,11 @@ module GraphQL
3
3
  module Analysis
4
4
  module_function
5
5
 
6
+ def use(schema_class)
7
+ schema = schema_class.is_a?(Class) ? schema_class : schema_class.target
8
+ schema.analysis_engine = self
9
+ end
10
+
6
11
  # @return [void]
7
12
  def analyze_multiplex(multiplex, analyzers)
8
13
  multiplex.trace("analyze_multiplex", { multiplex: multiplex }) do
@@ -38,6 +43,8 @@ module GraphQL
38
43
  # @param analyzers [Array<#call>] Objects that respond to `#call(memo, visit_type, irep_node)`
39
44
  # @return [Array<Any>] Results from those analyzers
40
45
  def analyze_query(query, analyzers, multiplex_states: [])
46
+ warn "Legacy analysis will be removed in GraphQL-Ruby 2.0, please upgrade to AST Analysis: https://graphql-ruby.org/queries/ast_analysis.html (schema: #{query.schema})"
47
+
41
48
  query.trace("analyze_query", { query: query }) do
42
49
  analyzers_to_run = analyzers.select do |analyzer|
43
50
  if analyzer.respond_to?(:analyze?)
@@ -13,7 +13,12 @@ module GraphQL
13
13
  module_function
14
14
 
15
15
  def use(schema_class)
16
- schema_class.analysis_engine = GraphQL::Analysis::AST
16
+ if schema_class.analysis_engine == self
17
+ definition_line = caller(2, 1).first
18
+ warn("GraphQL::Analysis::AST is now the default; remove `use GraphQL::Analysis::AST` from the schema definition (#{definition_line})")
19
+ else
20
+ schema_class.analysis_engine = self
21
+ end
17
22
  end
18
23
 
19
24
  # Analyze a multiplex, and all queries within.
@@ -67,7 +72,11 @@ module GraphQL
67
72
 
68
73
  visitor.visit
69
74
 
70
- query_analyzers.map(&:result)
75
+ if visitor.rescued_errors.any?
76
+ visitor.rescued_errors
77
+ else
78
+ query_analyzers.map(&:result)
79
+ end
71
80
  else
72
81
  []
73
82
  end
@@ -19,6 +19,7 @@ module GraphQL
19
19
  @field_definitions = []
20
20
  @argument_definitions = []
21
21
  @directive_definitions = []
22
+ @rescued_errors = []
22
23
  @query = query
23
24
  @schema = query.schema
24
25
  @response_path = []
@@ -32,6 +33,9 @@ module GraphQL
32
33
  # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
33
34
  attr_reader :object_types
34
35
 
36
+ # @return [Array<GraphQL::AnalysisError]
37
+ attr_reader :rescued_errors
38
+
35
39
  def visit
36
40
  return unless @document
37
41
  super
@@ -239,7 +243,11 @@ module GraphQL
239
243
 
240
244
  def call_analyzers(method, node, parent)
241
245
  @analyzers.each do |analyzer|
242
- analyzer.public_send(method, node, parent, self)
246
+ begin
247
+ analyzer.public_send(method, node, parent, self)
248
+ rescue AnalysisError => err
249
+ @rescued_errors << err
250
+ end
243
251
  end
244
252
  end
245
253
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/backtrace/inspect_result"
3
+ require "graphql/backtrace/legacy_tracer"
3
4
  require "graphql/backtrace/table"
4
5
  require "graphql/backtrace/traced_error"
5
6
  require "graphql/backtrace/tracer"
@@ -9,13 +10,12 @@ module GraphQL
9
10
  # {TracedError} provides a GraphQL backtrace with arguments and return values.
10
11
  # The underlying error is available as {TracedError#cause}.
11
12
  #
12
- # WARNING: {.enable} is not threadsafe because {GraphQL::Tracing.install} is not threadsafe.
13
- #
14
13
  # @example toggling backtrace annotation
15
- # # to enable:
16
- # GraphQL::Backtrace.enable
17
- # # later, to disable:
18
- # GraphQL::Backtrace.disable
14
+ # class MySchema < GraphQL::Schema
15
+ # if Rails.env.development? || Rails.env.test?
16
+ # use GraphQL::Backtrace
17
+ # end
18
+ # end
19
19
  #
20
20
  class Backtrace
21
21
  include Enumerable
@@ -23,19 +23,13 @@ module GraphQL
23
23
 
24
24
  def_delegators :to_a, :each, :[]
25
25
 
26
- def self.enable
27
- warn("GraphQL::Backtrace.enable is deprecated, add `use GraphQL::Backtrace` to your schema definition instead.")
28
- GraphQL::Tracing.install(Backtrace::Tracer)
29
- nil
30
- end
31
-
32
- def self.disable
33
- GraphQL::Tracing.uninstall(Backtrace::Tracer)
34
- nil
35
- end
36
-
37
- def self.use(schema_defn)
38
- schema_defn.tracer(self::Tracer)
26
+ def self.use(schema_defn, legacy: false)
27
+ tracer = if legacy
28
+ self::LegacyTracer
29
+ else
30
+ self::Tracer
31
+ end
32
+ schema_defn.tracer(tracer)
39
33
  end
40
34
 
41
35
  def initialize(context, value: nil)
@@ -51,5 +45,20 @@ module GraphQL
51
45
  def to_a
52
46
  @table.to_backtrace
53
47
  end
48
+
49
+ # Used for internal bookkeeping
50
+ # @api private
51
+ class Frame
52
+ attr_reader :path, :query, :ast_node, :object, :field, :arguments, :parent_frame
53
+ def initialize(path:, query:, ast_node:, object:, field:, arguments:, parent_frame:)
54
+ @path = path
55
+ @query = query
56
+ @ast_node = ast_node
57
+ @field = field
58
+ @object = object
59
+ @arguments = arguments
60
+ @parent_frame = parent_frame
61
+ end
62
+ end
54
63
  end
55
64
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Backtrace
4
+ module LegacyTracer
5
+ module_function
6
+
7
+ # Implement the {GraphQL::Tracing} API.
8
+ def trace(key, metadata)
9
+ case key
10
+ when "lex", "parse"
11
+ # No context here, don't have a query yet
12
+ nil
13
+ when "execute_multiplex", "analyze_multiplex"
14
+ # No query context yet
15
+ nil
16
+ when "validate", "analyze_query", "execute_query", "execute_query_lazy"
17
+ query = metadata[:query] || metadata[:queries].first
18
+ push_data = query
19
+ multiplex = query.multiplex
20
+ when "execute_field", "execute_field_lazy"
21
+ # The interpreter passes `query:`, legacy passes `context:`
22
+ context = metadata[:context] || ((q = metadata[:query]) && q.context)
23
+ push_data = context
24
+ multiplex = context.query.multiplex
25
+ else
26
+ # Custom key, no backtrace data for this
27
+ nil
28
+ end
29
+
30
+ if push_data
31
+ multiplex.context[:last_graphql_backtrace_context] = push_data
32
+ end
33
+
34
+ if key == "execute_multiplex"
35
+ begin
36
+ yield
37
+ rescue StandardError => err
38
+ # This is an unhandled error from execution,
39
+ # Re-raise it with a GraphQL trace.
40
+ potential_context = metadata[:multiplex].context[:last_graphql_backtrace_context]
41
+
42
+ if potential_context.is_a?(GraphQL::Query::Context) || potential_context.is_a?(GraphQL::Query::Context::FieldResolutionContext)
43
+ raise TracedError.new(err, potential_context)
44
+ else
45
+ raise
46
+ end
47
+ ensure
48
+ metadata[:multiplex].context.delete(:last_graphql_backtrace_context)
49
+ end
50
+ else
51
+ yield
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end