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
data/lib/graphql/query.rb CHANGED
@@ -3,6 +3,7 @@ require "graphql/query/arguments"
3
3
  require "graphql/query/arguments_cache"
4
4
  require "graphql/query/context"
5
5
  require "graphql/query/executor"
6
+ require "graphql/query/fingerprint"
6
7
  require "graphql/query/literal_input"
7
8
  require "graphql/query/null_context"
8
9
  require "graphql/query/result"
@@ -87,6 +88,7 @@ module GraphQL
87
88
  schema = schema.graphql_definition
88
89
  end
89
90
  @schema = schema
91
+ @interpreter = @schema.interpreter?
90
92
  @filter = schema.default_filter.merge(except: except, only: only)
91
93
  @context = schema.context_class.new(query: self, object: root_value, values: context)
92
94
  @warden = warden
@@ -95,8 +97,7 @@ module GraphQL
95
97
  @fragments = nil
96
98
  @operations = nil
97
99
  @validate = validate
98
- # TODO: remove support for global tracers
99
- @tracers = schema.tracers + GraphQL::Tracing.tracers + (context ? context.fetch(:tracers, []) : [])
100
+ @tracers = schema.tracers + (context ? context.fetch(:tracers, []) : [])
100
101
  # Support `ctx[:backtrace] = true` for wrapping backtraces
101
102
  if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
102
103
  @tracers << GraphQL::Backtrace::Tracer
@@ -106,7 +107,7 @@ module GraphQL
106
107
  if variables.is_a?(String)
107
108
  raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
108
109
  else
109
- @provided_variables = variables
110
+ @provided_variables = variables || {}
110
111
  end
111
112
 
112
113
  @query_string = query_string || query
@@ -116,6 +117,10 @@ module GraphQL
116
117
  raise ArgumentError, "Query should only be provided a query string or a document, not both."
117
118
  end
118
119
 
120
+ if @query_string && !@query_string.is_a?(String)
121
+ raise ArgumentError, "Query string argument should be a String, got #{@query_string.class.name} instead."
122
+ end
123
+
119
124
  # A two-layer cache of type resolution:
120
125
  # { abstract_type => { value => resolved_type } }
121
126
  @resolved_types_cache = Hash.new do |h1, k1|
@@ -124,8 +129,6 @@ module GraphQL
124
129
  end
125
130
  end
126
131
 
127
- @arguments_cache = ArgumentsCache.build(self)
128
-
129
132
  # Trying to execute a document
130
133
  # with no operations returns an empty hash
131
134
  @ast_variables = []
@@ -150,7 +153,11 @@ module GraphQL
150
153
  @query_string ||= (document ? document.to_query_string : nil)
151
154
  end
152
155
 
153
- def_delegators :@schema, :interpreter?
156
+ def interpreter?
157
+ @interpreter
158
+ end
159
+
160
+ attr_accessor :multiplex
154
161
 
155
162
  def subscription_update?
156
163
  @subscription_topic && subscription?
@@ -192,9 +199,7 @@ module GraphQL
192
199
  # @return [Hash] A GraphQL response, with `"data"` and/or `"errors"` keys
193
200
  def result
194
201
  if !@executed
195
- with_prepared_ast {
196
- Execution::Multiplex.run_queries(@schema, [self], context: @context)
197
- }
202
+ Execution::Multiplex.run_queries(@schema, [self], context: @context)
198
203
  end
199
204
  @result ||= Query::Result.new(query: self, values: @result_values)
200
205
  end
@@ -243,10 +248,60 @@ module GraphQL
243
248
  end
244
249
 
245
250
  # Node-level cache for calculating arguments. Used during execution and query analysis.
