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,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ module HasInterfaces
7
+ def implements(*new_interfaces, **options)
8
+ new_memberships = []
9
+ new_interfaces.each do |int|
10
+ if int.is_a?(Module)
11
+ unless int.include?(GraphQL::Schema::Interface)
12
+ raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
13
+ end
14
+
15
+ new_memberships << int.type_membership_class.new(int, self, **options)
16
+
17
+ # Include the methods here,
18
+ # `.fields` will use the inheritance chain
19
+ # to find inherited fields
20
+ include(int)
21
+
22
+ # If this interface has interfaces of its own, add those, too
23
+ int.interfaces.each do |next_interface|
24
+ implements(next_interface)
25
+ end
26
+ elsif int.is_a?(GraphQL::InterfaceType)
27
+ new_memberships << int.type_membership_class.new(int, self, **options)
28
+ elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
29
+ if options.any?
30
+ raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
31
+ end
32
+ new_memberships << int
33
+ else
34
+ raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
35
+ end
36
+ end
37
+
38
+ # Remove any String or late-bound interfaces which are being replaced
39
+ own_interface_type_memberships.reject! { |old_i_m|
40
+ if !(old_i_m.respond_to?(:abstract_type) && old_i_m.abstract_type.is_a?(Module))
41
+ old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
42
+ old_name = Schema::Member::BuildType.to_type_name(old_int_type)
43
+
44
+ new_memberships.any? { |new_i_m|
45
+ new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
46
+ new_name = Schema::Member::BuildType.to_type_name(new_int_type)
47
+
48
+ new_name == old_name
49
+ }
50
+ end
51
+ }
52
+ own_interface_type_memberships.concat(new_memberships)
53
+ end
54
+
55
+ def own_interface_type_memberships
56
+ @own_interface_type_memberships ||= []
57
+ end
58
+
59
+ def interface_type_memberships
60
+ own_interface_type_memberships + ((self.is_a?(Class) && superclass.respond_to?(:interface_type_memberships)) ? superclass.interface_type_memberships : [])
61
+ end
62
+
63
+ # param context [Query::Context] If omitted, skip filtering.
64
+ def interfaces(context = GraphQL::Query::NullContext)
65
+ warden = Warden.from_context(context)
66
+ visible_interfaces = []
67
+ own_interface_type_memberships.each do |type_membership|
68
+ # During initialization, `type_memberships` can hold late-bound types
69
+ case type_membership
70
+ when String, Schema::LateBoundType
71
+ visible_interfaces << type_membership
72
+ when Schema::TypeMembership
73
+ if warden.visible_type_membership?(type_membership, context)
74
+ visible_interfaces << type_membership.abstract_type
75
+ end
76
+ else
77
+ raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
78
+ end
79
+ end
80
+
81
+ if self.is_a?(Class) && superclass <= GraphQL::Schema::Object
82
+ visible_interfaces.concat(superclass.interfaces(context))
83
+ end
84
+
85
+ visible_interfaces
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # Set up a type-specific error to make debugging & bug tracker integration better
7
+ module HasUnresolvedTypeError
8
+ private
9
+ def add_unresolved_type_error(child_class)
10
+ child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ class Schema
4
+ class Member
5
+ module HasValidators
6
+ include Schema::FindInheritedValue::EmptyObjects
7
+
8
+ # Build {GraphQL::Schema::Validator}s based on the given configuration
9
+ # and use them for this schema member
10
+ # @param validation_config [Hash{Symbol => Hash}]
11
+ # @return [void]
12
+ def validates(validation_config)
13
+ new_validators = GraphQL::Schema::Validator.from_config(self, validation_config)
14
+ @own_validators ||= []
15
+ @own_validators.concat(new_validators)
16
+ nil
17
+ end
18
+
19
+ # @return [Array<GraphQL::Schema::Validator>]
20
+ def validators
21
+ own_validators = @own_validators || EMPTY_ARRAY
22
+ if self.is_a?(Class) && superclass.respond_to?(:validators) && (inherited_validators = superclass.validators).any?
23
+ inherited_validators + own_validators
24
+ else
25
+ own_validators
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
- # test_via: ../object.rb
3
2
 
