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
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Language
4
+ # A custom printer used to print sanitized queries. It inlines provided variables
5
+ # within the query for facilitate logging and analysis of queries.
6
+ #
7
+ # The printer returns `nil` if the query is invalid.
8
+ #
9
+ # Since the GraphQL Ruby AST for a GraphQL query doesnt contain any reference
10
+ # on the type of fields or arguments, we have to track the current object, field
11
+ # and input type while printing the query.
12
+ #
13
+ # @example Printing a scrubbed string
14
+ # printer = QueryPrinter.new(query)
15
+ # puts printer.sanitized_query_string
16
+ #
17
+ # @see {Query#sanitized_query_string}
18
+ class SanitizedPrinter < GraphQL::Language::Printer
19
+
20
+ REDACTED = "\"<REDACTED>\""
21
+
22
+ def initialize(query, inline_variables: true)
23
+ @query = query
24
+ @current_type = nil
25
+ @current_field = nil
26
+ @current_input_type = nil
27
+ @inline_variables = inline_variables
28
+ end
29
+
30
+ # @return [String, nil] A scrubbed query string, if the query was valid.
31
+ def sanitized_query_string
32
+ if query.valid?
33
+ print(query.document)
34
+ else
35
+ nil
36
+ end
37
+ end
38
+
39
+ def print_node(node, indent: "")
40
+ case node
41
+ when FalseClass, Float, Integer, String, TrueClass
42
+ if @current_argument && redact_argument_value?(@current_argument, node)
43
+ redacted_argument_value(@current_argument)
44
+ else
45
+ super
46
+ end
47
+ when Array
48
+ old_input_type = @current_input_type
49
+ if @current_input_type && @current_input_type.list?
50
+ @current_input_type = @current_input_type.of_type
51
+ @current_input_type = @current_input_type.of_type if @current_input_type.non_null?
52
+ end
53
+
54
+ res = super
55
+ @current_input_type = old_input_type
56
+ res
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ # Indicates whether or not to redact non-null values for the given argument. Defaults to redacting all strings
63
+ # arguments but this can be customized by subclasses.
64
+ def redact_argument_value?(argument, value)
65
+ # Default to redacting any strings or custom scalars encoded as strings
66
+ type = argument.type.unwrap
67
+ value.is_a?(String) && type.kind.scalar? && (type.graphql_name == "String" || !type.default_scalar?)
68
+ end
69
+
70
+ # Returns the value to use for redacted versions of the given argument. Defaults to the
71
+ # string "<REDACTED>".
72
+ def redacted_argument_value(argument)
73
+ REDACTED
74
+ end
75
+
76
+ def print_argument(argument)
77
+ # We won't have type information if we're recursing into a custom scalar
78
+ return super if @current_input_type && @current_input_type.kind.scalar?
79
+
80
+ arg_owner = @current_input_type || @current_directive || @current_field
81
+ old_current_argument = @current_argument
82
+ @current_argument = arg_owner.get_argument(argument.name, @query.context)
83
+
84
+ old_input_type = @current_input_type
85
+ @current_input_type = @current_argument.type.non_null? ? @current_argument.type.of_type : @current_argument.type
86
+
87
+ argument_value = if coerce_argument_value_to_list?(@current_input_type, argument.value)
88
+ [argument.value]
89
+ else
90
+ argument.value
91
+ end
92
+ res = "#{argument.name}: #{print_node(argument_value)}".dup
93
+
94
+ @current_input_type = old_input_type
95
+ @current_argument = old_current_argument
96
+ res
97
+ end
98
+
99
+ def coerce_argument_value_to_list?(type, value)
100
+ type.list? &&
101
+ !value.is_a?(Array) &&
102
+ !value.nil? &&
103
+ !value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
104
+ end
105
+
106
+ def print_variable_identifier(variable_id)
107
+ if @inline_variables
108
+ variable_value = query.variables[variable_id.name]
109
+ print_node(value_to_ast(variable_value, @current_input_type))
110
+ else
111
+ super
112
+ end
113
+ end
114
+
115
+ def print_field(field, indent: "")
116
+ @current_field = query.get_field(@current_type, field.name)
117
+ old_type = @current_type
118
+ @current_type = @current_field.type.unwrap
119
+ res = super
120
+ @current_type = old_type
121
+ res
122
+ end
123
+
124
+ def print_inline_fragment(inline_fragment, indent: "")
125
+ old_type = @current_type
126
+
127
+ if inline_fragment.type
128
+ @current_type = query.get_type(inline_fragment.type.name)
129
+ end
130
+
131
+ res = super
132
+
133
+ @current_type = old_type
134
+
135
+ res
136
+ end
137
+
138
+ def print_fragment_definition(fragment_def, indent: "")
139
+ old_type = @current_type
140
+ @current_type = query.get_type(fragment_def.type.name)
141
+
142
+ res = super
143
+
144
+ @current_type = old_type
145
+
146
+ res
147
+ end
148
+
149
+ def print_directive(directive)
150
+ @current_directive = query.schema.directives[directive.name]
151
+
152
+ res = super
153
+
154
+ @current_directive = nil
155
+ res
156
+ end
157
+
158
+ # Print the operation definition but do not include the variable
159
+ # definitions since we will inline them within the query
160
+ def print_operation_definition(operation_definition, indent: "")
161
+ old_type = @current_type
162
+ @current_type = query.schema.public_send(operation_definition.operation_type)
163
+
164
+ if @inline_variables
165
+ out = "#{indent}#{operation_definition.operation_type}".dup
166
+ out << " #{operation_definition.name}" if operation_definition.name
167
+ out << print_directives(operation_definition.directives)
168
+ out << print_selections(operation_definition.selections, indent: indent)
169
+ else
170
+ out = super
171
+ end
172
+
173
+ @current_type = old_type
174
+ out
175
+ end
176
+
177
+ private
178
+
179
+ def value_to_ast(value, type)
180
+ type = type.of_type if type.non_null?
181
+
182
+ if value.nil?
183
+ return GraphQL::Language::Nodes::NullValue.new(name: "null")
184
+ end
185
+
186
+ case type.kind.name
187
+ when "INPUT_OBJECT"
188
+ value = if value.respond_to?(:to_unsafe_h)
189
+ # for ActionController::Parameters
190
+ value.to_unsafe_h
191
+ else
192
+ value.to_h
193
+ end
194
+
195
+ arguments = value.map do |key, val|
196
+ sub_type = type.get_argument(key.to_s, @query.context).type
197
+
198
+ GraphQL::Language::Nodes::Argument.new(
199
+ name: key.to_s,
200
+ value: value_to_ast(val, sub_type)
201
+ )
202
+ end
203
+ GraphQL::Language::Nodes::InputObject.new(
204
+ arguments: arguments
205
+ )
206
+ when "LIST"
207
+ if value.is_a?(Array)
208
+ value.map { |v| value_to_ast(v, type.of_type) }
209
+ else
210
+ [value].map { |v| value_to_ast(v, type.of_type) }
211
+ end
212
+ when "ENUM"
213
+ GraphQL::Language::Nodes::Enum.new(name: value)
214
+ else
215
+ value
216
+ end
217
+ end
218
+
219
+ attr_reader :query
220
+ end
221
+ end
222
+ end
@@ -4,10 +4,6 @@ module GraphQL
4
4
  # Emitted by the lexer and passed to the parser.