246
- # @api private
247
- # @return [GraphQL::Query::Arguments] Arguments for this node, merging default values, literal values and query variables
248
- def arguments_for(irep_or_ast_node, definition)
249
- @arguments_cache[irep_or_ast_node][definition]
251
+ # @param ast_node [GraphQL::Language::Nodes::AbstractNode]
252
+ # @param definition [GraphQL::Schema::Field]
253
+ # @param parent_object [GraphQL::Schema::Object]
254
+ # @return Hash{Symbol => Object}
255
+ def arguments_for(ast_node, definition, parent_object: nil)
256
+ if interpreter?
257
+ arguments_cache.fetch(ast_node, definition, parent_object)
258
+ else
259
+ arguments_cache[ast_node][definition]
260
+ end
261
+ end
262
+
263
+ def arguments_cache
264
+ if interpreter?
265
+ @arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
266
+ else
267
+ @arguments_cache ||= ArgumentsCache.build(self)
268
+ end
269
+ end
270
+
271
+ # A version of the given query string, with:
272
+ # - Variables inlined to the query
273
+ # - Strings replaced with `<REDACTED>`
274
+ # @return [String, nil] Returns nil if the query is invalid.
275
+ def sanitized_query_string(inline_variables: true)
276
+ with_prepared_ast {
277
+ schema.sanitized_printer.new(self, inline_variables: inline_variables).sanitized_query_string
278
+ }
279
+ end
280
+
281
+ # This contains a few components:
282
+ #
283
+ # - The selected operation name (or `anonymous`)
284
+ # - The fingerprint of the query string
285
+ # - The number of given variables (for readability)
286
+ # - The fingerprint of the given variables
287
+ #
288
+ # This fingerprint can be used to track runs of the same operation-variables combination over time.
289
+ #
290
+ # @see operation_fingerprint
291
+ # @see variables_fingerprint
292
+ # @return [String] An opaque hash identifying this operation-variables combination
293
+ def fingerprint
294
+ @fingerprint ||= "#{operation_fingerprint}/#{variables_fingerprint}"
295
+ end
296
+
297
+ # @return [String] An opaque hash for identifying this query's given query string and selected operation
298
+ def operation_fingerprint
299
+ @operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string)}"
300
+ end
301
+
302
+ # @return [String] An opaque hash for identifying this query's given a variable values (not including defaults)
303
+ def variables_fingerprint
304
+ @variables_fingerprint ||= "#{provided_variables.size}/#{Fingerprint.generate(provided_variables.to_json)}"
250
305
  end
251
306
 
252
307
  def validation_pipeline
@@ -1,8 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module GraphQL
5
4
  class Railtie < Rails::Railtie
5
+ config.before_configuration do
6
+ # Bootsnap compile cache has similar expiration properties,
7
+ # so we assume that if the user has bootsnap setup it's ok
8
+ # to piggy back on it.
9
+ if ::Object.const_defined?("Bootsnap::CompileCache::ISeq") && Bootsnap::CompileCache::ISeq.cache_dir
10
+ Language::Parser.cache ||= Language::Cache.new(Pathname.new(Bootsnap::CompileCache::ISeq.cache_dir).join('graphql'))
11
+ end
12
+ end
13
+
6
14
  rake_tasks do
7
15
  # Defer this so that you only need the `parser` gem when you _run_ the upgrader
8
16
  def load_upgraders
@@ -76,15 +76,7 @@ module GraphQL
76
76
  # Set the parameters of this task by passing keyword arguments
77
77
  # or assigning attributes inside the block
78
78
  def initialize(options = {})
79
- default_dependencies = if Rake::Task.task_defined?("environment")
80
- [:environment]
81
- else
82
- []
83
- end
84
-
85
- all_options = DEFAULT_OPTIONS
86
- .merge(dependencies: default_dependencies)
87
- .merge(options)
79
+ all_options = DEFAULT_OPTIONS.merge(options)
88
80
  all_options.each do |k, v|
89
81
  self.public_send("#{k}=", v)
90
82
  end
@@ -106,6 +98,9 @@ module GraphQL
106
98
  result = schema.public_send(method_name, only: @only, except: @except, context: context)