4
3
  module GraphQL
5
4
  class Schema
@@ -6,12 +6,12 @@ module GraphQL
6
6
  module TypeSystemHelpers
7
7
  # @return [Schema::NonNull] Make a non-null-type representation of this type
8
8
  def to_non_null_type
9
- GraphQL::Schema::NonNull.new(self)
9
+ @to_non_null_type ||= GraphQL::Schema::NonNull.new(self)
10
10
  end
11
11
 
12
12
  # @return [Schema::List] Make a list-type representation of this type
13
13
  def to_list_type
14
- GraphQL::Schema::List.new(self)
14
+ @to_list_type ||= GraphQL::Schema::List.new(self)
15
15
  end
16
16
 
17
17
  # @return [Boolean] true if this is a non-nullable type. A nullable list of non-nullables is considered nullable.
@@ -30,7 +30,7 @@ module GraphQL
30
30
 
31
31
  # @return [GraphQL::TypeKinds::TypeKind]
32
32
  def kind
33
- raise GraphQL::RequiredImplementationMissingError
33
+ raise GraphQL::RequiredImplementationMissingError, "No `.kind` defined for #{self}"
34
34
  end
35
35
  end
36
36
  end
@@ -4,7 +4,12 @@ require 'graphql/schema/member/base_dsl_methods'
4
4
  require 'graphql/schema/member/cached_graphql_definition'
5
5
  require 'graphql/schema/member/graphql_type_names'
6
6
  require 'graphql/schema/member/has_ast_node'
7
+ require 'graphql/schema/member/has_directives'
8
+ require 'graphql/schema/member/has_deprecation_reason'
9
+ require 'graphql/schema/member/has_interfaces'
7
10
  require 'graphql/schema/member/has_path'
11
+ require 'graphql/schema/member/has_unresolved_type_error'
12
+ require 'graphql/schema/member/has_validators'
8
13
  require 'graphql/schema/member/relay_shortcuts'
9
14
  require 'graphql/schema/member/scoped'
10
15
  require 'graphql/schema/member/type_system_helpers'
@@ -29,6 +34,7 @@ module GraphQL
29
34
  extend RelayShortcuts
30
35
  extend HasPath
31
36
  extend HasAstNode
37
+ extend HasDirectives
32
38
  end
33
39
  end
34
40
  end
@@ -71,7 +71,7 @@ module GraphQL
71
71
 
72
72
  def wrap(callable)
73
73
  if BackwardsCompatibility.get_arity(callable) == 6
74
- warn("Middleware that takes a next_middleware parameter is deprecated (#{callable.inspect}); instead, accept a block and use yield.")
74
+ GraphQL::Deprecation.warn("Middleware that takes a next_middleware parameter is deprecated (#{callable.inspect}); instead, accept a block and use yield.")
75
75
  MiddlewareWrapper.new(callable)
76
76
  else
77
77
  callable
@@ -78,6 +78,10 @@ module GraphQL
78
78
 
79
79
  private
80
80
 
81
+ def conflict_field_name_warning(field_defn)
82
+ "#{self.graphql_name}'s `field :#{field_defn.name}` conflicts with a built-in method, use `hash_key:` or `method:` to pick a different resolve behavior for this field (for example, `hash_key: :#{field_defn.resolver_method}_value`, and modify the return hash). Or use `method_conflict_warning: false` to suppress this warning."
83
+ end
84
+
81
85
  # Override this to attach self as `mutation`
82
86
  def generate_payload_type
83
87
  payload_class = super
@@ -57,6 +57,11 @@ module GraphQL
57
57
  def coerce_result(value, ctx)
58
58
  of_type.coerce_result(value, ctx)
59
59
  end
60
+
61
+ # This is for implementing introspection
62
+ def description
63
+ nil
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "graphql/query/null_context"
4
+
3
5
  module GraphQL
4
6
  class Schema
5
7
  class Object < GraphQL::Schema::Member
6
8
  extend GraphQL::Schema::Member::AcceptsDefinition
