graphql 1.10.1 → 1.13.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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +18 -2
  3. data/lib/generators/graphql/install_generator.rb +36 -6
  4. data/lib/generators/graphql/loader_generator.rb +1 -0
  5. data/lib/generators/graphql/mutation_generator.rb +2 -1
  6. data/lib/generators/graphql/object_generator.rb +54 -9
  7. data/lib/generators/graphql/relay.rb +63 -0
  8. data/lib/generators/graphql/relay_generator.rb +21 -0
  9. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  10. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  11. data/lib/generators/graphql/templates/base_edge.erb +8 -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_mutation.erb +2 -0
  17. data/lib/generators/graphql/templates/base_object.erb +2 -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/enum.erb +2 -0
  21. data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
  22. data/lib/generators/graphql/templates/interface.erb +2 -0
  23. data/lib/generators/graphql/templates/loader.erb +2 -0
  24. data/lib/generators/graphql/templates/mutation.erb +2 -0
  25. data/lib/generators/graphql/templates/mutation_type.erb +2 -0
  26. data/lib/generators/graphql/templates/node_type.erb +9 -0
  27. data/lib/generators/graphql/templates/object.erb +3 -1
  28. data/lib/generators/graphql/templates/query_type.erb +3 -3
  29. data/lib/generators/graphql/templates/scalar.erb +2 -0
  30. data/lib/generators/graphql/templates/schema.erb +21 -33
  31. data/lib/generators/graphql/templates/union.erb +3 -1
  32. data/lib/generators/graphql/type_generator.rb +1 -1
  33. data/lib/graphql/analysis/analyze_query.rb +7 -0
  34. data/lib/graphql/analysis/ast/field_usage.rb +24 -1
  35. data/lib/graphql/analysis/ast/query_complexity.rb +126 -109
  36. data/lib/graphql/analysis/ast/visitor.rb +13 -5
  37. data/lib/graphql/analysis/ast.rb +11 -2
  38. data/lib/graphql/argument.rb +3 -3
  39. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  40. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  41. data/lib/graphql/backtrace/table.rb +34 -3
  42. data/lib/graphql/backtrace/traced_error.rb +0 -1
  43. data/lib/graphql/backtrace/tracer.rb +40 -9
  44. data/lib/graphql/backtrace.rb +28 -19
  45. data/lib/graphql/backwards_compatibility.rb +2 -1
  46. data/lib/graphql/base_type.rb +1 -1
  47. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
  48. data/lib/graphql/compatibility/execution_specification.rb +1 -0
  49. data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
  50. data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
  51. data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
  52. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  53. data/lib/graphql/dataloader/request.rb +19 -0
  54. data/lib/graphql/dataloader/request_all.rb +19 -0
  55. data/lib/graphql/dataloader/source.rb +155 -0
  56. data/lib/graphql/dataloader.rb +308 -0
  57. data/lib/graphql/define/assign_global_id_field.rb +2 -2
  58. data/lib/graphql/define/defined_object_proxy.rb +1 -1
  59. data/lib/graphql/define/instance_definable.rb +34 -4
  60. data/lib/graphql/define/type_definer.rb +5 -5
  61. data/lib/graphql/deprecated_dsl.rb +18 -5
  62. data/lib/graphql/deprecation.rb +9 -0
  63. data/lib/graphql/directive.rb +4 -4
  64. data/lib/graphql/enum_type.rb +7 -1
  65. data/lib/graphql/execution/errors.rb +110 -7
  66. data/lib/graphql/execution/execute.rb +8 -1
  67. data/lib/graphql/execution/instrumentation.rb +1 -1
  68. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  69. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  70. data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
  71. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  72. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  73. data/lib/graphql/execution/interpreter/runtime.rb +685 -421
  74. data/lib/graphql/execution/interpreter.rb +42 -13
  75. data/lib/graphql/execution/lazy.rb +5 -1
  76. data/lib/graphql/execution/lookahead.rb +25 -110
  77. data/lib/graphql/execution/multiplex.rb +37 -25
  78. data/lib/graphql/field.rb +5 -1
  79. data/lib/graphql/function.rb +4 -0
  80. data/lib/graphql/input_object_type.rb +6 -0
  81. data/lib/graphql/integer_decoding_error.rb +17 -0
  82. data/lib/graphql/integer_encoding_error.rb +18 -2
  83. data/lib/graphql/interface_type.rb +7 -0
  84. data/lib/graphql/internal_representation/document.rb +2 -2
  85. data/lib/graphql/internal_representation/rewrite.rb +1 -1
  86. data/lib/graphql/internal_representation/scope.rb +2 -2
  87. data/lib/graphql/internal_representation/visit.rb +2 -2
  88. data/lib/graphql/introspection/directive_type.rb +8 -4
  89. data/lib/graphql/introspection/entry_points.rb +2 -2
  90. data/lib/graphql/introspection/enum_value_type.rb +2 -2
  91. data/lib/graphql/introspection/field_type.rb +9 -5
  92. data/lib/graphql/introspection/input_value_type.rb +15 -3
  93. data/lib/graphql/introspection/introspection_query.rb +6 -92
  94. data/lib/graphql/introspection/schema_type.rb +4 -4
  95. data/lib/graphql/introspection/type_type.rb +16 -12
  96. data/lib/graphql/introspection.rb +96 -0
  97. data/lib/graphql/invalid_null_error.rb +18 -0
  98. data/lib/graphql/language/block_string.rb +20 -5
  99. data/lib/graphql/language/cache.rb +37 -0
  100. data/lib/graphql/language/document_from_schema_definition.rb +73 -25
  101. data/lib/graphql/language/lexer.rb +4 -3
  102. data/lib/graphql/language/lexer.rl +3 -3
  103. data/lib/graphql/language/nodes.rb +51 -89
  104. data/lib/graphql/language/parser.rb +552 -530
  105. data/lib/graphql/language/parser.y +114 -99
  106. data/lib/graphql/language/printer.rb +7 -2
  107. data/lib/graphql/language/sanitized_printer.rb +222 -0
  108. data/lib/graphql/language/token.rb +0 -4
  109. data/lib/graphql/language/visitor.rb +2 -2
  110. data/lib/graphql/language.rb +2 -0
  111. data/lib/graphql/name_validator.rb +2 -7
  112. data/lib/graphql/object_type.rb +44 -35
  113. data/lib/graphql/pagination/active_record_relation_connection.rb +14 -1
  114. data/lib/graphql/pagination/array_connection.rb +2 -2
  115. data/lib/graphql/pagination/connection.rb +75 -20
  116. data/lib/graphql/pagination/connections.rb +83 -31
  117. data/lib/graphql/pagination/relation_connection.rb +34 -14
  118. data/lib/graphql/parse_error.rb +0 -1
  119. data/lib/graphql/query/arguments.rb +4 -3
  120. data/lib/graphql/query/arguments_cache.rb +1 -2
  121. data/lib/graphql/query/context.rb +42 -7
  122. data/lib/graphql/query/executor.rb +0 -1
  123. data/lib/graphql/query/fingerprint.rb +26 -0
  124. data/lib/graphql/query/input_validation_result.rb +23 -6
  125. data/lib/graphql/query/literal_input.rb +1 -1
  126. data/lib/graphql/query/null_context.rb +24 -8
  127. data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
  128. data/lib/graphql/query/serial_execution.rb +1 -0
  129. data/lib/graphql/query/validation_pipeline.rb +5 -2
  130. data/lib/graphql/query/variable_validation_error.rb +1 -1
  131. data/lib/graphql/query/variables.rb +14 -4
  132. data/lib/graphql/query.rb +68 -13
  133. data/lib/graphql/railtie.rb +9 -1
  134. data/lib/graphql/rake_task.rb +12 -9
  135. data/lib/graphql/relay/array_connection.rb +10 -12
  136. data/lib/graphql/relay/base_connection.rb +26 -13
  137. data/lib/graphql/relay/connection_instrumentation.rb +4 -4
  138. data/lib/graphql/relay/connection_type.rb +1 -1
  139. data/lib/graphql/relay/edges_instrumentation.rb +0 -1
  140. data/lib/graphql/relay/mutation.rb +1 -0
  141. data/lib/graphql/relay/node.rb +3 -0
  142. data/lib/graphql/relay/range_add.rb +23 -9
  143. data/lib/graphql/relay/relation_connection.rb +8 -10
  144. data/lib/graphql/relay/type_extensions.rb +2 -0
  145. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  146. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  147. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  148. data/lib/graphql/rubocop.rb +4 -0
  149. data/lib/graphql/scalar_type.rb +16 -1
  150. data/lib/graphql/schema/addition.rb +247 -0
  151. data/lib/graphql/schema/argument.rb +210 -12
  152. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  153. data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
  154. data/lib/graphql/schema/build_from_definition.rb +213 -86
  155. data/lib/graphql/schema/default_type_error.rb +2 -0
  156. data/lib/graphql/schema/directive/deprecated.rb +1 -1
  157. data/lib/graphql/schema/directive/feature.rb +1 -1
  158. data/lib/graphql/schema/directive/flagged.rb +57 -0
  159. data/lib/graphql/schema/directive/include.rb +1 -1
  160. data/lib/graphql/schema/directive/skip.rb +1 -1
  161. data/lib/graphql/schema/directive/transform.rb +14 -2
  162. data/lib/graphql/schema/directive.rb +78 -2
  163. data/lib/graphql/schema/enum.rb +80 -9
  164. data/lib/graphql/schema/enum_value.rb +17 -6
  165. data/lib/graphql/schema/field/connection_extension.rb +46 -30
  166. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  167. data/lib/graphql/schema/field.rb +285 -133
  168. data/lib/graphql/schema/find_inherited_value.rb +4 -1
  169. data/lib/graphql/schema/finder.rb +5 -5
  170. data/lib/graphql/schema/input_object.rb +97 -89
  171. data/lib/graphql/schema/interface.rb +24 -19
  172. data/lib/graphql/schema/late_bound_type.rb +2 -2
  173. data/lib/graphql/schema/list.rb +7 -1
  174. data/lib/graphql/schema/loader.rb +137 -103
  175. data/lib/graphql/schema/member/accepts_definition.rb +8 -1
  176. data/lib/graphql/schema/member/base_dsl_methods.rb +15 -19
  177. data/lib/graphql/schema/member/build_type.rb +14 -7
  178. data/lib/graphql/schema/member/has_arguments.rb +205 -12
  179. data/lib/graphql/schema/member/has_ast_node.rb +4 -1
  180. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  181. data/lib/graphql/schema/member/has_directives.rb +98 -0
  182. data/lib/graphql/schema/member/has_fields.rb +95 -30
  183. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  184. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  185. data/lib/graphql/schema/member/has_validators.rb +31 -0
  186. data/lib/graphql/schema/member/instrumentation.rb +0 -1
  187. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  188. data/lib/graphql/schema/member.rb +6 -0
  189. data/lib/graphql/schema/middleware_chain.rb +1 -1
  190. data/lib/graphql/schema/mutation.rb +4 -0
  191. data/lib/graphql/schema/non_null.rb +5 -0
  192. data/lib/graphql/schema/object.rb +47 -46
  193. data/lib/graphql/schema/possible_types.rb +9 -4
  194. data/lib/graphql/schema/printer.rb +16 -34
  195. data/lib/graphql/schema/relay_classic_mutation.rb +32 -4
  196. data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
  197. data/lib/graphql/schema/resolver.rb +123 -63
  198. data/lib/graphql/schema/scalar.rb +11 -1
  199. data/lib/graphql/schema/subscription.rb +57 -21
  200. data/lib/graphql/schema/timeout.rb +29 -15
  201. data/lib/graphql/schema/timeout_middleware.rb +3 -1
  202. data/lib/graphql/schema/type_expression.rb +1 -1
  203. data/lib/graphql/schema/type_membership.rb +18 -4
  204. data/lib/graphql/schema/union.rb +41 -1
  205. data/lib/graphql/schema/unique_within_type.rb +1 -2
  206. data/lib/graphql/schema/validation.rb +12 -2
  207. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  208. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  209. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  210. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  211. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  212. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  213. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  214. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  215. data/lib/graphql/schema/validator.rb +174 -0
  216. data/lib/graphql/schema/warden.rb +153 -28
  217. data/lib/graphql/schema.rb +364 -330
  218. data/lib/graphql/static_validation/all_rules.rb +1 -0
  219. data/lib/graphql/static_validation/base_visitor.rb +8 -5
  220. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  221. data/lib/graphql/static_validation/error.rb +3 -1
  222. data/lib/graphql/static_validation/literal_validator.rb +51 -26
  223. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
  224. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  225. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
  226. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  227. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  228. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -43
  229. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  230. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  231. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  232. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  233. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
  234. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +6 -7
  235. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
  236. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -8
  237. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
  238. data/lib/graphql/static_validation/validation_context.rb +9 -3
  239. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  240. data/lib/graphql/static_validation/validator.rb +42 -8
  241. data/lib/graphql/static_validation.rb +1 -0
  242. data/lib/graphql/string_encoding_error.rb +13 -3
  243. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +118 -19
  244. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  245. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  246. data/lib/graphql/subscriptions/event.rb +81 -30
  247. data/lib/graphql/subscriptions/instrumentation.rb +0 -1
  248. data/lib/graphql/subscriptions/serialize.rb +33 -6
  249. data/lib/graphql/subscriptions/subscription_root.rb +15 -4
  250. data/lib/graphql/subscriptions.rb +88 -45
  251. data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
  252. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  253. data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
  254. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  255. data/lib/graphql/tracing/platform_tracing.rb +43 -17
  256. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  257. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  258. data/lib/graphql/tracing/skylight_tracing.rb +1 -1
  259. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  260. data/lib/graphql/tracing.rb +9 -33
  261. data/lib/graphql/types/big_int.rb +5 -1
  262. data/lib/graphql/types/int.rb +10 -3
  263. data/lib/graphql/types/iso_8601_date.rb +3 -3
  264. data/lib/graphql/types/iso_8601_date_time.rb +25 -10
  265. data/lib/graphql/types/relay/base_connection.rb +6 -90
  266. data/lib/graphql/types/relay/base_edge.rb +2 -34
  267. data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
  268. data/lib/graphql/types/relay/default_relay.rb +27 -0
  269. data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
  270. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  271. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  272. data/lib/graphql/types/relay/node.rb +2 -4
  273. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  274. data/lib/graphql/types/relay/node_field.rb +2 -20
  275. data/lib/graphql/types/relay/nodes_field.rb +2 -20
  276. data/lib/graphql/types/relay/page_info.rb +2 -14
  277. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  278. data/lib/graphql/types/relay.rb +11 -3
  279. data/lib/graphql/types/string.rb +8 -2
  280. data/lib/graphql/unauthorized_error.rb +2 -2
  281. data/lib/graphql/union_type.rb +2 -0
  282. data/lib/graphql/upgrader/member.rb +1 -0
  283. data/lib/graphql/upgrader/schema.rb +1 -0
  284. data/lib/graphql/version.rb +1 -1
  285. data/lib/graphql.rb +65 -31
  286. data/readme.md +3 -6
  287. metadata +77 -112
  288. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  289. data/lib/graphql/literal_validation_error.rb +0 -6
  290. data/lib/graphql/types/relay/base_field.rb +0 -22
  291. data/lib/graphql/types/relay/base_interface.rb +0 -29
  292. data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -6,20 +6,13 @@ module GraphQL
