graphql 2.0.13 → 2.3.10

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 (228) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
  3. data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
  4. data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
  5. data/lib/generators/graphql/install_generator.rb +3 -0
  6. data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
  7. data/lib/generators/graphql/mutation_update_generator.rb +1 -1
  8. data/lib/generators/graphql/relay.rb +18 -1
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +2 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +2 -0
  12. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  13. data/lib/generators/graphql/templates/base_field.erb +2 -0
  14. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  15. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  16. data/lib/generators/graphql/templates/base_object.erb +2 -0
  17. data/lib/generators/graphql/templates/base_resolver.erb +6 -0
  18. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  19. data/lib/generators/graphql/templates/base_union.erb +2 -0
  20. data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
  21. data/lib/generators/graphql/templates/loader.erb +2 -0
  22. data/lib/generators/graphql/templates/mutation.erb +2 -0
  23. data/lib/generators/graphql/templates/node_type.erb +2 -0
  24. data/lib/generators/graphql/templates/query_type.erb +2 -0
  25. data/lib/generators/graphql/templates/schema.erb +8 -0
  26. data/lib/graphql/analysis/analyzer.rb +89 -0
  27. data/lib/graphql/analysis/field_usage.rb +82 -0
  28. data/lib/graphql/analysis/max_query_complexity.rb +20 -0
  29. data/lib/graphql/analysis/max_query_depth.rb +20 -0
  30. data/lib/graphql/analysis/query_complexity.rb +183 -0
  31. data/lib/graphql/analysis/query_depth.rb +58 -0
  32. data/lib/graphql/analysis/visitor.rb +283 -0
  33. data/lib/graphql/analysis.rb +92 -1
  34. data/lib/graphql/backtrace/inspect_result.rb +0 -12
  35. data/lib/graphql/backtrace/table.rb +2 -2
  36. data/lib/graphql/backtrace/trace.rb +93 -0
  37. data/lib/graphql/backtrace/tracer.rb +1 -1
  38. data/lib/graphql/backtrace.rb +2 -1
  39. data/lib/graphql/coercion_error.rb +1 -9
  40. data/lib/graphql/dataloader/async_dataloader.rb +88 -0
  41. data/lib/graphql/dataloader/null_dataloader.rb +1 -1
  42. data/lib/graphql/dataloader/request.rb +5 -0
  43. data/lib/graphql/dataloader/source.rb +89 -45
  44. data/lib/graphql/dataloader.rb +115 -142
  45. data/lib/graphql/duration_encoding_error.rb +16 -0
  46. data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
  47. data/lib/graphql/execution/interpreter/arguments.rb +1 -1
  48. data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
  49. data/lib/graphql/execution/interpreter/resolve.rb +19 -0
  50. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
  51. data/lib/graphql/execution/interpreter/runtime.rb +331 -455
  52. data/lib/graphql/execution/interpreter.rb +125 -61
  53. data/lib/graphql/execution/lazy.rb +6 -12
  54. data/lib/graphql/execution/lookahead.rb +124 -46
  55. data/lib/graphql/execution/multiplex.rb +3 -117
  56. data/lib/graphql/execution.rb +0 -1
  57. data/lib/graphql/introspection/directive_type.rb +3 -3
  58. data/lib/graphql/introspection/dynamic_fields.rb +1 -1
  59. data/lib/graphql/introspection/entry_points.rb +11 -5
  60. data/lib/graphql/introspection/field_type.rb +2 -2
  61. data/lib/graphql/introspection/schema_type.rb +10 -13
  62. data/lib/graphql/introspection/type_type.rb +17 -10
  63. data/lib/graphql/introspection.rb +3 -2
  64. data/lib/graphql/language/block_string.rb +34 -18
  65. data/lib/graphql/language/definition_slice.rb +1 -1
  66. data/lib/graphql/language/document_from_schema_definition.rb +75 -59
  67. data/lib/graphql/language/lexer.rb +358 -1506
  68. data/lib/graphql/language/nodes.rb +166 -93
  69. data/lib/graphql/language/parser.rb +795 -1953
  70. data/lib/graphql/language/printer.rb +340 -160
  71. data/lib/graphql/language/sanitized_printer.rb +21 -23
  72. data/lib/graphql/language/static_visitor.rb +167 -0
  73. data/lib/graphql/language/visitor.rb +188 -141
  74. data/lib/graphql/language.rb +61 -1
  75. data/lib/graphql/load_application_object_failed_error.rb +5 -1
  76. data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
  77. data/lib/graphql/pagination/array_connection.rb +6 -6
  78. data/lib/graphql/pagination/connection.rb +33 -6
  79. data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
  80. data/lib/graphql/query/context/scoped_context.rb +101 -0
  81. data/lib/graphql/query/context.rb +117 -112
  82. data/lib/graphql/query/null_context.rb +12 -25
  83. data/lib/graphql/query/validation_pipeline.rb +6 -5
  84. data/lib/graphql/query/variables.rb +3 -3
  85. data/lib/graphql/query.rb +86 -30
  86. data/lib/graphql/railtie.rb +9 -6
  87. data/lib/graphql/rake_task.rb +29 -11
  88. data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +59 -23
  90. data/lib/graphql/schema/always_visible.rb +11 -0
  91. data/lib/graphql/schema/argument.rb +55 -26
  92. data/lib/graphql/schema/base_64_encoder.rb +3 -5
  93. data/lib/graphql/schema/build_from_definition.rb +56 -32
  94. data/lib/graphql/schema/directive/one_of.rb +24 -0
  95. data/lib/graphql/schema/directive/specified_by.rb +14 -0
  96. data/lib/graphql/schema/directive/transform.rb +1 -1
  97. data/lib/graphql/schema/directive.rb +15 -3
  98. data/lib/graphql/schema/enum.rb +35 -24
  99. data/lib/graphql/schema/enum_value.rb +2 -3
  100. data/lib/graphql/schema/field/connection_extension.rb +2 -16
  101. data/lib/graphql/schema/field/scope_extension.rb +8 -1
  102. data/lib/graphql/schema/field.rb +147 -107
  103. data/lib/graphql/schema/field_extension.rb +1 -4
  104. data/lib/graphql/schema/find_inherited_value.rb +2 -7
  105. data/lib/graphql/schema/has_single_input_argument.rb +158 -0
  106. data/lib/graphql/schema/input_object.rb +47 -11
  107. data/lib/graphql/schema/interface.rb +15 -21
  108. data/lib/graphql/schema/introspection_system.rb +7 -17
  109. data/lib/graphql/schema/late_bound_type.rb +10 -0
  110. data/lib/graphql/schema/list.rb +2 -2
  111. data/lib/graphql/schema/loader.rb +2 -3
  112. data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
  113. data/lib/graphql/schema/member/build_type.rb +11 -3
  114. data/lib/graphql/schema/member/has_arguments.rb +170 -130
  115. data/lib/graphql/schema/member/has_ast_node.rb +12 -0
  116. data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
  117. data/lib/graphql/schema/member/has_directives.rb +81 -61
  118. data/lib/graphql/schema/member/has_fields.rb +100 -38
  119. data/lib/graphql/schema/member/has_interfaces.rb +65 -10
  120. data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
  121. data/lib/graphql/schema/member/has_validators.rb +32 -6
  122. data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
  123. data/lib/graphql/schema/member/scoped.rb +19 -0
  124. data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
  125. data/lib/graphql/schema/member/validates_input.rb +3 -3
  126. data/lib/graphql/schema/mutation.rb +7 -0
  127. data/lib/graphql/schema/object.rb +16 -5
  128. data/lib/graphql/schema/printer.rb +11 -8
  129. data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
  130. data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
  131. data/lib/graphql/schema/resolver.rb +47 -32
  132. data/lib/graphql/schema/scalar.rb +3 -3
  133. data/lib/graphql/schema/subscription.rb +11 -4
  134. data/lib/graphql/schema/subset.rb +397 -0
  135. data/lib/graphql/schema/timeout.rb +25 -29
  136. data/lib/graphql/schema/type_expression.rb +2 -2
  137. data/lib/graphql/schema/type_membership.rb +3 -0
  138. data/lib/graphql/schema/union.rb +11 -2
  139. data/lib/graphql/schema/unique_within_type.rb +1 -1
  140. data/lib/graphql/schema/validator/all_validator.rb +60 -0
  141. data/lib/graphql/schema/validator.rb +4 -2
  142. data/lib/graphql/schema/warden.rb +238 -93
  143. data/lib/graphql/schema.rb +498 -103
  144. data/lib/graphql/static_validation/all_rules.rb +2 -1
  145. data/lib/graphql/static_validation/base_visitor.rb +7 -6
  146. data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
  147. data/lib/graphql/static_validation/literal_validator.rb +24 -7
  148. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
  149. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  150. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
  151. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  152. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
  153. data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
  154. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  155. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  156. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  157. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  158. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  159. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  160. data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
  161. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
  162. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
  163. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  164. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
  165. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
  166. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  167. data/lib/graphql/static_validation/validation_context.rb +5 -5
  168. data/lib/graphql/static_validation/validator.rb +4 -1
  169. data/lib/graphql/static_validation.rb +0 -1
  170. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
  171. data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
  172. data/lib/graphql/subscriptions/event.rb +11 -10
  173. data/lib/graphql/subscriptions/serialize.rb +2 -0
  174. data/lib/graphql/subscriptions.rb +20 -13
  175. data/lib/graphql/testing/helpers.rb +151 -0
  176. data/lib/graphql/testing.rb +2 -0
  177. data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
  178. data/lib/graphql/tracing/appoptics_trace.rb +251 -0
  179. data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
  180. data/lib/graphql/tracing/appsignal_trace.rb +77 -0
  181. data/lib/graphql/tracing/data_dog_trace.rb +183 -0
  182. data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
  183. data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
  184. data/lib/graphql/tracing/legacy_trace.rb +69 -0
  185. data/lib/graphql/tracing/new_relic_trace.rb +75 -0
  186. data/lib/graphql/tracing/notifications_trace.rb +45 -0
  187. data/lib/graphql/tracing/platform_trace.rb +118 -0
  188. data/lib/graphql/tracing/platform_tracing.rb +17 -3
  189. data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
  190. data/lib/graphql/tracing/prometheus_trace.rb +89 -0
  191. data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
  192. data/lib/graphql/tracing/scout_trace.rb +72 -0
  193. data/lib/graphql/tracing/sentry_trace.rb +112 -0
  194. data/lib/graphql/tracing/statsd_trace.rb +56 -0
  195. data/lib/graphql/tracing/trace.rb +76 -0
  196. data/lib/graphql/tracing.rb +20 -40
  197. data/lib/graphql/type_kinds.rb +7 -4
  198. data/lib/graphql/types/iso_8601_duration.rb +77 -0
  199. data/lib/graphql/types/relay/base_connection.rb +1 -1
  200. data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
  201. data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
  202. data/lib/graphql/types/relay/node_behaviors.rb +8 -2
  203. data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
  204. data/lib/graphql/types/relay.rb +0 -1
  205. data/lib/graphql/types/string.rb +1 -1
  206. data/lib/graphql/types.rb +1 -0
  207. data/lib/graphql/version.rb +1 -1
  208. data/lib/graphql.rb +27 -20
  209. data/readme.md +13 -3
  210. metadata +96 -47
  211. data/lib/graphql/analysis/ast/analyzer.rb +0 -84
  212. data/lib/graphql/analysis/ast/field_usage.rb +0 -57
  213. data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
  214. data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
  215. data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
  216. data/lib/graphql/analysis/ast/query_depth.rb +0 -55
  217. data/lib/graphql/analysis/ast/visitor.rb +0 -269
  218. data/lib/graphql/analysis/ast.rb +0 -81
  219. data/lib/graphql/deprecation.rb +0 -9
  220. data/lib/graphql/filter.rb +0 -53
  221. data/lib/graphql/language/lexer.rl +0 -280
  222. data/lib/graphql/language/parser.y +0 -554
  223. data/lib/graphql/language/token.rb +0 -34
  224. data/lib/graphql/schema/base_64_bp.rb +0 -26
  225. data/lib/graphql/schema/invalid_type_error.rb +0 -7
  226. data/lib/graphql/static_validation/type_stack.rb +0 -216
  227. data/lib/graphql/subscriptions/instrumentation.rb +0 -28
  228. data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Analysis