7
9
  extend GraphQL::Schema::Member::HasFields
10
+ extend GraphQL::Schema::Member::HasInterfaces
8
11
 
9
12
  # @return [Object] the application object this type is wrapping
10
13
  attr_reader :object
@@ -12,6 +15,17 @@ module GraphQL
12
15
  # @return [GraphQL::Query::Context] the context instance for this query
13
16
  attr_reader :context
14
17
 
18
+ # @return [GraphQL::Dataloader]
19
+ def dataloader
20
+ context.dataloader
21
+ end
22
+
23
+ # Call this in a field method to return a value that should be returned to the client
24
+ # without any further handling by GraphQL.
25
+ def raw_value(obj)
26
+ GraphQL::Execution::Interpreter::RawValue.new(obj)
27
+ end
28
+
15
29
  class << self
16
30
  # This is protected so that we can be sure callers use the public method, {.authorized_new}
17
31
  # @see authorized_new to make instances
@@ -35,14 +49,28 @@ module GraphQL
35
49
  # @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
36
50
  # @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
37
51
  def authorized_new(object, context)
38
- auth_val = context.query.with_error_handling do
39
- begin
40
- authorized?(object, context)
41
- rescue GraphQL::UnauthorizedError => err
42
- context.schema.unauthorized_object(err)
52
+ trace_payload = { context: context, type: self, object: object, path: context[:current_path] }
53
+
54
+ maybe_lazy_auth_val = context.query.trace("authorized", trace_payload) do
55
+ context.query.with_error_handling do
56
+ begin
57
+ authorized?(object, context)
58
+ rescue GraphQL::UnauthorizedError => err
59
+ context.schema.unauthorized_object(err)
60
+ end
43
61
  end
44
62
  end
45
63
 
64
+ auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
65
+ GraphQL::Execution::Lazy.new do
66
+ context.query.trace("authorized_lazy", trace_payload) do
67
+ context.schema.sync_lazy(maybe_lazy_auth_val)
68
+ end
69
+ end
70
+ else
71
+ maybe_lazy_auth_val
72
+ end
73
+
46
74
  context.schema.after_lazy(auth_val) do |is_authorized|
47
75
  if is_authorized
48
76
  self.new(object, context)
@@ -69,50 +97,23 @@ module GraphQL
69
97
  end
70
98
 
71
99
  class << self
72
- def implements(*new_interfaces)
73
- new_interfaces.each do |int|
74
- if int.is_a?(Module)
75
- unless int.include?(GraphQL::Schema::Interface)
76
- raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
77
- end
78
-
79
- # Include the methods here,
80
- # `.fields` will use the inheritance chain
81
- # to find inherited fields
82
- include(int)
83
- end
84
- end
85
- # Remove any interfaces which are being replaced (late-bound types are updated in place this way)
86
- own_interfaces.reject! { |i|
87
- new_interfaces.any? { |new_i|
88
- new_name = new_i.is_a?(String) ? new_i : new_i.graphql_name
89
- old_name = i.is_a?(String) ? i : i.graphql_name
90
- new_name == old_name
91
- }
92
- }
93
- own_interfaces.concat(new_interfaces)
94
- end
95
-
96
- def interfaces
97
- inherited_interfaces = (superclass.respond_to?(:interfaces) ? superclass.interfaces : nil)
98
- if inherited_interfaces && !inherited_interfaces.empty?
99
- own_interfaces + inherited_interfaces
100
- else
101
- own_interfaces
102
- end
103
- end
104
-
105
- def own_interfaces
106
- @own_interfaces ||= []
100
+ # Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`.
101
+ # It should help with debugging and bug tracker integrations.
102
+ def inherited(child_class)
103
+ child_class.const_set(:InvalidNullError, GraphQL::InvalidNullError.subclass_for(child_class))
104
+ super
107
105
  end
108
106
 
109
- # Include legacy-style interfaces, too
110
- def fields
107
+ # @return [Hash<String => GraphQL::Schema::Field>] All of this object's fields, indexed by name
108
+ # @see get_field A faster way to find one field by name ({#fields} merges hashes of inherited fields; {#get_field} just looks up one field.)
109
+ def fields(context = GraphQL::Query::NullContext)
111
110
  all_fields = super