107
99
  dir = File.dirname(file)
108
100
  FileUtils.mkdir_p(dir)
101
+ if !result.end_with?("\n")
102
+ result += "\n"
103
+ end
109
104
  File.write(file, result)
110
105
  end
111
106
 
@@ -117,18 +112,26 @@ module GraphQL
117
112
  File.join(@directory, @json_outfile)
118
113
  end
119
114
 
115
+ def load_rails_environment_if_defined
116
+ if Rake::Task.task_defined?('environment')
117
+ Rake::Task['environment'].invoke
118
+ end
119
+ end
120
+
120
121
  # Use the Rake DSL to add tasks
121
122
  def define_task
122
123
  namespace(@namespace) do
123
124
  namespace("schema") do
124
125
  desc("Dump the schema to IDL in #{idl_path}")
125
126
  task :idl => @dependencies do
127
+ load_rails_environment_if_defined
126
128
  write_outfile(:to_definition, idl_path)
127
129
  puts "Schema IDL dumped into #{idl_path}"
128
130
  end
129
131
 
130
132
  desc("Dump the schema to JSON in #{json_path}")
131
133
  task :json => @dependencies do
134
+ load_rails_environment_if_defined
132
135
  write_outfile(:to_json, json_path)
133
136
  puts "Schema JSON dumped into #{json_path}"
134
137
  end
@@ -31,24 +31,22 @@ module GraphQL
31
31
  end
32
32
  end
33
33
 
34
- private
35
-
36
34
  def first
37
- return @first if defined? @first
38
-
39
- @first = get_limited_arg(:first)
40
- @first = max_page_size if @first && max_page_size && @first > max_page_size
41
- @first
35
+ @first ||= begin
36
+ capped = limit_pagination_argument(arguments[:first], max_page_size)
37
+ if capped.nil? && last.nil?
38
+ capped = max_page_size
39
+ end
40
+ capped
41
+ end
42
42
  end
43
43
 
44
44
  def last
45
- return @last if defined? @last
46
-
47
- @last = get_limited_arg(:last)
48
- @last = max_page_size if @last && max_page_size && @last > max_page_size
49
- @last
45
+ @last ||= limit_pagination_argument(arguments[:last], max_page_size)
50
46
  end
51
47
 
48
+ private
49
+
52
50
  # apply first / last limit results
53
51
  def paged_nodes
54
52
  @paged_nodes ||= begin
@@ -59,6 +59,13 @@ module GraphQL
59
59
  # @param parent [Object] The object which this collection belongs to
60
60
  # @param context [GraphQL::Query::Context] The context from the field being resolved
61
61
  def initialize(nodes, arguments, field: nil, max_page_size: nil, parent: nil, context: nil)
62
+ GraphQL::Deprecation.warn "GraphQL::Relay::BaseConnection (used for #{self.class}) will be removed from GraphQL-Ruby 2.0, use GraphQL::Pagination::Connections instead: https://graphql-ruby.org/pagination/overview.html"
63
+
64
+ deprecated_caller = caller(0, 10).find { |c| !c.include?("lib/graphql") }
65
+ if deprecated_caller
66
+ GraphQL::Deprecation.warn " -> called from #{deprecated_caller}"
67
+ end
68
+
62
69
  @context = context
63
70
  @nodes = nodes
64
71
  @arguments = arguments
@@ -74,14 +81,18 @@ module GraphQL
74
81
 
75
82
  def decode(data)
76
83
  @encoder.decode(data, nonce: true)
77
- rescue ArgumentError
78
- raise GraphQL::ExecutionError, "Invalid cursor: #{data.inspect}"
79
84
  end
80
85
 
81
86
  # The value passed as `first:`, if there was one. Negative numbers become `0`.
82
87
  # @return [Integer, nil]
83
88
  def first