4
+ # A query reducer for measuring the depth of a given query.
5
+ #
6
+ # See https://graphql-ruby.org/queries/ast_analysis.html for more examples.
7
+ #
8
+ # @example Logging the depth of a query
9
+ # class LogQueryDepth < GraphQL::Analysis::QueryDepth
10
+ # def result
11
+ # log("GraphQL query depth: #{@max_depth}")
12
+ # end
13
+ # end
14
+ #
15
+ # # In your Schema file:
16
+ #
17
+ # class MySchema < GraphQL::Schema
18
+ # query_analyzer LogQueryDepth
19
+ # end
20
+ #
21
+ # # When you run the query, the depth will get logged:
22
+ #
23
+ # Schema.execute(query_str)
24
+ # # GraphQL query depth: 8
25
+ #
26
+ class QueryDepth < Analyzer
27
+ def initialize(query)
28
+ @max_depth = 0
29
+ @current_depth = 0
30
+ @count_introspection_fields = query.schema.count_introspection_fields
31
+ super
32
+ end
33
+
34
+ def on_enter_field(node, parent, visitor)
35
+ return if visitor.skipping? ||
36
+ visitor.visiting_fragment_definition? ||
37
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
38
+
39
+ @current_depth += 1
40
+ end
41
+
42
+ def on_leave_field(node, parent, visitor)
43
+ return if visitor.skipping? ||
44
+ visitor.visiting_fragment_definition? ||
45
+ (@count_introspection_fields == false && visitor.field_definition.introspection?)
46
+
47
+ if @max_depth < @current_depth
48
+ @max_depth = @current_depth
49
+ end
50
+ @current_depth -= 1
51
+ end
52
+
53
+ def result
54
+ @max_depth
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,283 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Analysis
4
+ # Depth first traversal through a query AST, calling AST analyzers
5
+ # along the way.
6
+ #
7
+ # The visitor is a special case of GraphQL::Language::StaticVisitor, visiting
8
+ # only the selected operation, providing helpers for common use cases such
9
+ # as skipped fields and visiting fragment spreads.
10
+ #
11
+ # @see {GraphQL::Analysis::Analyzer} AST Analyzers for queries
12
+ class Visitor < GraphQL::Language::StaticVisitor
13
+ def initialize(query:, analyzers:)
14
+ @analyzers = analyzers
15
+ @path = []
16
+ @object_types = []
17
+ @directives = []
18
+ @field_definitions = []
19
+ @argument_definitions = []
20
+ @directive_definitions = []
21
+ @rescued_errors = []
22
+ @query = query
23
+ @schema = query.schema
24
+ @types = query.types
25
+ @response_path = []
26
+ @skip_stack = [false]
27
+ super(query.selected_operation)
28
+ end
29
+
30
+ # @return [GraphQL::Query] the query being visited
31
+ attr_reader :query
32
+
33
+ # @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
34
+ attr_reader :object_types
35
+
36
+ # @return [Array<GraphQL::AnalysisError]
37
+ attr_reader :rescued_errors
38
+
39
+ def visit
40
+ return unless @document
41
+ super
42
+ end
43
+
44
+ # Visit Helpers
45
+
46
+ # @return [GraphQL::Execution::Interpreter::Arguments] Arguments for this node, merging default values, literal values and query variables
47
+ # @see {GraphQL::Query#arguments_for}
48
+ def arguments_for(ast_node, field_definition)
49
+ @query.arguments_for(ast_node, field_definition)
50
+ end
51
+
52
+ # @return [Boolean] If the visitor is currently inside a fragment definition
53
+ def visiting_fragment_definition?
54
+ @in_fragment_def
55
+ end
56
+
57
+ # @return [Boolean] If the current node should be skipped because of a skip or include directive
58
+ def skipping?
59
+ @skipping
60
+ end
61
+
62
+ # @return [Array<String>] The path to the response key for the current field
63
+ def response_path
64
+ @response_path.dup
65
+ end
66
+
67
+ # Visitor Hooks
68
+ [
69
+ :operation_definition, :fragment_definition,
70
+ :inline_fragment, :field, :directive, :argument, :fragment_spread
71
+ ].each do |node_type|
72
+ module_eval <<-RUBY, __FILE__, __LINE__
73
+ def call_on_enter_#{node_type}(node, parent)
74
+ @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
80
+ end
81
+ end
82
+
83
+ def call_on_leave_#{node_type}(node, parent)
84
+ @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
90
+ end
91
+ end
92
+
93
+ RUBY
94
+ end
95
+
96
+ def on_operation_definition(node, parent)
97
+ object_type = @schema.root_type_for_operation(node.operation_type)
98
+ @object_types.push(object_type)
99
+ @path.push("#{node.operation_type}#{node.name ? " #{node.name}" : ""}")
100
+ call_on_enter_operation_definition(node, parent)
101
+ super
102
+ call_on_leave_operation_definition(node, parent)
103
+ @object_types.pop
104
+ @path.pop
105
+ end
106
+
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
+ 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)
128
+ end
129
+ end
130
+
131
+ def on_field(node, parent)
132
+ @response_path.push(node.alias || node.name)
133
+ parent_type = @object_types.last
134
+ # This could be nil if the previous field wasn't found:
135
+ field_definition = parent_type && @types.field(parent_type, node.name)
136
+ @field_definitions.push(field_definition)
137
+ if !field_definition.nil?
138
+ next_object_type = field_definition.type.unwrap
139
+ @object_types.push(next_object_type)
140
+ else
141
+ @object_types.push(nil)
142
+ end
143
+ @path.push(node.alias || node.name)
144
+
145
+ @skipping = @skip_stack.last || skip?(node)
146
+ @skip_stack << @skipping
147
+
148
+ call_on_enter_field(node, parent)
149
+ super
150
+ @skipping = @skip_stack.pop
151
+ call_on_leave_field(node, parent)
152
+ @response_path.pop
153
+ @field_definitions.pop
154
+ @object_types.pop
155
+ @path.pop
156
+ end
157
+
158
+ def on_directive(node, parent)
159
+ directive_defn = @schema.directives[node.name]
160
+ @directive_definitions.push(directive_defn)
161
+ call_on_enter_directive(node, parent)
162
+ super
163
+ call_on_leave_directive(node, parent)
164
+ @directive_definitions.pop
165
+ end
166
+
167
+ def on_argument(node, parent)
168
+ argument_defn = if (arg = @argument_definitions.last)
169
+ arg_type = arg.type.unwrap
170
+ if arg_type.kind.input_object?
171
+ @types.argument(arg_type, node.name)
172
+ else
173
+ nil
174
+ end
175
+ elsif (directive_defn = @directive_definitions.last)
176
+ @types.argument(directive_defn, node.name)
177
+ elsif (field_defn = @field_definitions.last)
178
+ @types.argument(field_defn, node.name)
179
+ else
180
+ nil
181
+ end
182
+
183
+ @argument_definitions.push(argument_defn)
184
+ @path.push(node.name)
185
+ call_on_enter_argument(node, parent)
186
+ super
187
+ call_on_leave_argument(node, parent)
188
+ @argument_definitions.pop
189
+ @path.pop
190
+ end
191
+
192
+ def on_fragment_spread(node, parent)
193
+ @path.push("... #{node.name}")
194
+ @skipping = @skip_stack.last || skip?(node)
195
+ @skip_stack << @skipping
196
+
197
+ call_on_enter_fragment_spread(node, parent)
198
+ enter_fragment_spread_inline(node)
199
+ super
200
+ @skipping = @skip_stack.pop
201
+ leave_fragment_spread_inline(node)
202
+ call_on_leave_fragment_spread(node, parent)
203
+ @path.pop
204
+ end
205
+
206
+ # @return [GraphQL::BaseType] The current object type
207
+ def type_definition
208
+ @object_types.last
209
+ end
210
+
211
+ # @return [GraphQL::BaseType] The type which the current type came from
212
+ def parent_type_definition
213
+ @object_types[-2]
214
+ end
215
+
216
+ # @return [GraphQL::Field, nil] The most-recently-entered GraphQL::Field, if currently inside one
217
+ def field_definition
218
+ @field_definitions.last
219
+ end
220
+
221
+ # @return [GraphQL::Field, nil] The GraphQL field which returned the object that the current field belongs to
222
+ def previous_field_definition
223
+ @field_definitions[-2]
224
+ end
225
+
226
+ # @return [GraphQL::Directive, nil] The most-recently-entered GraphQL::Directive, if currently inside one
227
+ def directive_definition
228
+ @directive_definitions.last
229
+ end
230
+
231
+ # @return [GraphQL::Argument, nil] The most-recently-entered GraphQL::Argument, if currently inside one
232
+ def argument_definition
233
+ @argument_definitions.last
234
+ end
235
+
236
+ # @return [GraphQL::Argument, nil] The previous GraphQL argument
237
+ def previous_argument_definition
238
+ @argument_definitions[-2]
239
+ end
240
+
241
+ private
242
+
243
+ # Visit a fragment spread inline instead of visiting the definition
244
+ # by itself.
245
+ def enter_fragment_spread_inline(fragment_spread)
246
+ fragment_def = query.fragments[fragment_spread.name]
247
+
248
+ object_type = if fragment_def.type
249
+ @types.type(fragment_def.type.name)
250
+ else
251
+ object_types.last
252
+ end
253
+
254
+ object_types << object_type
255
+
256
+ on_fragment_definition_children(fragment_def)
257
+ end
258
+
259
+ # Visit a fragment spread inline instead of visiting the definition
260
+ # by itself.
261
+ def leave_fragment_spread_inline(_fragment_spread)
262
+ object_types.pop
263
+ end
264
+
265
+ def skip?(ast_node)
266
+ dir = ast_node.directives
267
+ dir.any? && !GraphQL::Execution::DirectiveChecks.include?(dir, query)
268
+ end
269
+
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
275
+ end
276
+ @object_types.push(object_type)
277
+ yield(node)
278
+ @object_types.pop
279
+ @path.pop
280
+ end
281
+ end
282
+ end
283
+ end
@@ -1,2 +1,93 @@
1
1
  # frozen_string_literal: true
