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,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Addition
6
+ attr_reader :directives, :possible_types, :types, :union_memberships, :references, :arguments_with_default_values
7
+
8
+ def initialize(schema:, own_types:, new_types:)
9
+ @schema = schema
10
+ @own_types = own_types
11
+ @directives = Set.new
12
+ @possible_types = {}
13
+ @types = {}
14
+ @union_memberships = {}
15
+ @references = Hash.new { |h, k| h[k] = [] }
16
+ @arguments_with_default_values = []
17
+ add_type_and_traverse(new_types)
18
+ end
19
+
20
+ private
21
+
22
+ def references_to(thing, from:)
23
+ @references[thing] << from
24
+ end
25
+
26
+ def get_type(name)
27
+ local_type = @types[name]
28
+ # This isn't really sophisticated, but
29
+ # I think it's good enough to support the current usage of LateBoundTypes
30
+ if local_type.is_a?(Array)
31
+ local_type = local_type.first
32
+ end
33
+ local_type || @schema.get_type(name)
34
+ end
35
+
36
+ # Lookup using `own_types` here because it's ok to override
37
+ # inherited types by name
38
+ def get_local_type(name)
39
+ @types[name] || @own_types[name]
40
+ end
41
+
42
+ def add_directives_from(owner)
43
+ dirs = owner.directives.map(&:class)
44
+ @directives.merge(dirs)
45
+ add_type_and_traverse(dirs)
46
+ end
47
+
48
+ def add_type_and_traverse(new_types)
49
+ late_types = []
50
+ new_types.each { |t| add_type(t, owner: nil, late_types: late_types, path: [t.graphql_name]) }
51
+ missed_late_types = 0
52
+ while (late_type_vals = late_types.shift)
53
+ type_owner, lt = late_type_vals
54
+ if lt.is_a?(String)
55
+ type = Member::BuildType.constantize(lt)
56
+ # Reset the counter, since we might succeed next go-round
57
+ missed_late_types = 0
58
+ update_type_owner(type_owner, type)
59
+ add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name])
60
+ elsif lt.is_a?(LateBoundType)
61
+ if (type = get_type(lt.name))
62
+ # Reset the counter, since we might succeed next go-round
63
+ missed_late_types = 0
64
+ update_type_owner(type_owner, type)
65
+ add_type(type, owner: type_owner, late_types: late_types, path: [type.graphql_name])
66
+ else
67
+ missed_late_types += 1
68
+ # Add it back to the list, maybe we'll be able to resolve it later.
69
+ late_types << [type_owner, lt]
70
+ if missed_late_types == late_types.size
71
+ # We've looked at all of them and haven't resolved one.
72
+ raise UnresolvedLateBoundTypeError.new(type: lt)
73
+ else
74
+ # Try the next one
75
+ end
76
+ end
77
+ else
78
+ raise ArgumentError, "Unexpected late type: #{lt.inspect}"
79
+ end
80
+ end
81
+ nil
82
+ end
83
+
84
+ def update_type_owner(owner, type)
85
+ case owner
86
+ when Module
87
+ if owner.kind.union?
88
+ # It's a union with possible_types
89
+ # Replace the item by class name
90
+ owner.assign_type_membership_object_type(type)
91
+ @possible_types[owner.graphql_name] = owner.possible_types
92
+ elsif type.kind.interface? && (owner.kind.object? || owner.kind.interface?)
93
+ new_interfaces = []
94
+ owner.interfaces.each do |int_t|
95
+ if int_t.is_a?(String) && int_t == type.graphql_name
96
+ new_interfaces << type
97
+ elsif int_t.is_a?(LateBoundType) && int_t.graphql_name == type.graphql_name
98
+ new_interfaces << type
99
+ else
100
+ # Don't re-add proper interface definitions,
101
+ # they were probably already added, maybe with options.
102
+ end
103
+ end
104
+ owner.implements(*new_interfaces)
105
+ new_interfaces.each do |int|
106
+ pt = @possible_types[int.graphql_name] ||= []
107
+ if !pt.include?(owner) && owner.is_a?(Class)
108
+ pt << owner
109
+ end
110
+ end
111
+ end
112
+ when nil
113
+ # It's a root type
114
+ @types[type.graphql_name] = type
115
+ when GraphQL::Schema::Field, GraphQL::Schema::Argument
116
+ orig_type = owner.type
117
+ # Apply list/non-null wrapper as needed
118
+ if orig_type.respond_to?(:of_type)
119
+ transforms = []
120
+ while (orig_type.respond_to?(:of_type))
121
+ if orig_type.kind.non_null?
122
+ transforms << :to_non_null_type
123
+ elsif orig_type.kind.list?
124
+ transforms << :to_list_type
125
+ else
126
+ raise "Invariant: :of_type isn't non-null or list"
127
+ end
128
+ orig_type = orig_type.of_type
129
+ end
130
+ transforms.reverse_each { |t| type = type.public_send(t) }
131
+ end
132
+ owner.type = type
133
+ else
134
+ raise "Unexpected update: #{owner.inspect} #{type.inspect}"
135
+ end
136
+ end
137
+
138
+ def add_type(type, owner:, late_types:, path:)
139
+ if type.respond_to?(:metadata) && type.metadata.is_a?(Hash)
140
+ type_class = type.metadata[:type_class]
141
+ if type_class.nil?
142
+ raise ArgumentError, "Can't add legacy type: #{type} (#{type.class})"
143
+ else
144
+ type = type_class
145
+ end
146
+ elsif type.is_a?(String) || type.is_a?(GraphQL::Schema::LateBoundType)
147
+ late_types << [owner, type]
148
+ return
149
+ end
150
+
151
+ if owner.is_a?(Class) && owner < GraphQL::Schema::Union
152
+ um = @union_memberships[type.graphql_name] ||= []
153
+ um << owner
154
+ end
155
+
156
+ if (prev_type = get_local_type(type.graphql_name)) && prev_type == type
157
+ # No need to re-visit
158
+ elsif type.is_a?(Class) && type < GraphQL::Schema::Directive
159
+ @directives << type
160
+ type.all_argument_definitions.each do |arg|
161
+ arg_type = arg.type.unwrap
162
+ references_to(arg_type, from: arg)
163
+ add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
164
+ if arg.default_value?
165
+ @arguments_with_default_values << arg
166
+ end
167
+ end
168
+ else
169
+ prev_type = @types[type.graphql_name]
170
+ if prev_type.nil?
171
+ @types[type.graphql_name] = type
172
+ elsif prev_type.is_a?(Array)
173
+ prev_type << type
174
+ else
175
+ @types[type.graphql_name] = [prev_type, type]
176
+ end
177
+
178
+ add_directives_from(type)
179
+ if type.kind.fields?
180
+ type.all_field_definitions.each do |field|
181
+ name = field.graphql_name
182
+ field_type = field.type.unwrap
183
+ references_to(field_type, from: field)
184
+ field_path = path + [name]
185
+ add_type(field_type, owner: field, late_types: late_types, path: field_path)
186
+ add_directives_from(field)
187
+ field.all_argument_definitions.each do |arg|
188
+ add_directives_from(arg)
189
+ arg_type = arg.type.unwrap
190
+ references_to(arg_type, from: arg)
191
+ add_type(arg_type, owner: arg, late_types: late_types, path: field_path + [arg.graphql_name])
192
+ if arg.default_value?
193
+ @arguments_with_default_values << arg
194
+ end
195
+ end
196
+ end
197
+ end
198
+ if type.kind.input_object?
199
+ type.all_argument_definitions.each do |arg|
200
+ add_directives_from(arg)
201
+ arg_type = arg.type.unwrap
202
+ references_to(arg_type, from: arg)
203
+ add_type(arg_type, owner: arg, late_types: late_types, path: path + [arg.graphql_name])
204
+ if arg.default_value?
205
+ @arguments_with_default_values << arg
206
+ end
207
+ end
208
+ end
209
+ if type.kind.union?
210
+ @possible_types[type.graphql_name] = type.all_possible_types
211
+ type.all_possible_types.each do |t|
212
+ add_type(t, owner: type, late_types: late_types, path: path + ["possible_types"])
213
+ end
214
+ end
215
+ if type.kind.interface?
216
+ type.orphan_types.each do |t|
217
+ add_type(t, owner: type, late_types: late_types, path: path + ["orphan_types"])
218
+ end
219
+ end
220
+ if type.kind.object?
221
+ possible_types_for_this_name = @possible_types[type.graphql_name] ||= []
222
+ possible_types_for_this_name << type
223
+ end
224
+
225
+ if type.kind.object? || type.kind.interface?
226
+ type.interface_type_memberships.each do |interface_type_membership|
227
+ case interface_type_membership
228
+ when Schema::TypeMembership
229
+ interface_type = interface_type_membership.abstract_type
230
+ # We can get these now; we'll have to get late-bound types later
231
+ if interface_type.is_a?(Module) && type.is_a?(Class)
232
+ implementers = @possible_types[interface_type.graphql_name] ||= []
233
+ implementers << type
234
+ end
235
+ when String, Schema::LateBoundType
236
+ interface_type = interface_type_membership
237
+ else
238
+ raise ArgumentError, "Invariant: unexpected type membership for #{type.graphql_name}: #{interface_type_membership.class} (#{interface_type_membership.inspect})"
239
+ end
240
+ add_type(interface_type, owner: type, late_types: late_types, path: path + ["implements"])
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
@@ -2,14 +2,14 @@
2
2
  module GraphQL