5
5
  # Contains type, value and position data.
6
6
  class Token
7
- if !String.method_defined?(:-@)
8
- using GraphQL::StringDedupBackport
9
- end
10
-
11
7
  # @return [Symbol] The kind of token this is
12
8
  attr_reader :name
13
9
  # @return [String] The text of this token
@@ -89,7 +89,7 @@ module GraphQL
89
89
  # @param parent [GraphQL::Language::Nodes::AbstractNode, nil] the previously-visited node, or `nil` if this is the root node.
90
90
  # @return [Array, nil] If there were modifications, it returns an array of new nodes, otherwise, it returns `nil`.
91
91
  def on_abstract_node(node, parent)
92
- if node == DELETE_NODE
92
+ if node.equal?(DELETE_NODE)
93
93
  # This might be passed to `super(DELETE_NODE, ...)`
94
94
  # by a user hook, don't want to keep visiting in that case.
95
95
  nil
@@ -179,7 +179,7 @@ module GraphQL
179
179
  # The user-provided hook returned a new node.
180
180
  new_parent = new_parent && new_parent.replace_child(node, new_node)
181
181
  return new_node, new_parent
182
- elsif new_node == DELETE_NODE
182
+ elsif new_node.equal?(DELETE_NODE)
183
183
  # The user-provided hook requested to remove this node