111
+ # This adds fields from legacy-style interfaces only.
112
+ # Multi-fields are not supported here.
112
113
  interfaces.each do |int|
113
114
  if int.is_a?(GraphQL::InterfaceType)
114
115
  int_f = {}
115
- int.fields.each do |name, legacy_field|
116
+ int.fields.each do |name, legacy_field| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
116
117
  int_f[name] = field_class.from_options(name, field: legacy_field)
117
118
  end
118
119
  all_fields = int_f.merge(all_fields)
@@ -126,13 +127,13 @@ module GraphQL
126
127
  obj_type = GraphQL::ObjectType.new
127
128
  obj_type.name = graphql_name
128
129
  obj_type.description = description
129
- obj_type.interfaces = interfaces
130
+ obj_type.structural_interface_type_memberships = interface_type_memberships
130
131
  obj_type.introspection = introspection
131
132
  obj_type.mutation = mutation
132
133
  obj_type.ast_node = ast_node
133
- fields.each do |field_name, field_inst|
134
+ fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
134
135
  field_defn = field_inst.to_graphql
135
- obj_type.fields[field_defn.name] = field_defn
136
+ obj_type.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
136
137
  end
137
138
 
138
139
  obj_type.metadata[:type_class] = self
@@ -14,9 +14,10 @@ module GraphQL
14
14
  class PossibleTypes
15
15
  def initialize(schema)
16
16
  @object_types = schema.types.values.select { |type| type.kind.object? }
17
-
18
- @interface_implementers = Hash.new do |hash, key|
19
- hash[key] = @object_types.select { |type| type.interfaces.include?(key) }.sort_by(&:name)
17
+ @interface_implementers = Hash.new do |h1, ctx|
18
+ h1[ctx] = Hash.new do |h2, int_type|
19
+ h2[int_type] = @object_types.select { |type| type.interfaces(ctx).include?(int_type) }.sort_by(&:name)
20
+ end
20
21
  end
21
22
  end
22
23
 
@@ -27,13 +28,17 @@ module GraphQL
27
28
  when GraphQL::UnionType
28
29
  type_defn.possible_types(ctx)
29
30
  when GraphQL::InterfaceType
30
- @interface_implementers[type_defn]
31
+ interface_implementers(ctx, type_defn)
31
32
  when GraphQL::BaseType
32
33
  [type_defn]
33
34
  else
34
35
  raise "Unexpected possible_types object: #{type_defn.inspect}"
35
36
  end
36
37
  end
38
+
39
+ def interface_implementers(ctx, type_defn)
40
+ @interface_implementers[ctx][type_defn]
41
+ end
37
42
  end
38
43
  end
39
44
  end
@@ -4,37 +4,32 @@ module GraphQL
4
4
  # Used to convert your {GraphQL::Schema} to a GraphQL schema string
5
5
  #
6
6
  # @example print your schema to standard output (via helper)
7
- # MySchema = GraphQL::Schema.define(query: QueryType)
8
7
  # puts GraphQL::Schema::Printer.print_schema(MySchema)
9
8
  #
10
9
  # @example print your schema to standard output
11
- # MySchema = GraphQL::Schema.define(query: QueryType)
12
10
  # puts GraphQL::Schema::Printer.new(MySchema).print_schema
13
11
  #
14
12
  # @example print a single type to standard output
15
- # query_root = GraphQL::ObjectType.define do
16
- # name "Query"
13
+ # class Types::Query < GraphQL::Schema::Object
17
14
  # description "The query root of this schema"
18
15
  #
19
- # field :post do
20
- # type post_type
21
- # resolve ->(obj, args, ctx) { Post.find(args["id"]) }
22
- # end
16
+ # field :post, Types::Post, null: true
23
17
  # end
24
18
  #
25
- # post_type = GraphQL::ObjectType.define do
26
- # name "Post"
19
+ # class Types::Post < GraphQL::Schema::Object
27
20
  # description "A blog post"
28
21
  #