3
3
  class Schema
4
4
  class Argument
5
- if !String.method_defined?(:-@)
6
- using GraphQL::StringDedupBackport
7
- end
8
-
9
5
  include GraphQL::Schema::Member::CachedGraphQLDefinition
10
6
  include GraphQL::Schema::Member::AcceptsDefinition
11
7
  include GraphQL::Schema::Member::HasPath
12
8
  include GraphQL::Schema::Member::HasAstNode
9
+ include GraphQL::Schema::Member::HasDirectives
10
+ include GraphQL::Schema::Member::HasDeprecationReason
11
+ include GraphQL::Schema::Member::HasValidators
12
+ include GraphQL::Schema::FindInheritedValue::EmptyObjects
13
13
 
14
14
  NO_DEFAULT = :__no_default__
15
15
 
@@ -45,7 +45,10 @@ module GraphQL
45
45
  # @param camelize [Boolean] if true, the name will be camelized when building the schema
46
46
  # @param from_resolver [Boolean] if true, a Resolver class defined this argument
47
47
  # @param method_access [Boolean] If false, don't build method access on legacy {Query::Arguments} instances.
48
- def initialize(arg_name = nil, type_expr = nil, desc = nil, required:, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, &definition_block)
48
+ # @param directives [Hash{Class => Hash}]
49
+ # @param deprecation_reason [String]
50
+ # @param validates [Hash, nil] Options for building validators, if any should be applied
51
+ def initialize(arg_name = nil, type_expr = nil, desc = nil, required: true, type: nil, name: nil, loads: nil, description: nil, ast_node: nil, default_value: NO_DEFAULT, as: nil, from_resolver: false, camelize: true, prepare: nil, method_access: true, owner:, validates: nil, directives: nil, deprecation_reason: nil, &definition_block)
49
52
  arg_name ||= name