84
- @first ||= get_limited_arg(:first)
89
+ @first ||= begin
90
+ capped = limit_pagination_argument(arguments[:first], max_page_size)
91
+ if capped.nil? && last.nil?
92
+ capped = max_page_size
93
+ end
94
+ capped
95
+ end
85
96
  end
86
97
 
87
98
  # The value passed as `after:`, if there was one
@@ -93,7 +104,7 @@ module GraphQL
93
104
  # The value passed as `last:`, if there was one. Negative numbers become `0`.
94
105
  # @return [Integer, nil]
95
106
  def last
96
- @last ||= get_limited_arg(:last)
107
+ @last ||= limit_pagination_argument(arguments[:last], max_page_size)
97
108
  end
98
109
 
99
110
  # The value passed as `before:`, if there was one
@@ -152,16 +163,18 @@ module GraphQL
152
163
 
153
164
  private
154
165
 
155
- # Return a sanitized `arguments[arg_name]` (don't allow negatives)
156
- def get_limited_arg(arg_name)
157
- arg_value = arguments[arg_name]
158
- if arg_value.nil?
159
- arg_value
160
- elsif arg_value < 0
161
- 0
162
- else
163
- arg_value
166
+ # @param argument [nil, Integer] `first` or `last`, as provided by the client
167
+ # @param max_page_size [nil, Integer]
168
+ # @return [nil, Integer] `nil` if the input was `nil`, otherwise a value between `0` and `max_page_size`
169
+ def limit_pagination_argument(argument, max_page_size)
170
+ if argument
171
+ if argument < 0
172
+ argument = 0
173
+ elsif max_page_size && argument > max_page_size
174
+ argument = max_page_size
175
+ end
164
176
  end
177
+ argument
165
178
  end
166
179
 
167
180
  def paged_nodes
@@ -10,10 +10,10 @@ module GraphQL
10
10
  def self.default_arguments
11
11
  @default_arguments ||= begin
12
12
  argument_definitions = [
13
- ["first", GraphQL::INT_TYPE, "Returns the first _n_ elements from the list."],
14
- ["after", GraphQL::STRING_TYPE, "Returns the elements in the list that come after the specified cursor."],
15
- ["last", GraphQL::INT_TYPE, "Returns the last _n_ elements from the list."],
16
- ["before", GraphQL::STRING_TYPE, "Returns the elements in the list that come before the specified cursor."],
13
+ ["first", GraphQL::DEPRECATED_INT_TYPE, "Returns the first _n_ elements from the list."],
14
+ ["after", GraphQL::DEPRECATED_STRING_TYPE, "Returns the elements in the list that come after the specified cursor."],
15
+ ["last", GraphQL::DEPRECATED_INT_TYPE, "Returns the last _n_ elements from the list."],
16
+ ["before", GraphQL::DEPRECATED_STRING_TYPE, "Returns the elements in the list that come before the specified cursor."],
17
17
  ]
18
18
 
19
19
  argument_definitions.reduce({}) do |memo, arg_defn|
@@ -20,7 +20,7 @@ module GraphQL
20
20
  # Any call that would trigger `wrapped_type.ensure_defined`
21
21
  # must be inside this lazy block, otherwise we get weird
22
22
  # cyclical dependency errors :S
23
- ObjectType.define do
23
+ ObjectType.deprecated_define do
24
24
  type_name = wrapped_type.is_a?(GraphQL::BaseType) ? wrapped_type.name : wrapped_type.graphql_name
25
25
  edge_type ||= wrapped_type.edge_type
26
26
  name("#{type_name}Connection")
@@ -16,7 +16,6 @@ module GraphQL
16
16
  end
17
17
  end
18
18
 
19
-
20
19
  class EdgesResolve
21
20
  def initialize(edge_class:, resolve:)
22
21
  @edge_class = edge_class
@@ -30,6 +30,7 @@ module GraphQL
30
30
  alias :input_fields :arguments
31
31
 
32
32
  def initialize