184
184
  new_parent = new_parent && new_parent.delete_child(node)
185
185
  return nil, new_parent
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/language/block_string"
3
3
  require "graphql/language/printer"
4
+ require "graphql/language/sanitized_printer"
4
5
  require "graphql/language/document_from_schema_definition"
5
6
  require "graphql/language/generation"
6
7
  require "graphql/language/lexer"
7
8
  require "graphql/language/nodes"
9
+ require "graphql/language/cache"
8
10
  require "graphql/language/parser"
9
11
  require "graphql/language/token"
10
12
  require "graphql/language/visitor"
@@ -4,13 +4,8 @@ module GraphQL
4
4
  VALID_NAME_REGEX = /^[_a-zA-Z][_a-zA-Z0-9]*$/
5
5
 
6
6
  def self.validate!(name)
7
- raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless valid?(name)
8
- end
9
-
10
- private
11
-
12
- def self.valid?(name)
13
- name =~ VALID_NAME_REGEX
7
+ name = name.is_a?(String) ? name : name.to_s
8
+ raise GraphQL::InvalidNameError.new(name, VALID_NAME_REGEX) unless name.match?(VALID_NAME_REGEX)
14
9
  end
15
10
  end
16
11
  end
@@ -2,6 +2,8 @@
2
2
  module GraphQL
3
3
  # @api deprecated
4
4
  class ObjectType < GraphQL::BaseType
5
+ extend Define::InstanceDefinable::DeprecatedDefine
6
+
5
7
  accepts_definitions :interfaces, :fields, :mutation, :relay_node_type, field: GraphQL::Define::AssignObjectField
6
8
  accepts_definitions implements: ->(type, *interfaces, inherit: false) { type.implements(interfaces, inherit: inherit) }
7
9
 
@@ -17,17 +19,15 @@ module GraphQL
17
19
  def initialize
18
20
  super
19
21
  @fields = {}
20
- @interface_fields = {}
21
- @dirty_interfaces = []
22
- @dirty_inherited_interfaces = []
22
+ @clean_inherited_fields = nil
23
+ @structural_interface_type_memberships = []
24
+ @inherited_interface_type_memberships = []
23
25
  end
24
26
 
25
27
  def initialize_copy(other)
26
28
  super
27
- @clean_interfaces = nil
28
- @clean_inherited_interfaces = nil
29
- @dirty_interfaces = other.dirty_interfaces.dup
30
- @dirty_inherited_interfaces = other.dirty_inherited_interfaces.dup
29
+ @structural_interface_type_memberships = other.structural_interface_type_memberships.dup
30
+ @inherited_interface_type_memberships = other.inherited_interface_type_memberships.dup
31
31
  @fields = other.fields.dup
32
32
  end
33
33
 
@@ -35,18 +35,27 @@ module GraphQL
35
35
  # @param new_interfaces [Array<GraphQL::Interface>] interfaces that this type implements
36
36
  # @deprecated Use `implements` instead of `interfaces`.
37
37
  def interfaces=(new_interfaces)
38
- @clean_interfaces = nil
39
- @clean_inherited_interfaces = nil
38
+ @structural_interface_type_memberships = []
39
+ @inherited_interface_type_memberships = []
40
40
  @clean_inherited_fields = nil