29
- # field :id, !types.ID
30
- # field :title, !types.String
31
- # field :body, !types.String
22
+ # field :id, ID, null: false
23
+ # field :title, String, null: false
24
+ # field :body, String, null: false
32
25
  # end
33
26
  #
34
- # MySchema = GraphQL::Schema.define(query: query_root)
27
+ # class MySchema < GraphQL::Schema
28
+ # query(Types::Query)
29
+ # end
35
30
  #
36
31
  # printer = GraphQL::Schema::Printer.new(MySchema)
37
- # puts printer.print_type(post_type)
32
+ # puts printer.print_type(Types::Post)
38
33
  #
39
34
  class Printer < GraphQL::Language::Printer
40
35
  attr_reader :schema, :warden
@@ -59,14 +54,15 @@ module GraphQL
59
54
 
60
55
  # Return the GraphQL schema string for the introspection type system
61
56
  def self.print_introspection_schema
62
- query_root = ObjectType.define(name: "Root") do
63
- field :throwaway_field, types.String
57
+ query_root = Class.new(GraphQL::Schema::Object) do
58
+ graphql_name "Root"
59
+ field :throwaway_field, String
64
60
  end
65
- schema = GraphQL::Schema.define(query: query_root)
61
+ schema = Class.new(GraphQL::Schema) { query(query_root) }
66
62
 
67
63
  introspection_schema_ast = GraphQL::Language::DocumentFromSchemaDefinition.new(
68
64
  schema,
69
- except: ->(member, _) { member.name == "Root" },
65
+ except: ->(member, _) { member.graphql_name == "Root" },
70
66
  include_introspection_types: true,
71
67
  include_built_in_directives: true,
72
68
  ).document
@@ -86,7 +82,7 @@ module GraphQL
86
82
 
87
83
  # Return a GraphQL schema string for the defined types in the schema
88
84
  def print_schema
89
- print(@document)
85
+ print(@document) + "\n"
90
86
  end
91
87
 
92
88
  def print_type(type)
@@ -94,20 +90,6 @@ module GraphQL
94
90
  print(node)
95
91
  end
96
92
 
97
- def print_directive(directive)
98
- if directive.name == "deprecated"
99
- reason = directive.arguments.find { |arg| arg.name == "reason" }
100
-
101
- if reason.value == GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON
102
- "@deprecated"
103
- else
104
- "@deprecated(reason: #{reason.value.to_s.inspect})"
105
- end
106
- else
107
- super
108
- end
109
- end
110
-
111
93
  class IntrospectionPrinter < GraphQL::Language::Printer
112
94
  def print_schema_definition(schema)
113
95
  "schema {\n query: Root\n}"
@@ -22,7 +22,7 @@ module GraphQL
22
22
  #
23
23
  class RelayClassicMutation < GraphQL::Schema::Mutation
24
24
  # The payload should always include this field
25
- field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.", null: true)
25
+ field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
26
26
  # Relay classic default:
27
27
  null(true)
28
28
 
@@ -81,6 +81,31 @@ module GraphQL
81
81
  end
82
82
 
83
83
  class << self
84
+
85
+ # Also apply this argument to the input type:
86
+ def argument(*args, **kwargs, &block)
87
+ it = input_type # make sure any inherited arguments are already added to it
88
+ arg = super
89
+
90
+ # This definition might be overriding something inherited;
91
+ # if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
92
+ prev_args = it.own_arguments[arg.graphql_name]
93
+ case prev_args
94
+ when GraphQL::Schema::Argument
95
+ if prev_args.owner != self
96
+ it.own_arguments.delete(arg.graphql_name)
97
+ end
98
+ when Array
99
+ prev_args.reject! { |a| a.owner != self }
100
+ if prev_args.empty?
101
+ it.own_arguments.delete(arg.graphql_name)
102
+ end
103
+ end
104
+
105
+ it.add_argument(arg)
106
+ arg
107
+ end
108
+
84
109
  # The base class for generated input object types
85
110
  # @param new_class [Class] The base class to use for generating input object definitions
86
111
  # @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