33
+ GraphQL::Deprecation.warn "GraphQL::Relay::Mutation will be removed from GraphQL-Ruby 2.0, use GraphQL::Schema::RelayClassicMutation instead: https://graphql-ruby.org/mutations/mutation_classes"
33
34
  @fields = {}
34
35
  @arguments = {}
35
36
  @has_generated_return_type = false
@@ -5,6 +5,7 @@ module GraphQL
5
5
  module Node
6
6
  # @return [GraphQL::Field] a field for finding objects by their global ID.
7
7
  def self.field(**kwargs, &block)
8
+ GraphQL::Deprecation.warn "GraphQL::Relay::Node.field will be removed from GraphQL-Ruby 2.0, use GraphQL::Types::Relay::NodeField instead"
8
9
  # We have to define it fresh each time because
9
10
  # its name will be modified and its description
10
11
  # _may_ be modified.
@@ -18,6 +19,7 @@ module GraphQL
18
19
  end
19
20
 
20
21
  def self.plural_field(**kwargs, &block)
22
+ GraphQL::Deprecation.warn "GraphQL::Relay::Nodes.field will be removed from GraphQL-Ruby 2.0, use GraphQL::Types::Relay::NodesField instead"
21
23
  field = GraphQL::Types::Relay::NodesField.graphql_definition
22
24
 
23
25
  if kwargs.any? || block
@@ -29,6 +31,7 @@ module GraphQL
29
31
 
30
32
  # @return [GraphQL::InterfaceType] The interface which all Relay types must implement
31
33
  def self.interface
34
+ GraphQL::Deprecation.warn "GraphQL::Relay::Node.interface will be removed from GraphQL-Ruby 2.0, use GraphQL::Types::Relay::Node instead"
32
35
  @interface ||= GraphQL::Types::Relay::Node.graphql_definition
33
36
  end
34
37
  end
@@ -9,7 +9,7 @@ module GraphQL
9
9
  # should be ordered and paginated before providing it here.
10
10
  #
11
11
  # @example Adding a comment to list of comments
12
- # post = Post.find(args[:postId])
12
+ # post = Post.find(args[:post_id])
13
13
  # comments = post.comments
14
14
  # new_comment = comments.build(body: args[:body])
15
15
  # new_comment.save!
@@ -18,13 +18,13 @@ module GraphQL
18
18
  # parent: post,
19
19
  # collection: comments,
20
20
  # item: new_comment,
21
- # context: ctx,
21
+ # context: context,
22
22
  # )
23
23
  #
24
24
  # response = {
25
25
  # post: post,
26
- # commentsConnection: range_add.connection,
27
- # newCommentEdge: range_add.edge,
26
+ # comments_connection: range_add.connection,
27
+ # new_comment_edge: range_add.edge,
28
28
  # }
29
29
  class RangeAdd
30
30
  attr_reader :edge, :connection, :parent
@@ -33,12 +33,26 @@ module GraphQL
33
33
  # @param item [Object] The newly-added item (will be wrapped in `edge_class`)
34
34
  # @param parent [Object] The owner of `collection`, will be passed to the connection if provided
35
35
  # @param context [GraphQL::Query::Context] The surrounding `ctx`, will be passed to the connection if provided (this is required for cursor encoders)
36
- # @param edge_class [Class] The class to wrap `item` with
37
- def initialize(collection:, item:, parent: nil, context: nil, edge_class: Relay::Edge)
38
- connection_class = BaseConnection.connection_for_nodes(collection)
36
+ # @param edge_class [Class] The class to wrap `item` with (defaults to the connection's edge class)
37
+ def initialize(collection:, item:, parent: nil, context: nil, edge_class: nil)
38
+ if context && context.schema.new_connections?
39
+ conn_class = context.schema.connections.wrapper_for(collection)
40
+ # The rest will be added by ConnectionExtension
41
+ @connection = conn_class.new(collection, parent: parent, context: context, edge_class: edge_class)
42
+ # Check if this connection supports it, to support old versions of GraphQL-Pro
43
+ @edge = if @connection.respond_to?(:range_add_edge)
44
+ @connection.range_add_edge(item)
45
+ else
46
+ @connection.edge_class.new(item, @connection)
47
+ end
48
+ else
49
+ connection_class = BaseConnection.connection_for_nodes(collection)
50
+ @connection = connection_class.new(collection, {}, parent: parent, context: context)
51
+ edge_class ||= Relay::Edge
52
+ @edge = edge_class.new(item, @connection)
53
+ end
54
+
39
55
  @parent = parent