6
6
  #
7
7
  # Attach as a plugin.
8
8
  #
9
- # @example Using new default connections
10
- # class MySchema < GraphQL::Schema
11
- # use GraphQL::Pagination::Connections
12
- # end
13
- #
14
9
  # @example Adding a custom wrapper
15
10
  # class MySchema < GraphQL::Schema
16
- # use GraphQL::Pagination::Connections
17
11
  # connections.add(MyApp::SearchResults, MyApp::SearchResultsConnection)
18
12
  # end
19
13
  #
20
14
  # @example Removing default connection support for arrays (they can still be manually wrapped)
21
15
  # class MySchema < GraphQL::Schema
22
- # use GraphQL::Pagination::Connections
23
16
  # connections.delete(Array)
24
17
  # end
25
18
  #
@@ -29,17 +22,14 @@ module GraphQL
29
22
  end
30
23
 
31
24
  def self.use(schema_defn)
32
- if schema_defn.is_a?(Class)
33
- schema_defn.connections = self.new
34
- else
35
- # Unwrap a `.define` object
36
- schema_defn = schema_defn.target
37
- schema_defn.connections = self.new
38
- schema_defn.class.connections = schema_defn.connections
25
+ if schema_defn.plugins.any? { |(plugin, args)| plugin == self }
26
+ GraphQL::Deprecation.warn("#{self} is now the default, remove `use #{self}` from #{caller(2,1).first}")
39
27
  end