50
53
  @name = -(camelize ? Member::BuildType.camelize(arg_name.to_s) : arg_name.to_s)
51
54
  @type_expr = type_expr || type
@@ -55,11 +58,20 @@ module GraphQL
55
58
  @owner = owner
56
59
  @as = as
57
60
  @loads = loads
58
- @keyword = as || Schema::Member::BuildType.underscore(@name).to_sym
61
+ @keyword = as || (arg_name.is_a?(Symbol) ? arg_name : Schema::Member::BuildType.underscore(@name).to_sym)
59
62
  @prepare = prepare
60
63
  @ast_node = ast_node
61
64
  @from_resolver = from_resolver
62
65
  @method_access = method_access
66
+ self.deprecation_reason = deprecation_reason
67
+
68
+ if directives
69
+ directives.each do |dir_class, dir_options|
70
+ directive(dir_class, **dir_options)
71
+ end
72
+ end
73
+
74
+ self.validates(validates)
63
75
 
64
76
  if definition_block
65
77
  if definition_block.arity == 1
@@ -70,6 +82,10 @@ module GraphQL
70
82
  end
71
83
  end
72
84
 
85
+ def inspect
86
+ "#<#{self.class} #{path}: #{type.to_type_signature}#{description ? " @description=#{description.inspect}" : ""}>"
87
+ end
88
+
73
89
  # @return [Object] the value used when the client doesn't provide a value for this argument
74
90
  attr_reader :default_value
75
91
 
@@ -89,6 +105,20 @@ module GraphQL
89
105
  end
90
106
  end
91
107
 
108
+ # @return [String] Deprecation reason for this argument
109
+ def deprecation_reason(text = nil)
110
+ if text
111
+ self.deprecation_reason = text
112
+ else
113
+ super()
114
+ end
115
+ end
116
+
117
+ def deprecation_reason=(new_reason)
118
+ validate_deprecated_or_optional(null: @null, deprecation_reason: new_reason)
119
+ super
120
+ end
121
+
92
122
  def visible?(context)
93
123
  true
94
124
  end
@@ -117,11 +147,11 @@ module GraphQL
117
147
  end
118
148
  end
119
149
  elsif as_type.kind.input_object?
120
- as_type.arguments.each do |_name, input_obj_arg|
150
+ as_type.arguments(ctx).each do |_name, input_obj_arg|
121
151
  input_obj_arg = input_obj_arg.type_class