41
-
42
- @dirty_inherited_interfaces = []
43
- @dirty_inherited_fields = {}
44
41
  implements(new_interfaces, inherit: true)
45
42
  end
46
43
 
47
- def interfaces
48
- load_interfaces
49
- @clean_interfaces
44
+ def interfaces(ctx = GraphQL::Query::NullContext)
45
+ ensure_defined
46
+ visible_ifaces = []
47
+ unfiltered = ctx == GraphQL::Query::NullContext
48
+ [@structural_interface_type_memberships, @inherited_interface_type_memberships].each do |tms|
49
+ tms.each do |type_membership|
50
+ if unfiltered || type_membership.visible?(ctx)
51
+ # if this is derived from a class-based object, we have to
52
+ # get the `.graphql_definition` of the attached interface.
53
+ visible_ifaces << GraphQL::BaseType.resolve_related_type(type_membership.abstract_type)
54
+ end
55
+ end
56
+ end
57
+
58
+ visible_ifaces
50
59
  end
51
60
 
52
61
  def kind
@@ -71,24 +80,30 @@ module GraphQL
71
80
  # This declaration will be validated when the schema is defined.
72
81
  # @param interfaces [Array<GraphQL::Interface>] add a new interface that this type implements
73
82
  # @param inherits [Boolean] If true, copy the interfaces' field definitions to this type
74
- def implements(interfaces, inherit: false)
83
+ def implements(interfaces, inherit: false, **options)
75
84
  if !interfaces.is_a?(Array)
76
85
  raise ArgumentError, "`implements(interfaces)` must be an array, not #{interfaces.class} (#{interfaces})"
77
86
  end
78
-
79
- @clean_interfaces = nil
80
87
  @clean_inherited_fields = nil
81
- dirty_ifaces = inherit ? @dirty_inherited_interfaces : @dirty_interfaces
82
- dirty_ifaces.concat(interfaces)
88
+
89
+ type_memberships = inherit ? @inherited_interface_type_memberships : @structural_interface_type_memberships
90
+ interfaces.each do |iface|
91
+ iface = BaseType.resolve_related_type(iface)
92
+ if iface.is_a?(GraphQL::InterfaceType)
93
+ type_memberships << iface.type_membership_class.new(iface, self, **options)
94
+ end
95
+ end
83
96
  end
84
97
 
85
98
  def resolve_type_proc
86
99
  nil
87
100
  end
88
101
 
102
+ attr_writer :structural_interface_type_memberships
103
+
89
104
  protected
90
105
 
91
- attr_reader :dirty_interfaces, :dirty_inherited_interfaces
106
+ attr_reader :structural_interface_type_memberships, :inherited_interface_type_memberships
92
107
 
93
108
  private
94
109
 
@@ -97,24 +112,18 @@ module GraphQL
97
112
  end
98
113
 
99
114
  def interface_fields
100
- load_interfaces
101
- @clean_inherited_fields
102
- end
103
-
104
- def load_interfaces
105
- @clean_interfaces ||= begin
115
+ if @clean_inherited_fields
116
+ @clean_inherited_fields
117
+ else
106
118
  ensure_defined
107
- clean_ifaces = normalize_interfaces(@dirty_interfaces)
108
- clean_inherited_ifaces = normalize_interfaces(@dirty_inherited_interfaces)
109
- inherited_fields = {}
110
- clean_inherited_ifaces.each do |iface|
111
- # This will be found later in schema validation:
119
+ @clean_inherited_fields = {}
120
+ @inherited_interface_type_memberships.each do |type_membership|
121
+ iface = GraphQL::BaseType.resolve_related_type(type_membership.abstract_type)
112
122
  if iface.is_a?(GraphQL::InterfaceType)
113
- inherited_fields.merge!(iface.fields)
123
+ @clean_inherited_fields.merge!(iface.fields)
114
124
  end
115
125
  end
116
- @clean_inherited_fields = inherited_fields
117
- clean_inherited_ifaces + clean_ifaces
126
+ @clean_inherited_fields
118
127
  end