28
+ schema_defn.connections = self.new(schema: schema_defn)
40
29
  end
41
30
 
42
- def initialize
31
+ def initialize(schema:)
32
+ @schema = schema
43
33
  @wrappers = {}
44
34
  add_default
45
35
  end
@@ -52,29 +42,86 @@ module GraphQL
52
42
  @wrappers.delete(nodes_class)
53
43
  end
54
44
 
55
- # Used by the runtime to wrap values in connection wrappers.
56
- # @api Private
57
- def wrap(field, object, arguments, context)
45
+ def all_wrappers
46
+ all_wrappers = {}
47
+ @schema.ancestors.reverse_each do |schema_class|
48
+ if schema_class.respond_to?(:connections) && (c = schema_class.connections)
49
+ all_wrappers.merge!(c.wrappers)
50
+ end
51
+ end
52
+ all_wrappers
53
+ end
54
+
55
+ def wrapper_for(items, wrappers: all_wrappers)
58
56
  impl = nil
59
- object.class.ancestors.each { |cls|
60
- impl = @wrappers[cls]
57
+
58
+ items.class.ancestors.each { |cls|
59
+ impl = wrappers[cls]
61
60
  break if impl
62
61
  }
63
62
 
64
- if impl.nil?
65
- raise ImplementationMissingError, "Couldn't find a connection wrapper for #{object.class} during #{field.path} (#{object.inspect})"
63
+ impl
64
+ end
65
+
66
+ # Used by the runtime to wrap values in connection wrappers.
67
+ # @api Private
68
+ def wrap(field, parent, items, arguments, context)
69
+ return items if GraphQL::Execution::Interpreter::RawValue === items
70
+ wrappers = context ? context.namespace(:connections)[:all_wrappers] : all_wrappers
71
+ impl = wrapper_for(items, wrappers: wrappers)
72
+
73
+ if impl
74
+ impl.new(
75
+ items,
76
+ context: context,
77
+ parent: parent,
78
+ field: field,
79
+ max_page_size: field.has_max_page_size? ? field.max_page_size : context.schema.default_max_page_size,
80
+ first: arguments[:first],
81
+ after: arguments[:after],
82
+ last: arguments[:last],
83
+ before: arguments[:before],
84
+ arguments: arguments,
85
+ edge_class: edge_class_for_field(field),
86
+ )
87
+ else
88
+ begin
89
+ connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(items)
90
+ if parent.is_a?(GraphQL::Schema::Object)
91
+ parent = parent.object
92
+ end
93
+ connection_class.new(
94
+ items,
95
+ arguments,
96
+ field: field,
97
+ max_page_size: field.max_page_size,
98
+ parent: parent,
99
+ context: context,
100
+ )
101
+ rescue RuntimeError => err
102
+ if err.message.include?("No connection implementation to wrap")
103
+ raise ImplementationMissingError, "Couldn't find a connection wrapper for #{items.class} during #{field.path} (#{items.inspect})"
104
+ else
105
+ raise err
106
+ end
107
+ end
66
108
  end
109
+ end
67
110
 
68
- impl.new(
69
- object,
70
- context: context,
71
- max_page_size: field.max_page_size || context.schema.default_max_page_size,
72
- first: arguments[:first],
73
- after: arguments[:after],
74
- last: arguments[:last],
75
- before: arguments[:before],
76
- )
111
+ # use an override if there is one
112
+ # @api private
113
+ def edge_class_for_field(field)
114
+ conn_type = field.type.unwrap
115
+ conn_type_edge_type = conn_type.respond_to?(:edge_class) && conn_type.edge_class
116
+ if conn_type_edge_type && conn_type_edge_type != Relay::Edge
117
+ conn_type_edge_type
118
+ else
119
+ nil
120
+ end
77
121
  end
122
+ protected
123
+
124
+ attr_reader :wrappers
78
125
 
79
126
  private
80
127
 
@@ -102,6 +149,11 @@ module GraphQL
102
149
  if defined?(Mongoid::Association::Referenced::HasMany::Targets::Enumerable)
103
150
  add(Mongoid::Association::Referenced::HasMany::Targets::Enumerable, Pagination::MongoidRelationConnection)
104
151
  end
152
+
153
+ # Mongoid 7.3+
154
+ if defined?(Mongoid::Association::Referenced::HasMany::Enumerable)
155
+ add(Mongoid::Association::Referenced::HasMany::Enumerable, Pagination::MongoidRelationConnection)
156
+ end
105
157
  end
106
158
  end
107
159
  end
@@ -12,7 +12,7 @@ module GraphQL
12
12
 
13
13
  def has_previous_page
14
14
  if @has_previous_page.nil?
15
- @has_previous_page = if @after_offset && @after_offset > 0
15
+ @has_previous_page = if after_offset && after_offset > 0
16
16
  true
17
17
  elsif last
18
18
  # See whether there are any nodes _before_ the current offset.
@@ -29,10 +29,14 @@ module GraphQL
29
29
 
30
30
  def has_next_page
31
31
  if @has_next_page.nil?
32
- @has_next_page = if @before_offset && @before_offset > 0
32
+ @has_next_page = if before_offset && before_offset > 0
33
33
  true
34
34
  elsif first
35
- relation_count(set_limit(sliced_nodes, first + 1)) == first + 1
35
+ if @nodes && @nodes.count < first
36
+ false
37
+ else
38
+ relation_larger_than(sliced_nodes, first)
39
+ end
36
40
  else
37
41
  false
38
42
  end
@@ -44,11 +48,18 @@ module GraphQL
44
48
  load_nodes
45
49
  # index in nodes + existing offset + 1 (because it's offset, not index)
46
50
  offset = nodes.index(item) + 1 + (@paged_nodes_offset || 0) + (relation_offset(items) || 0)
47
- context.schema.cursor_encoder.encode(offset.to_s)
51
+ encode(offset.to_s)
48
52
  end
49
53
 
50
54
  private
51
55
 
56
+ # @param relation [Object] A database query object
57
+ # @param size [Integer] The value against which we check the relation size
58
+ # @return [Boolean] True if the number of items in this relation is larger than `size`
59
+ def relation_larger_than(relation, size)
60
+ relation_count(set_limit(relation, size + 1)) == size + 1
61
+ end
62
+
52
63
  # @param relation [Object] A database query object
53
64
  # @return [Integer, nil] The offset value, or nil if there isn't one
54
65
  def relation_offset(relation)
@@ -105,40 +116,49 @@ module GraphQL
105
116
  def sliced_nodes
106
117
  @sliced_nodes ||= begin
107
118
  paginated_nodes = items
108
- @after_offset = after && offset_from_cursor(after)
109
- @before_offset = before && offset_from_cursor(before)
110
119
 
111
- if @after_offset
120
+ if after_offset
112
121
  previous_offset = relation_offset(items) || 0
113
- paginated_nodes = set_offset(paginated_nodes, previous_offset + @after_offset)
122
+ paginated_nodes = set_offset(paginated_nodes, previous_offset + after_offset)
114
123
  end
115
124
 
116
- if @before_offset && @after_offset
117
- if @after_offset < @before_offset
125
+ if before_offset && after_offset
126
+ if after_offset < before_offset
118
127
  # Get the number of items between the two cursors
119
- space_between = @before_offset - @after_offset - 1
128
+ space_between = before_offset - after_offset - 1
120
129
  paginated_nodes = set_limit(paginated_nodes, space_between)
121
130
  else
122
131
  # TODO I think this is untested
123
132
  # The cursors overextend one another to an empty set
124
133
  paginated_nodes = null_relation(paginated_nodes)
125
134
  end
126
- elsif @before_offset
135
+ elsif before_offset
127
136
  # Use limit to cut off the tail of the relation
128
- paginated_nodes = set_limit(paginated_nodes, @before_offset - 1)
137
+ paginated_nodes = set_limit(paginated_nodes, before_offset - 1)
129
138
  end
130
139
 
131
140
  paginated_nodes
132
141
  end
133
142
  end
134
143
 
144
+ # @return [Integer, nil]
145
+ def before_offset
146
+ @before_offset ||= before && offset_from_cursor(before)
147
+ end
148
+
149
+ # @return [Integer, nil]
150
+ def after_offset
151
+ @after_offset ||= after && offset_from_cursor(after)
152
+ end
153
+
135
154
  # Apply `first` and `last` to `sliced_nodes`,
136
155
  # returning a new relation
137
156
  def limited_nodes
138
157
  @limited_nodes ||= begin
139
158
  paginated_nodes = sliced_nodes
159
+ previous_limit = relation_limit(paginated_nodes)
140
160
 
141
- if first && (relation_limit(paginated_nodes).nil? || relation_limit(paginated_nodes) > first) && last.nil?
161
+ if first && (previous_limit.nil? || previous_limit > first)
142
162
  # `first` would create a stricter limit that the one already applied, so add it
143
163
  paginated_nodes = set_limit(paginated_nodes, first)
144
164
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: language/parser.rb
3
2
  module GraphQL
4
3
  class ParseError < GraphQL::Error
5
4
  attr_reader :line, :col, :query
@@ -9,7 +9,7 @@ module GraphQL
9
9
  include GraphQL::Dig
10
10
 
11
11
  def self.construct_arguments_class(argument_owner)
12
- argument_definitions = argument_owner.arguments
12
+ argument_definitions = argument_owner.arguments # rubocop:disable Development/ContextIsPassedCop -- legacy-related
13
13
  argument_owner.arguments_class = Class.new(self) do
14
14
  self.argument_owner = argument_owner
15
15
  self.argument_definitions = argument_definitions
@@ -22,7 +22,7 @@ module GraphQL
22
22
  method_names.each do |method_name|
23
23
  # Don't define a helper method if it would override something.
24
24
  if method_defined?(method_name)
25
- warn(
25
+ GraphQL::Deprecation.warn(
26
26
  "Unable to define a helper for argument with name '#{method_name}' "\
27
27
  "as this is a reserved name. Add `method_access: false` to stop this warning."
28
28
  )
@@ -86,7 +86,8 @@ module GraphQL
86
86
  end
87
87
  end
88
88
 
89
- def_delegators :to_h, :keys, :values, :each, :any?
89
+ def_delegators :to_h, :keys, :values, :each
90
+ def_delegators :@argument_values, :any?
90
91
 
91
92
  def prepare
92
93
  self
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../query.rb
3
2
  module GraphQL
4
3
  class Query
5
4
  module ArgumentsCache
@@ -8,7 +7,7 @@ module GraphQL
8
7
  Hash.new do |h1, irep_or_ast_node|
9
8
  h1[irep_or_ast_node] = Hash.new do |h2, definition|
10
9
  ast_node = irep_or_ast_node.is_a?(GraphQL::InternalRepresentation::Node) ? irep_or_ast_node.ast_node : irep_or_ast_node
11
- h2[definition] = if definition.arguments.empty?
10
+ h2[definition] = if definition.arguments(query.context).empty?
12
11
  GraphQL::Query::Arguments::NO_ARGS
13
12
  else
14
13
  GraphQL::Query::LiteralInput.from_arguments(
@@ -1,6 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../execution/execute.rb
3
- # test_via: ../execution/lazy.rb
4
2
  module GraphQL
5
3
  class Query
6
4
  # Expose some query-specific info to field resolve functions.
@@ -34,7 +32,7 @@ module GraphQL
34
32
  # Remove this child from the result value
35
33
  # (used for null propagation and skip)
36
34
  # @api private
37
- def delete(child_ctx)
35
+ def delete_child(child_ctx)
38
36
  @value.delete(child_ctx.key)
39
37
  end
40
38
 
@@ -158,6 +156,10 @@ module GraphQL
158
156
  @scoped_context = {}
159
157
  end
160
158
 
159
+ def dataloader
160
+ @dataloader ||= self[:dataloader] || (query.multiplex ? query.multiplex.dataloader : schema.dataloader_class.new)
161
+ end
162
+
161
163
  # @api private
162
164
  attr_writer :interpreter
163
165
 
@@ -167,8 +169,10 @@ module GraphQL
167
169
  # @api private
168
170
  attr_accessor :scoped_context
169
171
 
170
- def_delegators :@provided_values, :[]=
171
- def_delegators :to_h, :fetch, :dig
172
+ def []=(key, value)
173
+ @provided_values[key] = value
174
+ end
175
+
172
176
  def_delegators :@query, :trace, :interpreter?
173
177
 
174
178
  # @!method []=(key, value)
@@ -180,6 +184,34 @@ module GraphQL
180
184
  @provided_values[key]
181
185
  end
182
186
 
187
+ def delete(key)
188
+ if @scoped_context.key?(key)
189
+ @scoped_context.delete(key)
190
+ else
191
+ @provided_values.delete(key)
192
+ end
193
+ end
194
+
195
+ UNSPECIFIED_FETCH_DEFAULT = Object.new
196
+
197
+ def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
198
+ if @scoped_context.key?(key)
199
+ @scoped_context[key]
200
+ elsif @provided_values.key?(key)
201
+ @provided_values[key]
202
+ elsif default != UNSPECIFIED_FETCH_DEFAULT
203
+ default
204
+ elsif block_given?
205
+ yield(self, key)
206
+ else
207
+ raise KeyError.new(key: key)
208
+ end
209
+ end
210
+
211
+ def dig(key, *other_keys)
212
+ @scoped_context.key?(key) ? @scoped_context.dig(key, *other_keys) : @provided_values.dig(key, *other_keys)
213
+ end
214
+
183
215
  def to_h
184
216
  @provided_values.merge(@scoped_context)
185
217
  end
@@ -191,9 +223,12 @@ module GraphQL
191
223
 
192
224
  # @return [GraphQL::Schema::Warden]
193
225
  def warden
194
- @warden ||= @query.warden
226
+ @warden ||= (@query && @query.warden)
195
227
  end
196
228
 
229
+ # @api private
230
+ attr_writer :warden
231
+
197
232
  # Get an isolated hash for `ns`. Doesn't affect user-provided storage.
198
233
  # @param ns [Object] a usage-specific namespace identifier
199
234
  # @return [Hash] namespaced storage
@@ -293,7 +328,7 @@ module GraphQL
293
328
  end
294
329
  when GraphQL::Execution::Execute::SKIP
295
330
  @parent.skipped = true
296
- @parent.delete(self)
331
+ @parent.delete_child(self)
297
332
  else
298
333
  @value = new_value
299
334
  end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../query.rb
3
2
  module GraphQL
4
3
  class Query
5
4
  class Executor
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest/sha2'
4
+
5
+ module GraphQL
6
+ class Query
7
+ # @api private
8
+ # @see Query#query_fingerprint
9
+ # @see Query#variables_fingerprint
10
+ # @see Query#fingerprint
11
+ module Fingerprint
12
+ # Make an obfuscated hash of the given string (either a query string or variables JSON)
13
+ # @param string [String]
14
+ # @return [String] A normalized, opaque hash
15
+ def self.generate(input_str)
16
+ # Implemented to be:
17
+ # - Short (and uniform) length
18
+ # - Stable
19
+ # - Irreversibly Opaque (don't want to leak variable values)
20
+ # - URL-friendly
21
+ bytes = Digest::SHA256.digest(input_str)
22
+ Base64.urlsafe_encode64(bytes)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -4,22 +4,39 @@ module GraphQL
4
4
  class InputValidationResult
5
5
  attr_accessor :problems
6
6
 
7
+ def initialize(valid: true, problems: nil)
8
+ @valid = valid
9
+ @problems = problems
10
+ end
11
+
7
12
  def valid?
8
- @problems.nil?
13
+ @valid
9
14
  end
10
15
 
11
- def add_problem(explanation, path = nil)
16
+ def add_problem(explanation, path = nil, extensions: nil, message: nil)
12
17
  @problems ||= []
13
- @problems.push({ "path" => path || [], "explanation" => explanation })
18
+ @valid = false
19
+ problem = { "path" => path || [], "explanation" => explanation }
20
+ if extensions
21
+ problem["extensions"] = extensions
22
+ end
23
+ if message
24
+ problem["message"] = message
25
+ end
26
+ @problems.push(problem)
14
27
  end
15
28
 
16
29
  def merge_result!(path, inner_result)
17
30
  return if inner_result.valid?
18
31
 
19
- inner_result.problems.each do |p|
20
- item_path = [path, *p["path"]]
21
- add_problem(p["explanation"], item_path)
32
+ if inner_result.problems
33
+ inner_result.problems.each do |p|
34
+ item_path = [path, *p["path"]]
35
+ add_problem(p["explanation"], item_path, message: p["message"], extensions: p["extensions"])
36
+ end
22
37
  end
38
+ # It could have been explicitly set on inner_result (if it had no problems)
39
+ @valid = false
23
40
  end
24
41
  end
25
42
  end
@@ -62,7 +62,7 @@ module GraphQL
62
62
  raise ArgumentError, "Unexpected ast_arguments: #{ast_arguments}"
63
63
  end
64
64
 
65
- argument_defns = argument_owner.arguments
65
+ argument_defns = argument_owner.arguments(context || GraphQL::Query::NullContext)
66
66
  argument_defns.each do |arg_name, arg_defn|
67
67
  ast_arg = indexed_arguments[arg_name]
68
68
  # First, check the argument in the AST.
@@ -4,16 +4,28 @@ module GraphQL
4
4
  # This object can be `ctx` in places where there is no query
5
5
  class NullContext
6
6
  class NullWarden < GraphQL::Schema::Warden
7
- def visible?(t); true; end
8
- def visible_field?(t); true; end
9
- def visible_type?(t); true; end
7
+ def visible_field?(field, ctx); true; end
8
+ def visible_argument?(arg, ctx); true; end
9
+ def visible_type?(type, ctx); true; end
10
+ def visible_enum_value?(ev, ctx); true; end
11
+ def visible_type_membership?(tm, ctx); true; end
10
12
  end
11
13
 
12
- attr_reader :schema, :query, :warden
14
+ class NullQuery
15
+ def with_error_handling
16
+ yield
17
+ end
18
+ end
19
+
20
+ class NullSchema < GraphQL::Schema
21
+ end
22
+
23
+ attr_reader :schema, :query, :warden, :dataloader
13
24
 
14
25
  def initialize
15
- @query = nil
16
- @schema = GraphQL::Schema.new
26
+ @query = NullQuery.new
27
+ @dataloader = GraphQL::Dataloader::NullDataloader.new
28
+ @schema = NullSchema
17
29
  @warden = NullWarden.new(
18
30
  GraphQL::Filter.new,
19
31
  context: self,
@@ -23,16 +35,20 @@ module GraphQL
23
35
 
24
36
  def [](key); end
25
37
 
38
+ def interpreter?
39
+ true
40
+ end
41
+
26
42
  class << self
27
43
  extend Forwardable
28
44
 
29
45
  def [](key); end
30
46
 
31
47
  def instance
32
- @instance = self.new
48
+ @instance ||= self.new
33
49
  end
34
50
 
35
- def_delegators :instance, :query, :schema, :warden
51
+ def_delegators :instance, :query, :warden, :schema, :interpreter?, :dataloader
36
52
  end
37
53
  end
38
54
  end
@@ -81,7 +81,7 @@ module GraphQL
81
81
  # is added to the "errors" key.
82
82
  def get_raw_value
83
83
  begin
84
- @field_ctx.schema.middleware.invoke([parent_type, target, field, arguments, @field_ctx])
84
+ @field_ctx.schema.middleware.invoke([parent_type, target, field, arguments, @field_ctx]) # rubocop:disable Development/ContextIsPassedCop -- unrelated
85
85
  rescue GraphQL::ExecutionError => err
86
86
  err
87
87
  end
@@ -16,6 +16,7 @@ module GraphQL
16
16
  # @param query_object [GraphQL::Query] the query object for this execution
17
17
  # @return [Hash] a spec-compliant GraphQL result, as a hash
18
18
  def execute(ast_operation, root_type, query_object)
19
+ GraphQL::Deprecation.warn "#{self.class} will be removed in GraphQL-Ruby 2.0, please upgrade to the Interpreter: https://graphql-ruby.org/queries/interpreter.html"
19
20
  operation_resolution.resolve(
20
21
  query_object.irep_selection,
21
22
  root_type,
@@ -36,7 +36,7 @@ module GraphQL
36
36
  @valid
37
37
  end
38
38
 
39
- # @return [Array<GraphQL::StaticValidation::Error >] Static validation errors for the query string
39
+ # @return [Array<GraphQL::StaticValidation::Error, GraphQL::Query::VariableValidationError>] Static validation errors for the query string
40
40
  def validation_errors
41
41
  ensure_has_validated
42
42
  @validation_errors
@@ -72,7 +72,7 @@ module GraphQL
72
72
  elsif @operation_name_error
73
73
  @validation_errors << @operation_name_error
74
74
  else
75
- validation_result = @schema.static_validator.validate(@query, validate: @validate)
75
+ validation_result = @schema.static_validator.validate(@query, validate: @validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
76
76
  @validation_errors.concat(validation_result[:errors])
77
77
  @internal_representation = validation_result[:irep]
78
78
 
@@ -90,6 +90,9 @@ module GraphQL
90
90
  end
91
91
 
92
92
  @valid = @validation_errors.empty?
93
+ rescue SystemStackError => err
94
+ @valid = false
95
+ @schema.query_stack_error(@query, err)
93
96
  end
94
97
 
95
98
  # If there are max_* values, add them,
@@ -23,7 +23,7 @@ module GraphQL
23
23
  # a one level deep merge explicitly. However beyond that only show the
24
24
  # latest value and problems.
25
25
  super.merge({ "extensions" => { "value" => value, "problems" => validation_result.problems }}) do |key, oldValue, newValue|
26
- if oldValue.respond_to? merge
26
+ if oldValue.respond_to?(:merge)
27
27
  oldValue.merge(newValue)
28
28
  else
29
29
  newValue
@@ -34,7 +34,9 @@ module GraphQL
34
34
  if validation_result.valid?
35
35
  if value_was_provided
36
36
  # Add the variable if a value was provided
37
- memo[variable_name] = if provided_value.nil?
37
+ memo[variable_name] = if ctx.interpreter?
38
+ provided_value
39
+ elsif provided_value.nil?
38
40
  nil
39
41
  else
40
42
  schema.error_handler.with_error_handling(context) do
@@ -42,11 +44,19 @@ module GraphQL
42
44
  end
43
45
  end
44
46
  elsif default_value != nil
45
- # Add the variable if it wasn't provided but it has a default value (including `null`)
46
- memo[variable_name] = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
47
+ memo[variable_name] = if ctx.interpreter?
48
+ if default_value.is_a?(Language::Nodes::NullValue)
49
+ nil
50
+ else
51
+ default_value
52
+ end
53
+ else
54
+ # Add the variable if it wasn't provided but it has a default value (including `null`)
55
+ GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
56
+ end
47
57
  end
48
58
  end
49
- rescue GraphQL::CoercionError, GraphQL::ExecutionError => ex
59
+ rescue GraphQL::ExecutionError => ex
50
60
  # TODO: This should really include the path to the problematic node in the variable value
51
61
  # like InputValidationResults generated by validate_non_null_input but unfortunately we don't
52
62
  # have this information available in the coerce_input call chain. Note this path is the path