graphql 1.10.1 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
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