40
- @connection = connection_class.new(collection, {}, parent: parent, context: context)
41
- @edge = edge_class.new(item, @connection)
42
56
  end
43
57
  end
44
58
  end
@@ -50,19 +50,17 @@ module GraphQL
50
50
  end
51
51
 
52
52
  def first
53
- return @first if defined? @first
54
-
55
- @first = get_limited_arg(:first)
56
- @first = max_page_size if @first && max_page_size && @first > max_page_size
57
- @first
53
+ @first ||= begin
54
+ capped = limit_pagination_argument(arguments[:first], max_page_size)
55
+ if capped.nil? && last.nil?
56
+ capped = max_page_size
57
+ end
58
+ capped
59
+ end
58
60
  end
59
61
 
60
62
  def last
61
- return @last if defined? @last
62
-
63
- @last = get_limited_arg(:last)
64
- @last = max_page_size if @last && max_page_size && @last > max_page_size
65
- @last
63
+ @last ||= limit_pagination_argument(arguments[:last], max_page_size)
66
64
  end
67
65
 
68
66
  private
@@ -12,6 +12,7 @@ module GraphQL
12
12
  # Define a custom connection type for this object type
13
13
  # @return [GraphQL::ObjectType]
14
14
  def define_connection(**kwargs, &block)
15
+ GraphQL::Deprecation.warn ".connection_type and .define_connection will be removed from GraphQL-Ruby 2.0, use class-based type definitions instead: https://graphql-ruby.org/schema/class_based_api.html"
15
16
  GraphQL::Relay::ConnectionType.create_type(self, **kwargs, &block)
16
17
  end
17
18
 
@@ -23,6 +24,7 @@ module GraphQL
23
24
  # Define a custom edge type for this object type
24
25
  # @return [GraphQL::ObjectType]
25
26
  def define_edge(**kwargs, &block)
27
+ GraphQL::Deprecation.warn ".edge_type and .define_edge will be removed from GraphQL-Ruby 2.0, use class-based type definitions instead: https://graphql-ruby.org/schema/class_based_api.html"
26
28
  GraphQL::Relay::EdgeType.create_type(self, **kwargs, &block)
27
29
  end