2
- require "graphql/analysis/ast"
2
+ require "graphql/analysis/visitor"
3
+ require "graphql/analysis/analyzer"
4
+ require "graphql/analysis/field_usage"
5
+ require "graphql/analysis/query_complexity"
6
+ require "graphql/analysis/max_query_complexity"
7
+ require "graphql/analysis/query_depth"
8
+ require "graphql/analysis/max_query_depth"
9
+ require "timeout"
10
+
11
+ module GraphQL
12
+ module Analysis
13
+ AST = self
14
+ module_function
15
+ # Analyze a multiplex, and all queries within.
16
+ # Multiplex analyzers are ran for all queries, keeping state.
17
+ # Query analyzers are ran per query, without carrying state between queries.
18
+ #
19
+ # @param multiplex [GraphQL::Execution::Multiplex]
20
+ # @param analyzers [Array<GraphQL::Analysis::Analyzer>]
21
+ # @return [Array<Any>] Results from multiplex analyzers
22
+ def analyze_multiplex(multiplex, analyzers)
23
+ multiplex_analyzers = analyzers.map { |analyzer| analyzer.new(multiplex) }
24
+
25
+ multiplex.current_trace.analyze_multiplex(multiplex: multiplex) do
26
+ query_results = multiplex.queries.map do |query|
27
+ if query.valid?
28
+ analyze_query(
29
+ query,
30
+ query.analyzers,
31
+ multiplex_analyzers: multiplex_analyzers
32
+ )
33
+ else
34
+ []
35
+ end
36
+ end
37
+
38
+ multiplex_results = multiplex_analyzers.map(&:result)
39
+ multiplex_errors = analysis_errors(multiplex_results)
40
+
41
+ multiplex.queries.each_with_index do |query, idx|
42
+ query.analysis_errors = multiplex_errors + analysis_errors(query_results[idx])
43
+ end
44
+ multiplex_results
45
+ end
46
+ end
47
+
48
+ # @param query [GraphQL::Query]
49
+ # @param analyzers [Array<GraphQL::Analysis::Analyzer>]
50
+ # @return [Array<Any>] Results from those analyzers
51
+ def analyze_query(query, analyzers, multiplex_analyzers: [])
52
+ query.current_trace.analyze_query(query: query) do
53
+ query_analyzers = analyzers
54
+ .map { |analyzer| analyzer.new(query) }
55
+ .tap { _1.select!(&:analyze?) }
56
+
57
+ analyzers_to_run = query_analyzers + multiplex_analyzers
58
+ if analyzers_to_run.any?
59
+
60
+ analyzers_to_run.select!(&:visit?)
61
+ if analyzers_to_run.any?
62
+ visitor = GraphQL::Analysis::Visitor.new(
63
+ query: query,
64
+ analyzers: analyzers_to_run
65
+ )
66
+
67
+ # `nil` or `0` causes no timeout
68
+ Timeout::timeout(query.validate_timeout_remaining) do
69
+ visitor.visit
70
+ end
71
+
72
+ if visitor.rescued_errors.any?
73
+ return visitor.rescued_errors
74
+ end
75
+ end
76
+
77
+ query_analyzers.map(&:result)
78
+ else
79
+ []
80
+ end
81
+ end
82
+ rescue Timeout::Error
83
+ [GraphQL::AnalysisError.new("Timeout on validation of query")]
84
+ rescue GraphQL::UnauthorizedError
85
+ # This error was raised during analysis and will be returned the client before execution
86
+ []
87
+ end
88
+
89
+ def analysis_errors(results)
90
+ results.flatten.tap { _1.select! { |r| r.is_a?(GraphQL::AnalysisError) } }
91
+ end
92
+ end
93
+ end
@@ -16,12 +16,6 @@ module GraphQL
16
16
  "[" +