@@ -105,7 +130,7 @@ module GraphQL
105
130
  sig = super
106
131
  # Arguments were added at the root, but they should be nested
107
132
  sig[:arguments].clear
108
- sig[:arguments][:input] = { type: input_type, required: true }
133
+ sig[:arguments][:input] = { type: input_type, required: true, description: "Parameters for #{graphql_name}" }
109
134
  sig
110
135
  end
111
136
 
@@ -115,14 +140,17 @@ module GraphQL
115
140
  # To customize how input objects are generated, override this method
116
141
  # @return [Class] a subclass of {.input_object_class}
117
142
  def generate_input_type
118
- mutation_args = arguments
143
+ mutation_args = all_argument_definitions
119
144
  mutation_name = graphql_name
120
145
  mutation_class = self
121
146
  Class.new(input_object_class) do
122
147
  graphql_name("#{mutation_name}Input")
123
148
  description("Autogenerated input type of #{mutation_name}")
124
149
  mutation(mutation_class)
125
- own_arguments.merge!(mutation_args)
150
+ # these might be inherited:
151
+ mutation_args.each do |arg|
152
+ add_argument(arg)
153
+ end
126
154
  argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
127
155
  end
128
156
  end
@@ -26,8 +26,10 @@ module GraphQL
26
26
  def field_class(new_class = nil)
27
27
  if new_class
28
28
  @field_class = new_class
29
+ elsif defined?(@field_class) && @field_class
30
+ @field_class
29
31
  else
30
- @field_class || find_inherited_value(:field_class, GraphQL::Schema::Field)
32
+ find_inherited_value(:field_class, GraphQL::Schema::Field)
31
33
  end
32
34
  end
33
35
 
@@ -36,12 +38,39 @@ module GraphQL
36
38
  # @return [Class]
37
39
  def object_class(new_class = nil)
38
40
  if new_class
41
+ if defined?(@payload_type)
42
+ raise "Can't configure `object_class(...)` after the payload type has already been initialized. Move this configuration higher up the class definition."
43
+ end
39
44
  @object_class = new_class
40
45
  else
41
46
  @object_class || find_inherited_value(:object_class, GraphQL::Schema::Object)
42
47
  end
43
48
  end
44
49
 
50
+ NO_INTERFACES = [].freeze
51
+
52
+ def field(*args, **kwargs, &block)
53
+ pt = payload_type # make sure it's initialized with any inherited fields
54
+ field_defn = super
55
+
56
+ # Remove any inherited fields to avoid false conflicts at runtime
57
+ prev_fields = pt.own_fields[field_defn.graphql_name]
58
+ case prev_fields
59
+ when GraphQL::Schema::Field
60
+ if prev_fields.owner != self
61
+ pt.own_fields.delete(field_defn.graphql_name)
62
+ end
63
+ when Array
64
+ prev_fields.reject! { |f| f.owner != self }
65
+ if prev_fields.empty?
66
+ pt.own_fields.delete(field_defn.graphql_name)
67
+ end
68
+ end
69
+
70
+ pt.add_field(field_defn, method_conflict_warning: false)
71
+ field_defn
72
+ end
73
+
45
74
  private
46
75
 
47
76
  # Build a subclass of {.object_class} based on `self`.
@@ -49,14 +78,15 @@ module GraphQL
49
78
  # Override this hook to customize return type generation.
50
79
  def generate_payload_type
51
80
  resolver_name = graphql_name
52
- resolver_fields = fields
81
+ resolver_fields = all_field_definitions
53
82
  Class.new(object_class) do
54
83
  graphql_name("#{resolver_name}Payload")
55
84
  description("Autogenerated return type of #{resolver_name}")
56
- resolver_fields.each do |name, f|
85
+ resolver_fields.each do |f|
57
86
  # Reattach the already-defined field here
58
87
  # (The field's `.owner` will still point to the mutation, not the object type, I think)
59
- add_field(f)
88
+ # Don't re-warn about a method conflict. Since this type is generated, it should be fixed in the resolver instead.
89
+ add_field(f, method_conflict_warning: false)
60
90
  end
61
91
  end
62
92
  end