28
30
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+ require "rubocop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ class BaseCop < RuboCop::Cop::Base
8
+ extend RuboCop::Cop::AutoCorrector
9
+
10
+ # Return the source of `send_node`, but without the keyword argument represented by `pair_node`
11
+ def source_without_keyword_argument(send_node, pair_node)
12
+ # work back to the preceeding comma
13
+ first_pos = pair_node.location.expression.begin_pos
14
+ end_pos = pair_node.location.expression.end_pos
15
+ node_source = send_node.source_range.source
16
+ node_first_pos = send_node.location.expression.begin_pos
17
+
18
+ relative_first_pos = first_pos - node_first_pos
19
+ relative_last_pos = end_pos - node_first_pos
20
+
21
+ begin_removal_pos = relative_first_pos
22
+ while node_source[begin_removal_pos] != ","
23
+ begin_removal_pos -= 1
24
+ if begin_removal_pos < 1
25
+ raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})"
26
+ end
27
+ end
28
+
29
+ end_removal_pos = relative_last_pos
30
+ cleaned_node_source = node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1]
31
+ cleaned_node_source
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require_relative "base_cop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ # Identify (and auto-correct) any field configuration which duplicates
8
+ # the default `null: true` property.
9
+ #
10
+ # `null: true` is default because nullable fields can always be converted
11
+ # to non-null fields (`null: false`) without a breaking change. (The opposite change, from `null: false`
12
+ # to `null: true`, change.)
13
+ #
14
+ # @example
15
+ # # Both of these define `name: String` in GraphQL:
16
+ #
17
+ # # bad
18
+ # field :name, String, null: true
19
+ #
20
+ # # good
21
+ # field :name, String
22
+ #
23
+ class DefaultNullTrue < BaseCop
24
+ MSG = "`null: true` is the default and can be removed."
25
+
26
+ def_node_matcher :field_config_with_null_true?, <<-Pattern
27
+ (
28
+ send nil? :field ... (hash $(pair (sym :null) (true)) ...)
29
+ )
30
+ Pattern
31
+
32
+ def on_send(node)
33
+ field_config_with_null_true?(node) do |null_config|
34
+ add_offense(null_config) do |corrector|
35
+ cleaned_node_source = source_without_keyword_argument(node, null_config)
36
+ corrector.replace(node.source_range, cleaned_node_source)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require_relative "./base_cop"
3
+
4
+ module GraphQL
5
+ module Rubocop
6
+ module GraphQL
7
+ # Identify (and auto-correct) any argument configuration which duplicates
8
+ # the default `required: true` property.
9
+ #
10
+ # `required: true` is default because required arguments can always be converted
11
+ # to optional arguments (`required: false`) without a breaking change. (The opposite change, from `required: false`
12
+ # to `required: true`, change.)
13
+ #
14
+ # @example
15
+ # # Both of these define `id: ID!` in GraphQL:
16
+ #
17
+ # # bad
18
+ # argument :id, ID, required: true
19
+ #
20
+ # # good
21
+ # argument :id, ID
22
+ #
23
+ class DefaultRequiredTrue < BaseCop
24
+ MSG = "`required: true` is the default and can be removed."
25
+
26
+ def_node_matcher :argument_config_with_required_true?, <<-Pattern
27
+ (
28
+ send nil? :argument ... (hash <$(pair (sym :required) (true)) ...>)
29
+ )
30
+ Pattern
31
+
32
+ def on_send(node)
33
+ argument_config_with_required_true?(node) do |required_config|
34
+ add_offense(required_config) do |corrector|
35
+ cleaned_node_source = source_without_keyword_argument(node, required_config)
36
+ corrector.replace(node, cleaned_node_source)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "graphql/rubocop/graphql/default_null_true"
4
+ require "graphql/rubocop/graphql/default_required_true"
@@ -2,6 +2,8 @@
2
2
  module GraphQL
3
3
  # @api deprecated
4
4
  class ScalarType < GraphQL::BaseType
5
+ extend Define::InstanceDefinable::DeprecatedDefine
6
+
5
7
  accepts_definitions :coerce, :coerce_input, :coerce_result
6
8
  ensure_defined :coerce_non_null_input, :coerce_result
7
9
 
@@ -67,8 +69,21 @@ module GraphQL
67
69
 
68
70
  def validate_non_null_input(value, ctx)
69
71
  result = Query::InputValidationResult.new
70
- if value.is_a?(GraphQL::Language::Nodes::Enum) || coerce_non_null_input(value, ctx).nil?
72
+
73
+ coerced_result = begin
74
+ coerce_non_null_input(value, ctx)
75
+ rescue GraphQL::CoercionError => err
76
+ err
77
+ end
78
+
79
+ if value.is_a?(GraphQL::Language::Nodes::Enum) || coerced_result.nil?
71
80
  result.add_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{name}")
81
+ elsif coerced_result.is_a?(GraphQL::CoercionError)
82
+ result.add_problem(
83
+ coerced_result.message,
84
+ message: coerced_result.message,
85
+ extensions: coerced_result.extensions
86
+ )
72
87
  end
73
88
  result
74
89
  end