119
128
  end
120
129
  end
@@ -5,13 +5,26 @@ module GraphQL
5
5
  module Pagination
6
6
  # Customizes `RelationConnection` to work with `ActiveRecord::Relation`s.
7
7
  class ActiveRecordRelationConnection < Pagination::RelationConnection
8
+ private
9
+
10
+ def relation_larger_than(relation, size)
11
+ initial_offset = relation.offset_value || 0
12
+ relation.offset(initial_offset + size).exists?
13
+ end
14
+
8
15
  def relation_count(relation)
9
- if relation.respond_to?(:unscope)
16
+ int_or_hash = if relation.respond_to?(:unscope)
10
17
  relation.unscope(:order).count(:all)
11
18
  else
12
19
  # Rails 3
13
20
  relation.count
14
21
  end
22
+ if int_or_hash.is_a?(Integer)
23
+ int_or_hash
24
+ else
25
+ # Grouped relations return count-by-group hashes
26
+ int_or_hash.length
27
+ end
15
28
  end
16
29
 
17
30
  def relation_limit(relation)
@@ -21,7 +21,7 @@ module GraphQL
21
21
 
22
22
  def cursor_for(item)
23
23
  idx = items.find_index(item) + 1
24
- context.schema.cursor_encoder.encode(idx.to_s)
24
+ encode(idx.to_s)
25
25
  end
26
26
 
27
27
  private
@@ -59,7 +59,7 @@ module GraphQL
59
59
  sliced_nodes.count > first
60
60
  elsif before
61
61
  # The original array is longer than the `before` index
62
- index_from_cursor(before) < items.length
62
+ index_from_cursor(before) < items.length + 1
63
63
  else
64
64
  false
65
65
  end
@@ -15,50 +15,84 @@ module GraphQL
15
15
  class PaginationImplementationMissingError < GraphQL::Error
16
16
  end
17
17
 
18
- # @return [Class] The class to use for wrapping items as `edges { ... }`. Defaults to `Connection::Edge`
19
- def self.edge_class
20
- self::Edge
21
- end
22
-
23
18
  # @return [Object] A list object, from the application. This is the unpaginated value passed into the connection.
24
19
  attr_reader :items
25
20
 
26
21
  # @return [GraphQL::Query::Context]
27
22
  attr_accessor :context
28
23
 
24
+ # @return [Object] the object this collection belongs to
25
+ attr_accessor :parent
26
+
29
27
  # Raw access to client-provided values. (`max_page_size` not applied to first or last.)
30
28
  attr_accessor :before_value, :after_value, :first_value, :last_value
31
29
 
32
- # @return [String, nil] the client-provided cursor
30
+ # @return [String, nil] the client-provided cursor. `""` is treated as `nil`.
33
31
  def before
34
- @before_value
32
+ if defined?(@before)
33
+ @before
34
+ else
35
+ @before = @before_value == "" ? nil : @before_value
36
+ end
35
37
  end
36
38
 
37
- # @return [String, nil] the client-provided cursor
39
+ # @return [String, nil] the client-provided cursor. `""` is treated as `nil`.
38
40
  def after
39
- @after_value
41
+ if defined?(@after)
42
+ @after
43
+ else
44
+ @after = @after_value == "" ? nil : @after_value
45
+ end
40
46
  end
41
47
 
48
+ # @return [Hash<Symbol => Object>] The field arguments from the field that returned this connection
49
+ attr_accessor :arguments
50
+
42
51
  # @param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation`
43
52
  # @param context [Query::Context]
53
+ # @param parent [Object] The object this collection belongs to
44
54
  # @param first [Integer, nil] The limit parameter from the client, if it provided one
45
55
  # @param after [String, nil] A cursor for pagination, if the client provided one
46
56
  # @param last [Integer, nil] Limit parameter from the client, if provided
47
57
  # @param before [String, nil] A cursor for pagination, if the client provided one.
58
+ # @param arguments [Hash] The arguments to the field that returned the collection wrapped by this connection
48
59
  # @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
