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
@@ -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