122
152
  # TODO: this skips input objects whose values were alread replaced with application objects.
123
153
  # See: https://github.com/rmosolgo/graphql-ruby/issues/2633
124
- if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
154
+ if value.is_a?(InputObject) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
125
155
  return false
126
156
  end
127
157
  end
@@ -143,15 +173,38 @@ module GraphQL
143
173
  if NO_DEFAULT != @default_value
144
174
  argument.default_value = @default_value
145
175
  end
176
+ if self.deprecation_reason
177
+ argument.deprecation_reason = self.deprecation_reason
178
+ end
146
179
  argument
147
180
  end
148
181
 
149
- attr_writer :type
182
+ def type=(new_type)
183
+ validate_input_type(new_type)
184
+ # This isn't true for LateBoundTypes, but we can assume those will
185
+ # be updated via this codepath later in schema setup.
186
+ if new_type.respond_to?(:non_null?)
187
+ validate_deprecated_or_optional(null: !new_type.non_null?, deprecation_reason: deprecation_reason)
188
+ end
189
+ @type = new_type
190
+ end
150
191
 
151
192
  def type
152
- @type ||= Member::BuildType.parse_type(@type_expr, null: @null)
153
- rescue StandardError => err
154
- raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
193
+ @type ||= begin
194
+ parsed_type = begin
195
+ Member::BuildType.parse_type(@type_expr, null: @null)
196
+ rescue StandardError => err
197
+ raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
198
+ end
199
+ # Use the setter method to get validations
200
+ self.type = parsed_type
201
+ end
202
+ end
203
+
204
+ def statically_coercible?
205
+ return @statically_coercible if defined?(@statically_coercible)
206
+
207
+ @statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
155
208
  end
156
209
 
157
210
  # Apply the {prepare} configuration to `value`, using methods from `obj`.
@@ -162,6 +215,8 @@ module GraphQL
162
215
  value = value.prepare
163
216
  end
164
217
 
218
+ Schema::Validator.validate!(validators, obj, context, value)
219
+
165
220
  if @prepare.nil?
166
221
  value
167
222
  elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
@@ -180,6 +235,149 @@ module GraphQL
180
235
  raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
181
236
  end
182
237
  end