49
- def initialize(items, context: nil, first: nil, after: nil, max_page_size: nil, last: nil, before: nil)
60
+ def initialize(items, parent: nil, field: nil, context: nil, first: nil, after: nil, max_page_size: :not_given, last: nil, before: nil, edge_class: nil, arguments: nil)
50
61
  @items = items
62
+ @parent = parent
51
63
  @context = context
64
+ @field = field
52
65
  @first_value = first
53
66
  @after_value = after
54
67
  @last_value = last
55
68
  @before_value = before
56
- @max_page_size = max_page_size
69
+ @arguments = arguments
70
+ @edge_class = edge_class || self.class::Edge
71
+ # This is only true if the object was _initialized_ with an override
72
+ # or if one is assigned later.
73
+ @has_max_page_size_override = max_page_size != :not_given
74
+ @max_page_size = if max_page_size == :not_given
75
+ nil
76
+ else
77
+ max_page_size
78
+ end
79
+ end
80
+
81
+ def max_page_size=(new_value)
82
+ @has_max_page_size_override = true
83
+ @max_page_size = new_value
57
84
  end
58
85
 
59
- attr_writer :max_page_size
60
86
  def max_page_size
61
- @max_page_size ||= context.schema.default_max_page_size
87
+ if @has_max_page_size_override
88
+ @max_page_size
89
+ else
90
+ context.schema.default_max_page_size
91
+ end
92
+ end
93
+
94
+ def has_max_page_size_override?
95
+ @has_max_page_size_override
62
96
  end
63
97
 
64
98
  attr_writer :first
@@ -76,6 +110,15 @@ module GraphQL
76
110
  end
77
111
  end
78
112
 
113
+ # This is called by `Relay::RangeAdd` -- it can be overridden
114
+ # when `item` needs some modifications based on this connection's state.
115
+ #
116
+ # @param item [Object] An item newly added to `items`
117
+ # @return [Edge]
118
+ def range_add_edge(item)
119
+ edge_class.new(item, self)
120
+ end
121
+
79
122
  attr_writer :last
80
123
  # @return [Integer, nil] A clamped `last` value. (The underlying instance variable doesn't have limits on it)
81
124
  def last
@@ -84,9 +127,15 @@ module GraphQL
84
127
 
85
128
  # @return [Array<Edge>] {nodes}, but wrapped with Edge instances
86
129
  def edges
87
- @edges ||= nodes.map { |n| self.class.edge_class.new(n, self) }
130
+ @edges ||= nodes.map { |n| @edge_class.new(n, self) }
88
131
  end
89
132
 
133
+ # @return [Class] A wrapper class for edges of this connection
134
+ attr_accessor :edge_class
135
+
136
+ # @return [GraphQL::Schema::Field] The field this connection was returned by
137
+ attr_accessor :field
138
+
90
139
  # @return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
91
140
  def nodes
92
141
  raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
@@ -147,23 +196,29 @@ module GraphQL
147
196
  end
148
197
 
149
198
  def decode(cursor)
150
- context.schema.cursor_encoder.decode(cursor)
199
+ context.schema.cursor_encoder.decode(cursor, nonce: true)
200
+ end
201
+
202
+ def encode(cursor)
203
+ context.schema.cursor_encoder.encode(cursor, nonce: true)
151
204
  end
152
205
 
153
206
  # A wrapper around paginated items. It includes a {cursor} for pagination
154
207
  # and could be extended with custom relationship-level data.
155
208
  class Edge
156
- def initialize(item, connection)
209
+ attr_reader :node
210
+
211
+ def initialize(node, connection)
157
212
  @connection = connection
158
- @item = item
213
+ @node = node
159
214
  end
160
215
 
161
- def node
162
- @item
216
+ def parent
217
+ @connection.parent
163
218
  end
164
219
 
165
220
  def cursor
166
- @connection.cursor_for(@item)
221
+ @cursor ||= @connection.cursor_for(@node)
167
222
  end
168
223
  end
169
224
  end