17
17
  obj.map { |v| inspect_truncated(v) }.join(", ") +
18
18
  "]"
19
- when Query::Context::SharedMethods
20
- if obj.invalid_null?
21
- "nil"
22
- else
23
- inspect_truncated(obj.value)
24
- end
25
19
  else
26
20
  inspect_truncated(obj)
27
21
  end
@@ -33,12 +27,6 @@ module GraphQL
33
27
  "{...}"
34
28
  when Array
35
29
  "[...]"
36
- when Query::Context::SharedMethods
37
- if obj.invalid_null?
38
- "nil"
39
- else
40
- inspect_truncated(obj.value)
41
- end
42
30
  when GraphQL::Execution::Lazy
43
31
  "(unresolved)"
44
32
  else
@@ -83,7 +83,7 @@ module GraphQL
83
83
  value = if top && @override_value
84
84
  @override_value
85
85
  else
86
- value_at(@context.query.context.namespace(:interpreter)[:runtime], context_entry.path)
86
+ value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
87
87
  end
88
88
  rows << [
89
89
  "#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
@@ -112,7 +112,7 @@ module GraphQL
112
112
  if object.is_a?(GraphQL::Schema::Object)
113
113
  object = object.object
114
114
  end
115
- value = value_at(context_entry.namespace(:interpreter)[:runtime], [])
115
+ value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
116
116
  rows << [
117
117
  "#{position}",
118
118
  "#{op_type}#{op_name ? " #{op_name}" : ""}",
@@ -0,0 +1,93 @@
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
@@ -25,7 +25,7 @@ module GraphQL
25
25
  when "execute_field", "execute_field_lazy"
26
26
  query = metadata[:query]
27
27
  multiplex = query.multiplex
28
- push_key = metadata[:path]
28
+ push_key = query.context[:current_path]
29
29
  parent_frame = multiplex.context[:graphql_backtrace_contexts][push_key[0..-2]]
30
30
 
31
31
  if parent_frame.is_a?(GraphQL::Query)
@@ -3,6 +3,7 @@ require "graphql/backtrace/inspect_result"
3
3
  require "graphql/backtrace/table"
4
4
  require "graphql/backtrace/traced_error"
5
5
  require "graphql/backtrace/tracer"
6
+ require "graphql/backtrace/trace"
6
7
  module GraphQL
7
8
  # Wrap unhandled errors with {TracedError}.
8
9
  #
@@ -23,7 +24,7 @@ module GraphQL
23
24
  def_delegators :to_a, :each, :[]
24
25
 
25
26
  def self.use(schema_defn)
26
- schema_defn.tracer(self::Tracer)
27
+ schema_defn.trace_with(self::Trace)
27
28
  end
28
29
 
29
30
  def initialize(context, value: nil)
@@ -1,13 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- class CoercionError < GraphQL::Error
4
- # @return [Hash] Optional custom data for error objects which will be added
5
- # under the `extensions` key.
6
- attr_accessor :extensions
7
-
8
- def initialize(message, extensions: nil)
9
- @extensions = extensions
10
- super(message)
11
- end
3
+ class CoercionError < GraphQL::ExecutionError
12
4
  end
13
5
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Dataloader
4
+ class AsyncDataloader < Dataloader
5
+ def yield
6
+ if (condition = Thread.current[:graphql_dataloader_next_tick])
7
+ condition.wait
8
+ else
9
+ Fiber.yield
10
+ end
11
+ nil
12
+ end
13
+
14
+ def run
15
+ job_fibers = []
16
+ next_job_fibers = []
17
+ source_tasks = []
18
+ next_source_tasks = []
19
+ first_pass = true
20
+ sources_condition = Async::Condition.new
21
+ manager = spawn_fiber do
22
+ while first_pass || job_fibers.any?
23
+ first_pass = false
24
+ fiber_vars = get_fiber_variables
25
+
26
+ while (f = (job_fibers.shift || spawn_job_fiber))
27
+ if f.alive?
28
+ finished = run_fiber(f)
29
+ if !finished
30
+ next_job_fibers << f
31
+ end
32
+ end
33
+ end
34
+ job_fibers.concat(next_job_fibers)
35
+ next_job_fibers.clear
36
+
37
+ Sync do |root_task|
38
+ set_fiber_variables(fiber_vars)
39
+ while source_tasks.any? || @source_cache.each_value.any? { |group_sources| group_sources.each_value.any?(&:pending?) }
40
+ while (task = source_tasks.shift || spawn_source_task(root_task, sources_condition))
41
+ if task.alive?
42
+ root_task.yield # give the source task a chance to run
43
+ next_source_tasks << task
44
+ end
45
+ end
46
+ sources_condition.signal
47
+ source_tasks.concat(next_source_tasks)
48
+ next_source_tasks.clear
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ manager.resume
55
+ if manager.alive?
56
+ raise "Invariant: Manager didn't terminate successfully: #{manager}"
57
+ end
58
+
59
+ rescue UncaughtThrowError => e
60
+ throw e.tag, e.value
61
+ end
62
+
63
+ private
64
+
65
+ def spawn_source_task(parent_task, condition)
66
+ pending_sources = nil
67
+ @source_cache.each_value do |source_by_batch_params|
68
+ source_by_batch_params.each_value do |source|
69
+ if source.pending?
70
+ pending_sources ||= []
71
+ pending_sources << source
72
+ end
73
+ end
74
+ end
75
+
76
+ if pending_sources
77
+ fiber_vars = get_fiber_variables
78
+ parent_task.async do
79
+ set_fiber_variables(fiber_vars)
80
+ Thread.current[:graphql_dataloader_next_tick] = condition
81
+ pending_sources.each(&:run_pending_keys)
82
+ cleanup_fiber
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end