238
+
239
+ # @api private
240
+ def coerce_into_values(parent_object, values, context, argument_values)
241
+ arg_name = graphql_name
242
+ arg_key = keyword
243
+ default_used = false
244
+
245
+ if values.key?(arg_name)
246
+ value = values[arg_name]
247
+ elsif values.key?(arg_key)
248
+ value = values[arg_key]
249
+ elsif default_value?
250
+ value = default_value
251
+ default_used = true
252
+ else
253
+ # no value at all
254
+ owner.validate_directive_argument(self, nil)
255
+ return
256
+ end
257
+
258
+ loaded_value = nil
259
+ coerced_value = context.schema.error_handler.with_error_handling(context) do
260
+ type.coerce_input(value, context)
261
+ end
262
+
263
+ # If this isn't lazy, then the block returns eagerly and assigns the result here
264
+ # If it _is_ lazy, then we write the lazy to the hash, then update it later
265
+ argument_values[arg_key] = context.schema.after_lazy(coerced_value) do |resolved_coerced_value|
266
+ if loads && !from_resolver?
267
+ loaded_value = context.query.with_error_handling do
268
+ load_and_authorize_value(owner, coerced_value, context)
269
+ end
270
+ end
271
+
272
+ maybe_loaded_value = loaded_value || resolved_coerced_value
273
+ context.schema.after_lazy(maybe_loaded_value) do |resolved_loaded_value|
274
+ owner.validate_directive_argument(self, resolved_loaded_value)
275
+ prepared_value = context.schema.error_handler.with_error_handling(context) do
276
+ prepare_value(parent_object, resolved_loaded_value, context: context)
277
+ end
278
+
279
+ # TODO code smell to access such a deeply-nested constant in a distant module
280
+ argument_values[arg_key] = GraphQL::Execution::Interpreter::ArgumentValue.new(
281
+ value: prepared_value,
282
+ definition: self,
283
+ default_used: default_used,
284
+ )
285
+ end
286
+ end
287
+ end
288
+
289
+ def load_and_authorize_value(load_method_owner, coerced_value, context)
290
+ if coerced_value.nil?
291
+ return nil
292
+ end
293
+ arg_load_method = "load_#{keyword}"
294
+ if load_method_owner.respond_to?(arg_load_method)
295
+ custom_loaded_value = if load_method_owner.is_a?(Class)
296
+ load_method_owner.public_send(arg_load_method, coerced_value, context)
297
+ else
298
+ load_method_owner.public_send(arg_load_method, coerced_value)
299
+ end
300
+ context.schema.after_lazy(custom_loaded_value) do |custom_value|
301
+ if loads
302
+ if type.list?
303
+ loaded_values = custom_value.each_with_index.map { |custom_val, idx|
304
+ id = coerced_value[idx]
305
+ load_method_owner.authorize_application_object(self, id, context, custom_val)
306
+ }
307
+ context.schema.after_any_lazies(loaded_values, &:itself)
308
+ else
309
+ load_method_owner.authorize_application_object(self, coerced_value, context, custom_loaded_value)
310
+ end
311
+ else
312
+ custom_value
313
+ end
314
+ end
315
+ elsif loads
316
+ if type.list?
317
+ loaded_values = coerced_value.map { |val| load_method_owner.load_and_authorize_application_object(self, val, context) }
318
+ context.schema.after_any_lazies(loaded_values, &:itself)
319
+ else
320
+ load_method_owner.load_and_authorize_application_object(self, coerced_value, context)
321
+ end
322
+ else
323
+ coerced_value
324
+ end
325
+ end
326
+
327
+ # @api private
328
+ def validate_default_value
329
+ coerced_default_value = begin
330
+ # This is weird, but we should accept single-item default values for list-type arguments.
331
+ # If we used `coerce_isolated_input` below, it would do this for us, but it's not really
332
+ # the right thing here because we expect default values in application format (Ruby values)
333
+ # not GraphQL format (scalar values).
334
+ #
335
+ # But I don't think Schema::List#coerce_result should apply wrapping to single-item lists.
336
+ prepped_default_value = if default_value.nil?
337
+ nil
338
+ elsif (type.kind.list? || (type.kind.non_null? && type.of_type.list?)) && !default_value.respond_to?(:map)
339
+ [default_value]
340
+ else
341
+ default_value
342
+ end
343
+
344
+ type.coerce_isolated_result(prepped_default_value) unless prepped_default_value.nil?
345
+ rescue GraphQL::Schema::Enum::UnresolvedValueError
346
+ # It raises this, which is helpful at runtime, but not here...
347
+ default_value
348
+ end
349
+ res = type.valid_isolated_input?(coerced_default_value)
350
+ if !res
351
+ raise InvalidDefaultValueError.new(self)
352
+ end
353
+ end
354
+
355
+ class InvalidDefaultValueError < GraphQL::Error
356
+ def initialize(argument)
357
+ message = "`#{argument.path}` has an invalid default value: `#{argument.default_value.inspect}` isn't accepted by `#{argument.type.to_type_signature}`; update the default value or the argument type."
358
+ super(message)
359
+ end
360
+ end
361
+
362
+ private
363
+
364
+ def validate_input_type(input_type)
365
+ if input_type.is_a?(String) || input_type.is_a?(GraphQL::Schema::LateBoundType)
366
+ # Do nothing; assume this will be validated later
367
+ elsif input_type.kind.non_null? || input_type.kind.list?
368
+ validate_input_type(input_type.unwrap)
369
+ elsif !input_type.kind.input?
370
+ raise ArgumentError, "Invalid input type for #{path}: #{input_type.graphql_name}. Must be scalar, enum, or input object, not #{input_type.kind.name}."
371
+ else
372
+ # It's an input type, we're OK
373
+ end
374
+ end
375
+
376
+ def validate_deprecated_or_optional(null:, deprecation_reason:)
377
+ if deprecation_reason && !null
378
+ raise ArgumentError, "Required arguments cannot be deprecated: #{path}."
379
+ end
380
+ end
183
381
  end
184
382
  end
185
383
  end
@@ -13,6 +13,8 @@ module GraphQL
13
13
  def self.decode(encoded_text, nonce: false)
14
14
  # urlsafe_decode64 is for forward compatibility
15
15
  Base64Bp.urlsafe_decode64(encoded_text)
16
+ rescue ArgumentError
17
+ raise GraphQL::ExecutionError, "Invalid input: #{encoded_text.inspect}"
16
18
  end
17
19
  end
18
20
  end
@@ -45,8 +45,10 @@ module GraphQL
45
45
  @resolve_hash[type_name_s][field_name.to_s] = resolve_fn
46
46
  end
47
47
  when Proc
48
- # for example, __resolve_type
48
+ # for example, "resolve_type"
49
49
  @resolve_hash[type_name_s] = fields
50
+ else
51
+ raise ArgumentError, "Unexpected resolve hash value for #{type_name.inspect}: #{fields.inspect} (#{fields.class})"
50
52
  end
51
53
  end
52
54