graphql 1.9.21 → 2.0.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 (397) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/core.rb +21 -10
  3. data/lib/generators/graphql/enum_generator.rb +4 -10
  4. data/lib/generators/graphql/field_extractor.rb +31 -0
  5. data/lib/generators/graphql/input_generator.rb +50 -0
  6. data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
  7. data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
  8. data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
  9. data/lib/generators/graphql/install_generator.rb +44 -7
  10. data/lib/generators/graphql/interface_generator.rb +7 -7
  11. data/lib/generators/graphql/loader_generator.rb +1 -0
  12. data/lib/generators/graphql/mutation_create_generator.rb +22 -0
  13. data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
  14. data/lib/generators/graphql/mutation_generator.rb +6 -30
  15. data/lib/generators/graphql/mutation_update_generator.rb +22 -0
  16. data/lib/generators/graphql/object_generator.rb +28 -12
  17. data/lib/generators/graphql/orm_mutations_base.rb +40 -0
  18. data/lib/generators/graphql/relay.rb +63 -0
  19. data/lib/generators/graphql/relay_generator.rb +21 -0
  20. data/lib/generators/graphql/scalar_generator.rb +4 -2
  21. data/lib/generators/graphql/templates/base_argument.erb +2 -0
  22. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  23. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  24. data/lib/generators/graphql/templates/base_enum.erb +2 -0
  25. data/lib/generators/graphql/templates/base_field.erb +2 -0
  26. data/lib/generators/graphql/templates/base_input_object.erb +2 -0
  27. data/lib/generators/graphql/templates/base_interface.erb +2 -0
  28. data/lib/generators/graphql/templates/base_object.erb +2 -0
  29. data/lib/generators/graphql/templates/base_scalar.erb +2 -0
  30. data/lib/generators/graphql/templates/base_union.erb +2 -0
  31. data/lib/generators/graphql/templates/enum.erb +7 -1
  32. data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
  33. data/lib/generators/graphql/templates/input.erb +9 -0
  34. data/lib/generators/graphql/templates/interface.erb +6 -2
  35. data/lib/generators/graphql/templates/loader.erb +2 -0
  36. data/lib/generators/graphql/templates/mutation.erb +3 -1
  37. data/lib/generators/graphql/templates/mutation_create.erb +20 -0
  38. data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
  39. data/lib/generators/graphql/templates/mutation_update.erb +21 -0
  40. data/lib/generators/graphql/templates/node_type.erb +9 -0
  41. data/lib/generators/graphql/templates/object.erb +7 -3
  42. data/lib/generators/graphql/templates/query_type.erb +3 -3
  43. data/lib/generators/graphql/templates/scalar.erb +5 -1
  44. data/lib/generators/graphql/templates/schema.erb +22 -27
  45. data/lib/generators/graphql/templates/union.erb +6 -2
  46. data/lib/generators/graphql/type_generator.rb +47 -10
  47. data/lib/generators/graphql/union_generator.rb +5 -5
  48. data/lib/graphql/analysis/ast/field_usage.rb +29 -2
  49. data/lib/graphql/analysis/ast/query_complexity.rb +175 -68
  50. data/lib/graphql/analysis/ast/query_depth.rb +0 -1
  51. data/lib/graphql/analysis/ast/visitor.rb +17 -8
  52. data/lib/graphql/analysis/ast.rb +14 -14
  53. data/lib/graphql/analysis.rb +0 -7
  54. data/lib/graphql/backtrace/inspect_result.rb +0 -1
  55. data/lib/graphql/backtrace/table.rb +37 -16
  56. data/lib/graphql/backtrace/traced_error.rb +0 -1
  57. data/lib/graphql/backtrace/tracer.rb +39 -9
  58. data/lib/graphql/backtrace.rb +20 -17
  59. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  60. data/lib/graphql/dataloader/request.rb +19 -0
  61. data/lib/graphql/dataloader/request_all.rb +19 -0
  62. data/lib/graphql/dataloader/source.rb +155 -0
  63. data/lib/graphql/dataloader.rb +308 -0
  64. data/lib/graphql/date_encoding_error.rb +16 -0
  65. data/lib/graphql/deprecation.rb +9 -0
  66. data/lib/graphql/dig.rb +1 -1
  67. data/lib/graphql/execution/directive_checks.rb +2 -2
  68. data/lib/graphql/execution/errors.rb +108 -14
  69. data/lib/graphql/execution/instrumentation.rb +1 -1
  70. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  71. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  72. data/lib/graphql/execution/interpreter/arguments_cache.rb +105 -0
  73. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  74. data/lib/graphql/execution/interpreter/resolve.rb +37 -25
  75. data/lib/graphql/execution/interpreter/runtime.rb +715 -387
  76. data/lib/graphql/execution/interpreter.rb +32 -31
  77. data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
  78. data/lib/graphql/execution/lazy.rb +5 -1
  79. data/lib/graphql/execution/lookahead.rb +32 -114
  80. data/lib/graphql/execution/multiplex.rb +60 -92
  81. data/lib/graphql/execution.rb +11 -3
  82. data/lib/graphql/filter.rb +1 -1
  83. data/lib/graphql/integer_decoding_error.rb +17 -0
  84. data/lib/graphql/integer_encoding_error.rb +18 -2
  85. data/lib/graphql/introspection/base_object.rb +2 -5
  86. data/lib/graphql/introspection/directive_location_enum.rb +2 -2
  87. data/lib/graphql/introspection/directive_type.rb +11 -5
  88. data/lib/graphql/introspection/dynamic_fields.rb +3 -8
  89. data/lib/graphql/introspection/entry_points.rb +5 -18
  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 +41 -11
  93. data/lib/graphql/introspection/introspection_query.rb +6 -92
  94. data/lib/graphql/introspection/schema_type.rb +10 -10
  95. data/lib/graphql/introspection/type_type.rb +27 -17
  96. data/lib/graphql/introspection.rb +99 -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/definition_slice.rb +21 -10
  101. data/lib/graphql/language/document_from_schema_definition.rb +99 -63
  102. data/lib/graphql/language/lexer.rb +53 -27
  103. data/lib/graphql/language/lexer.rl +5 -3
  104. data/lib/graphql/language/nodes.rb +64 -93
  105. data/lib/graphql/language/parser.rb +929 -896
  106. data/lib/graphql/language/parser.y +125 -102
  107. data/lib/graphql/language/printer.rb +11 -2
  108. data/lib/graphql/language/sanitized_printer.rb +222 -0
  109. data/lib/graphql/language/token.rb +0 -4
  110. data/lib/graphql/language/visitor.rb +2 -2
  111. data/lib/graphql/language.rb +3 -1
  112. data/lib/graphql/name_validator.rb +2 -7
  113. data/lib/graphql/pagination/active_record_relation_connection.rb +85 -0
  114. data/lib/graphql/pagination/array_connection.rb +77 -0
  115. data/lib/graphql/pagination/connection.rb +226 -0
  116. data/lib/graphql/pagination/connections.rb +134 -0
  117. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  118. data/lib/graphql/pagination/relation_connection.rb +226 -0
  119. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  120. data/lib/graphql/pagination.rb +6 -0
  121. data/lib/graphql/parse_error.rb +0 -1
  122. data/lib/graphql/query/context.rb +51 -190
  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 +26 -11
  126. data/lib/graphql/query/null_context.rb +24 -8
  127. data/lib/graphql/query/validation_pipeline.rb +12 -38
  128. data/lib/graphql/query/variable_validation_error.rb +2 -2
  129. data/lib/graphql/query/variables.rb +26 -9
  130. data/lib/graphql/query.rb +62 -32
  131. data/lib/graphql/railtie.rb +6 -102
  132. data/lib/graphql/rake_task/validate.rb +3 -0
  133. data/lib/graphql/rake_task.rb +12 -9
  134. data/lib/graphql/relay/range_add.rb +23 -9
  135. data/lib/graphql/relay.rb +0 -15
  136. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  137. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  138. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  139. data/lib/graphql/rubocop.rb +4 -0
  140. data/lib/graphql/schema/addition.rb +240 -0
  141. data/lib/graphql/schema/argument.rb +262 -28
  142. data/lib/graphql/schema/base_64_encoder.rb +2 -0
  143. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
  144. data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
  145. data/lib/graphql/schema/build_from_definition.rb +319 -220
  146. data/lib/graphql/schema/built_in_types.rb +5 -5
  147. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  148. data/lib/graphql/schema/directive/feature.rb +1 -1
  149. data/lib/graphql/schema/directive/flagged.rb +57 -0
  150. data/lib/graphql/schema/directive/include.rb +2 -2
  151. data/lib/graphql/schema/directive/skip.rb +2 -2
  152. data/lib/graphql/schema/directive/transform.rb +14 -2
  153. data/lib/graphql/schema/directive.rb +117 -14
  154. data/lib/graphql/schema/enum.rb +115 -22
  155. data/lib/graphql/schema/enum_value.rb +16 -21
  156. data/lib/graphql/schema/field/connection_extension.rb +46 -20
  157. data/lib/graphql/schema/field/scope_extension.rb +1 -1
  158. data/lib/graphql/schema/field.rb +376 -291
  159. data/lib/graphql/schema/field_extension.rb +89 -2
  160. data/lib/graphql/schema/find_inherited_value.rb +17 -1
  161. data/lib/graphql/schema/finder.rb +16 -14
  162. data/lib/graphql/schema/input_object.rb +147 -60
  163. data/lib/graphql/schema/interface.rb +28 -43
  164. data/lib/graphql/schema/introspection_system.rb +101 -38
  165. data/lib/graphql/schema/late_bound_type.rb +3 -2
  166. data/lib/graphql/schema/list.rb +46 -3
  167. data/lib/graphql/schema/loader.rb +144 -102
  168. data/lib/graphql/schema/member/base_dsl_methods.rb +33 -32
  169. data/lib/graphql/schema/member/build_type.rb +23 -14
  170. data/lib/graphql/schema/member/has_arguments.rb +212 -23
  171. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  172. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  173. data/lib/graphql/schema/member/has_directives.rb +98 -0
  174. data/lib/graphql/schema/member/has_fields.rb +99 -34
  175. data/lib/graphql/schema/member/has_interfaces.rb +88 -0
  176. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  177. data/lib/graphql/schema/member/has_validators.rb +31 -0
  178. data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
  179. data/lib/graphql/schema/member/validates_input.rb +33 -0
  180. data/lib/graphql/schema/member.rb +11 -6
  181. data/lib/graphql/schema/mutation.rb +4 -9
  182. data/lib/graphql/schema/non_null.rb +34 -4
  183. data/lib/graphql/schema/object.rb +38 -65
  184. data/lib/graphql/schema/printer.rb +16 -35
  185. data/lib/graphql/schema/relay_classic_mutation.rb +57 -32
  186. data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
  187. data/lib/graphql/schema/resolver.rb +133 -79
  188. data/lib/graphql/schema/scalar.rb +40 -15
  189. data/lib/graphql/schema/subscription.rb +57 -21
  190. data/lib/graphql/schema/timeout.rb +29 -15
  191. data/lib/graphql/schema/type_expression.rb +21 -13
  192. data/lib/graphql/schema/type_membership.rb +19 -5
  193. data/lib/graphql/schema/union.rb +39 -14
  194. data/lib/graphql/schema/unique_within_type.rb +1 -2
  195. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  196. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  197. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  198. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  199. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  200. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  201. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  202. data/lib/graphql/schema/validator/required_validator.rb +82 -0
  203. data/lib/graphql/schema/validator.rb +171 -0
  204. data/lib/graphql/schema/warden.rb +182 -32
  205. data/lib/graphql/schema/wrapper.rb +0 -5
  206. data/lib/graphql/schema.rb +687 -891
  207. data/lib/graphql/static_validation/all_rules.rb +2 -0
  208. data/lib/graphql/static_validation/base_visitor.rb +21 -31
  209. data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
  210. data/lib/graphql/static_validation/error.rb +3 -1
  211. data/lib/graphql/static_validation/literal_validator.rb +51 -26
  212. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +45 -83
  213. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
  214. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +35 -26
  215. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
  216. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  217. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +14 -14
  218. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
  219. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
  220. data/lib/graphql/static_validation/rules/fields_will_merge.rb +94 -51
  221. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
  222. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  223. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
  224. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  225. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  226. data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
  227. data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
  228. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -2
  229. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +9 -10
  230. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
  231. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
  232. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +19 -14
  233. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  234. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
  235. data/lib/graphql/static_validation/type_stack.rb +2 -2
  236. data/lib/graphql/static_validation/validation_context.rb +13 -3
  237. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  238. data/lib/graphql/static_validation/validator.rb +31 -19
  239. data/lib/graphql/static_validation.rb +1 -2
  240. data/lib/graphql/string_encoding_error.rb +13 -3
  241. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +123 -22
  242. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  243. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +58 -0
  244. data/lib/graphql/subscriptions/event.rb +85 -31
  245. data/lib/graphql/subscriptions/instrumentation.rb +0 -47
  246. data/lib/graphql/subscriptions/serialize.rb +53 -6
  247. data/lib/graphql/subscriptions.rb +111 -52
  248. data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -17
  249. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  250. data/lib/graphql/tracing/appsignal_tracing.rb +23 -0
  251. data/lib/graphql/tracing/data_dog_tracing.rb +14 -1
  252. data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
  253. data/lib/graphql/tracing/notifications_tracing.rb +59 -0
  254. data/lib/graphql/tracing/platform_tracing.rb +57 -29
  255. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
  256. data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
  257. data/lib/graphql/tracing/scout_tracing.rb +19 -0
  258. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  259. data/lib/graphql/tracing.rb +15 -36
  260. data/lib/graphql/types/big_int.rb +5 -1
  261. data/lib/graphql/types/int.rb +10 -3
  262. data/lib/graphql/types/iso_8601_date.rb +16 -8
  263. data/lib/graphql/types/iso_8601_date_time.rb +32 -10
  264. data/lib/graphql/types/relay/base_connection.rb +6 -88
  265. data/lib/graphql/types/relay/base_edge.rb +2 -34
  266. data/lib/graphql/types/relay/connection_behaviors.rb +170 -0
  267. data/lib/graphql/types/relay/default_relay.rb +21 -0
  268. data/lib/graphql/types/relay/edge_behaviors.rb +64 -0
  269. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  270. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  271. data/lib/graphql/types/relay/node.rb +2 -4
  272. data/lib/graphql/types/relay/node_behaviors.rb +19 -0
  273. data/lib/graphql/types/relay/page_info.rb +2 -14
  274. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  275. data/lib/graphql/types/relay.rb +11 -5
  276. data/lib/graphql/types/string.rb +8 -2
  277. data/lib/graphql/unauthorized_error.rb +2 -2
  278. data/lib/graphql/unresolved_type_error.rb +2 -2
  279. data/lib/graphql/version.rb +1 -1
  280. data/lib/graphql.rb +41 -58
  281. data/readme.md +3 -6
  282. metadata +97 -231
  283. data/lib/graphql/analysis/analyze_query.rb +0 -91
  284. data/lib/graphql/analysis/field_usage.rb +0 -45
  285. data/lib/graphql/analysis/max_query_complexity.rb +0 -26
  286. data/lib/graphql/analysis/max_query_depth.rb +0 -26
  287. data/lib/graphql/analysis/query_complexity.rb +0 -88
  288. data/lib/graphql/analysis/query_depth.rb +0 -43
  289. data/lib/graphql/analysis/reducer_state.rb +0 -48
  290. data/lib/graphql/argument.rb +0 -159
  291. data/lib/graphql/authorization.rb +0 -82
  292. data/lib/graphql/backwards_compatibility.rb +0 -60
  293. data/lib/graphql/base_type.rb +0 -226
  294. data/lib/graphql/boolean_type.rb +0 -2
  295. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
  296. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
  297. data/lib/graphql/compatibility/execution_specification.rb +0 -435
  298. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
  299. data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -213
  300. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -91
  301. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
  302. data/lib/graphql/compatibility/query_parser_specification.rb +0 -264
  303. data/lib/graphql/compatibility/schema_parser_specification.rb +0 -680
  304. data/lib/graphql/compatibility.rb +0 -5
  305. data/lib/graphql/define/assign_argument.rb +0 -12
  306. data/lib/graphql/define/assign_connection.rb +0 -13
  307. data/lib/graphql/define/assign_enum_value.rb +0 -18
  308. data/lib/graphql/define/assign_global_id_field.rb +0 -11
  309. data/lib/graphql/define/assign_mutation_function.rb +0 -34
  310. data/lib/graphql/define/assign_object_field.rb +0 -42
  311. data/lib/graphql/define/defined_object_proxy.rb +0 -53
  312. data/lib/graphql/define/instance_definable.rb +0 -311
  313. data/lib/graphql/define/no_definition_error.rb +0 -7
  314. data/lib/graphql/define/non_null_with_bang.rb +0 -16
  315. data/lib/graphql/define/type_definer.rb +0 -31
  316. data/lib/graphql/define.rb +0 -31
  317. data/lib/graphql/deprecated_dsl.rb +0 -42
  318. data/lib/graphql/directive/deprecated_directive.rb +0 -13
  319. data/lib/graphql/directive/include_directive.rb +0 -2
  320. data/lib/graphql/directive/skip_directive.rb +0 -2
  321. data/lib/graphql/directive.rb +0 -104
  322. data/lib/graphql/enum_type.rb +0 -193
  323. data/lib/graphql/execution/execute.rb +0 -326
  324. data/lib/graphql/execution/flatten.rb +0 -40
  325. data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
  326. data/lib/graphql/execution/typecast.rb +0 -50
  327. data/lib/graphql/field/resolve.rb +0 -59
  328. data/lib/graphql/field.rb +0 -330
  329. data/lib/graphql/float_type.rb +0 -2
  330. data/lib/graphql/function.rb +0 -153
  331. data/lib/graphql/id_type.rb +0 -2
  332. data/lib/graphql/input_object_type.rb +0 -154
  333. data/lib/graphql/int_type.rb +0 -2
  334. data/lib/graphql/interface_type.rb +0 -86
  335. data/lib/graphql/internal_representation/document.rb +0 -27
  336. data/lib/graphql/internal_representation/node.rb +0 -206
  337. data/lib/graphql/internal_representation/print.rb +0 -51
  338. data/lib/graphql/internal_representation/rewrite.rb +0 -184
  339. data/lib/graphql/internal_representation/scope.rb +0 -88
  340. data/lib/graphql/internal_representation/visit.rb +0 -36
  341. data/lib/graphql/internal_representation.rb +0 -7
  342. data/lib/graphql/list_type.rb +0 -80
  343. data/lib/graphql/literal_validation_error.rb +0 -6
  344. data/lib/graphql/non_null_type.rb +0 -81
  345. data/lib/graphql/object_type.rb +0 -141
  346. data/lib/graphql/query/arguments.rb +0 -187
  347. data/lib/graphql/query/arguments_cache.rb +0 -25
  348. data/lib/graphql/query/executor.rb +0 -53
  349. data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
  350. data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
  351. data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
  352. data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
  353. data/lib/graphql/query/serial_execution.rb +0 -39
  354. data/lib/graphql/relay/array_connection.rb +0 -85
  355. data/lib/graphql/relay/base_connection.rb +0 -172
  356. data/lib/graphql/relay/connection_instrumentation.rb +0 -54
  357. data/lib/graphql/relay/connection_resolve.rb +0 -43
  358. data/lib/graphql/relay/connection_type.rb +0 -40
  359. data/lib/graphql/relay/edge.rb +0 -27
  360. data/lib/graphql/relay/edge_type.rb +0 -18
  361. data/lib/graphql/relay/edges_instrumentation.rb +0 -40
  362. data/lib/graphql/relay/global_id_resolve.rb +0 -18
  363. data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
  364. data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
  365. data/lib/graphql/relay/mutation/resolve.rb +0 -56
  366. data/lib/graphql/relay/mutation/result.rb +0 -38
  367. data/lib/graphql/relay/mutation.rb +0 -190
  368. data/lib/graphql/relay/node.rb +0 -36
  369. data/lib/graphql/relay/page_info.rb +0 -7
  370. data/lib/graphql/relay/relation_connection.rb +0 -190
  371. data/lib/graphql/relay/type_extensions.rb +0 -30
  372. data/lib/graphql/scalar_type.rb +0 -133
  373. data/lib/graphql/schema/catchall_middleware.rb +0 -35
  374. data/lib/graphql/schema/default_parse_error.rb +0 -10
  375. data/lib/graphql/schema/default_type_error.rb +0 -15
  376. data/lib/graphql/schema/member/accepts_definition.rb +0 -152
  377. data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -26
  378. data/lib/graphql/schema/member/instrumentation.rb +0 -132
  379. data/lib/graphql/schema/middleware_chain.rb +0 -82
  380. data/lib/graphql/schema/possible_types.rb +0 -39
  381. data/lib/graphql/schema/rescue_middleware.rb +0 -60
  382. data/lib/graphql/schema/timeout_middleware.rb +0 -86
  383. data/lib/graphql/schema/traversal.rb +0 -228
  384. data/lib/graphql/schema/validation.rb +0 -303
  385. data/lib/graphql/static_validation/default_visitor.rb +0 -15
  386. data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
  387. data/lib/graphql/string_type.rb +0 -2
  388. data/lib/graphql/subscriptions/subscription_root.rb +0 -74
  389. data/lib/graphql/tracing/skylight_tracing.rb +0 -62
  390. data/lib/graphql/types/relay/base_field.rb +0 -22
  391. data/lib/graphql/types/relay/base_interface.rb +0 -29
  392. data/lib/graphql/types/relay/base_object.rb +0 -26
  393. data/lib/graphql/types/relay/node_field.rb +0 -43
  394. data/lib/graphql/types/relay/nodes_field.rb +0 -45
  395. data/lib/graphql/union_type.rb +0 -135
  396. data/lib/graphql/upgrader/member.rb +0 -936
  397. data/lib/graphql/upgrader/schema.rb +0 -37
@@ -1,26 +1,19 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/schema/addition"
2
3
  require "graphql/schema/base_64_encoder"
3
- require "graphql/schema/catchall_middleware"
4
- require "graphql/schema/default_parse_error"
5
- require "graphql/schema/default_type_error"
6
4
  require "graphql/schema/find_inherited_value"
7
5
  require "graphql/schema/finder"
8
6
  require "graphql/schema/invalid_type_error"
9
7
  require "graphql/schema/introspection_system"
10
8
  require "graphql/schema/late_bound_type"
11
- require "graphql/schema/middleware_chain"
12
9
  require "graphql/schema/null_mask"
13
- require "graphql/schema/possible_types"
14
- require "graphql/schema/rescue_middleware"
15
10
  require "graphql/schema/timeout"
16
- require "graphql/schema/timeout_middleware"
17
- require "graphql/schema/traversal"
18
11
  require "graphql/schema/type_expression"
19
12
  require "graphql/schema/unique_within_type"
20
- require "graphql/schema/validation"
21
13
  require "graphql/schema/warden"
22
14
  require "graphql/schema/build_from_definition"
23
15
 
16
+ require "graphql/schema/validator"
24
17
  require "graphql/schema/member"
25
18
  require "graphql/schema/wrapper"
26
19
  require "graphql/schema/list"
@@ -36,9 +29,11 @@ require "graphql/schema/scalar"
36
29
  require "graphql/schema/object"
37
30
  require "graphql/schema/union"
38
31
  require "graphql/schema/directive"
32
+ require "graphql/schema/directive/deprecated"
39
33
  require "graphql/schema/directive/include"
40
34
  require "graphql/schema/directive/skip"
41
35
  require "graphql/schema/directive/feature"
36
+ require "graphql/schema/directive/flagged"
42
37
  require "graphql/schema/directive/transform"
43
38
  require "graphql/schema/type_membership"
44
39
 
@@ -55,7 +50,6 @@ module GraphQL
55
50
  # - types for exposing your application
56
51
  # - query analyzers for assessing incoming queries (including max depth & max complexity restrictions)
57
52
  # - execution strategies for running incoming queries
58
- # - middleware for interacting with execution
59
53
  #
60
54
  # Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}.
61
55
  # The schema will traverse the tree of fields & types, using those as starting points.
@@ -67,802 +61,430 @@ module GraphQL
67
61
  # Schemas can specify how queries should be executed against them.
68
62
  # `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
69
63
  # each apply to corresponding root types.
70
- #
71
- # A schema accepts a `Relay::GlobalNodeIdentification` instance for use with Relay IDs.
72
- #
64
+ # #
73
65
  # @example defining a schema
74
- # MySchema = GraphQL::Schema.define do
66
+ # class MySchema < GraphQL::Schema
75
67
  # query QueryType
76
- # middleware PermissionMiddleware
77
- # rescue_from(ActiveRecord::RecordNotFound) { "Not found" }
78
68
  # # If types are only connected by way of interfaces, they must be added here
79
69
  # orphan_types ImageType, AudioType
80
70
  # end
81
71
  #
82
72
  class Schema
83
- extend Forwardable
84
- extend GraphQL::Schema::Member::AcceptsDefinition
85
- include GraphQL::Define::InstanceDefinable
73
+ extend GraphQL::Schema::Member::HasAstNode
86
74
  extend GraphQL::Schema::FindInheritedValue
87
75
 
88
- accepts_definitions \
89
- :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
90
- :max_depth, :max_complexity, :default_max_page_size,
91
- :orphan_types, :resolve_type, :type_error, :parse_error,
92
- :error_bubbling,
93
- :raise_definition_error,
94
- :object_from_id, :id_from_object,
95
- :default_mask,
96
- :cursor_encoder,
97
- # If these are given as classes, normalize them. Accept `nil` when building from string.
98
- query: ->(schema, t) { schema.query = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
99
- mutation: ->(schema, t) { schema.mutation = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
100
- subscription: ->(schema, t) { schema.subscription = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
101
- disable_introspection_entry_points: ->(schema) { schema.disable_introspection_entry_points = true },
102
- disable_schema_introspection_entry_point: ->(schema) { schema.disable_schema_introspection_entry_point = true },
103
- disable_type_introspection_entry_point: ->(schema) { schema.disable_type_introspection_entry_point = true },
104
- directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.name] = d; m } },
105
- directive: ->(schema, directive) { schema.directives[directive.graphql_name] = directive },
106
- instrument: ->(schema, type, instrumenter, after_built_ins: false) {
107
- if type == :field && after_built_ins
108
- type = :field_after_built_ins
109
- end
110
- schema.instrumenters[type] << instrumenter
111
- },
112
- query_analyzer: ->(schema, analyzer) {
113
- if analyzer == GraphQL::Authorization::Analyzer
114
- warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
115
- end
116
- schema.query_analyzers << analyzer
117
- },
118
- multiplex_analyzer: ->(schema, analyzer) { schema.multiplex_analyzers << analyzer },
119
- middleware: ->(schema, middleware) { schema.middleware << middleware },
120
- lazy_resolve: ->(schema, lazy_class, lazy_value_method) { schema.lazy_methods.set(lazy_class, lazy_value_method) },
121
- rescue_from: ->(schema, err_class, &block) { schema.rescue_from(err_class, &block) },
122
- tracer: ->(schema, tracer) { schema.tracers.push(tracer) }
123
-
124
- ensure_defined :introspection_system
125
-
126
- attr_accessor \
127
- :query, :mutation, :subscription,
128
- :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
129
- :max_depth, :max_complexity, :default_max_page_size,
130
- :orphan_types, :directives,
131
- :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
132
- :cursor_encoder,
133
- :ast_node,
134
- :raise_definition_error,
135
- :introspection_namespace,
136
- :analysis_engine
137
-
138
- # [Boolean] True if this object bubbles validation errors up from a field into its parent InputObject, if there is one.
139
- attr_accessor :error_bubbling
140
-
141
- # Single, long-lived instance of the provided subscriptions class, if there is one.
142
- # @return [GraphQL::Subscriptions]
143
- attr_accessor :subscriptions
144
-
145
- # @return [MiddlewareChain] MiddlewareChain which is applied to fields during execution
146
- attr_accessor :middleware
147
-
148
- # @return [<#call(member, ctx)>] A callable for filtering members of the schema
149
- # @see {Query.new} for query-specific filters with `except:`
150
- attr_accessor :default_mask
151
-
152
- # @see {GraphQL::Query::Context} The parent class of these classes
153
- # @return [Class] Instantiated for each query
154
- attr_accessor :context_class
155
-
156
- # [Boolean] True if this object disables the introspection entry point fields
157
- attr_accessor :disable_introspection_entry_points
158
-
159
- # [Boolean] True if this object disables the __schema introspection entry point field
160
- attr_accessor :disable_schema_introspection_entry_point
161
-
162
- # [Boolean] True if this object disables the __type introspection entry point field
163
- attr_accessor :disable_type_introspection_entry_point
164
-
165
- class << self
166
- attr_writer :default_execution_strategy
167
- end
168
-
169
- def default_filter
170
- GraphQL::Filter.new(except: default_mask)
171
- end
172
-
173
- # @return [Array<#trace(key, data)>] Tracers applied to every query
174
- # @see {Query#tracers} for query-specific tracers
175
- attr_reader :tracers
176
-
177
- DYNAMIC_FIELDS = ["__type", "__typename", "__schema"].freeze
178
- EMPTY_ARRAY = [].freeze
179
- EMPTY_HASH = {}.freeze
180
-
181
- attr_reader :static_validator, :object_from_id_proc, :id_from_object_proc, :resolve_type_proc
182
-
183
- def initialize
184
- @tracers = []
185
- @definition_error = nil
186
- @orphan_types = []
187
- @directives = self.class.default_directives
188
- @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
189
- @middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
190
- @query_analyzers = []
191
- @multiplex_analyzers = []
192
- @resolve_type_proc = nil
193
- @object_from_id_proc = nil
194
- @id_from_object_proc = nil
195
- @type_error_proc = DefaultTypeError
196
- @parse_error_proc = DefaultParseError
197
- @instrumenters = Hash.new { |h, k| h[k] = [] }
198
- @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
199
- @lazy_methods.set(GraphQL::Execution::Lazy, :value)
200
- @cursor_encoder = Base64Encoder
201
- # Default to the built-in execution strategy:
202
- @analysis_engine = GraphQL::Analysis
203
- @query_execution_strategy = self.class.default_execution_strategy
204
- @mutation_execution_strategy = self.class.default_execution_strategy
205
- @subscription_execution_strategy = self.class.default_execution_strategy
206
- @default_mask = GraphQL::Schema::NullMask
207
- @rebuilding_artifacts = false
208
- @context_class = GraphQL::Query::Context
209
- @introspection_namespace = nil
210
- @introspection_system = nil
211
- @interpreter = false
212
- @error_bubbling = false
213
- @disable_introspection_entry_points = false
214
- @disable_schema_introspection_entry_point = false
215
- @disable_type_introspection_entry_point = false
216
- end
217
-
218
- # @return [Boolean] True if using the new {GraphQL::Execution::Interpreter}
219
- def interpreter?
220
- @interpreter
76
+ class DuplicateTypeNamesError < GraphQL::Error
77
+ def initialize(type_name:, first_definition:, second_definition:, path:)
78
+ super("Multiple definitions for `#{type_name}`. Previously found #{first_definition.inspect} (#{first_definition.class}), then found #{second_definition.inspect} (#{second_definition.class}) at #{path.join(".")}")
79
+ end
221
80
  end
222
81
 
223
- # @api private
224
- attr_writer :interpreter
82
+ class DuplicateNamesError < GraphQL::Error; end
225
83
 
226
- def inspect
227
- "#<#{self.class.name} ...>"
84
+ class UnresolvedLateBoundTypeError < GraphQL::Error
85
+ attr_reader :type
86
+ def initialize(type:)
87
+ @type = type
88
+ super("Late bound type was never found: #{type.inspect}")
89
+ end
228
90
  end
229
91
 
230
- def initialize_copy(other)
231
- super
232
- @orphan_types = other.orphan_types.dup
233
- @directives = other.directives.dup
234
- @static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
235
- @middleware = other.middleware.dup
236
- @query_analyzers = other.query_analyzers.dup
237
- @multiplex_analyzers = other.multiplex_analyzers.dup
238
- @tracers = other.tracers.dup
239
- @possible_types = GraphQL::Schema::PossibleTypes.new(self)
240
-
241
- @lazy_methods = other.lazy_methods.dup
92
+ # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
93
+ class InvalidDocumentError < Error; end;
242
94
 
243
- @instrumenters = Hash.new { |h, k| h[k] = [] }
244
- other.instrumenters.each do |key, insts|
245
- @instrumenters[key].concat(insts)
95
+ class << self
96
+ # Create schema with the result of an introspection query.
97
+ # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY}
98
+ # @return [Class<GraphQL::Schema>] the schema described by `input`
99
+ def from_introspection(introspection_result)
100
+ GraphQL::Schema::Loader.load(introspection_result)
101
+ end
102
+
103
+ # Create schema from an IDL schema or file containing an IDL definition.
104
+ # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
105
+ # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
106
+ # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
107
+ # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
108
+ # @return [Class] the schema described by `document`
109
+ def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
110
+ # If the file ends in `.graphql`, treat it like a filepath
111
+ if definition_or_path.end_with?(".graphql")
112
+ GraphQL::Schema::BuildFromDefinition.from_definition_path(
113
+ definition_or_path,
114
+ default_resolve: default_resolve,
115
+ parser: parser,
116
+ using: using,
117
+ )
118
+ else
119
+ GraphQL::Schema::BuildFromDefinition.from_definition(
120
+ definition_or_path,
121
+ default_resolve: default_resolve,
122
+ parser: parser,
123
+ using: using,
124
+ )
125
+ end
246
126
  end
247
127
 
248
- if other.rescues?
249
- @rescue_middleware = other.rescue_middleware
128
+ def deprecated_graphql_definition
129
+ graphql_definition(silence_deprecation_warning: true)
250
130
  end
251
131
 
252
- # This will be rebuilt when it's requested
253
- # or during a later `define` call
254
- @types = nil
255
- @introspection_system = nil
256
- end
132
+ # @return [GraphQL::Subscriptions]
133
+ attr_accessor :subscriptions
257
134
 
258
- def rescue_from(*args, &block)
259
- rescue_middleware.rescue_from(*args, &block)
260
- end
261
-
262
- def remove_handler(*args, &block)
263
- rescue_middleware.remove_handler(*args, &block)
264
- end
265
-
266
- def using_ast_analysis?
267
- @analysis_engine == GraphQL::Analysis::AST
268
- end
269
-
270
- # For forwards-compatibility with Schema classes
271
- alias :graphql_definition :itself
272
-
273
- # Validate a query string according to this schema.
274
- # @param string_or_document [String, GraphQL::Language::Nodes::Document]
275
- # @return [Array<GraphQL::StaticValidation::Error >]
276
- def validate(string_or_document, rules: nil, context: nil)
277
- doc = if string_or_document.is_a?(String)
278
- GraphQL.parse(string_or_document)
279
- else
280
- string_or_document
281
- end
282
- query = GraphQL::Query.new(self, document: doc, context: context)
283
- validator_opts = { schema: self }
284
- rules && (validator_opts[:rules] = rules)
285
- validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
286
- res = validator.validate(query)
287
- res[:errors]
288
- end
289
-
290
- def define(**kwargs, &block)
291
- super
292
- ensure_defined
293
- # Assert that all necessary configs are present:
294
- validation_error = Validation.validate(self)
295
- validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error)
296
- rebuild_artifacts
297
-
298
- @definition_error = nil
299
- nil
300
- rescue StandardError => err
301
- if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError)
302
- raise
303
- else
304
- # Raise this error _later_ to avoid messing with Rails constant loading
305
- @definition_error = err
306
- end
307
- nil
308
- end
309
-
310
- # Attach `instrumenter` to this schema for instrumenting events of `instrumentation_type`.
311
- # @param instrumentation_type [Symbol]
312
- # @param instrumenter
313
- # @return [void]
314
- def instrument(instrumentation_type, instrumenter)
315
- @instrumenters[instrumentation_type] << instrumenter
316
- if instrumentation_type == :field
317
- rebuild_artifacts
135
+ # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
136
+ # @see {#as_json}
137
+ # @return [String]
138
+ def to_json(**args)
139
+ JSON.pretty_generate(as_json(**args))
318
140
  end
319
- end
320
141
 
321
- # @return [Array<GraphQL::BaseType>] The root types of this schema
322
- def root_types
323
- @root_types ||= begin
324
- rebuild_artifacts
325
- @root_types
142
+ # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
143
+ # @param context [Hash]
144
+ # @param only [<#call(member, ctx)>]
145
+ # @param except [<#call(member, ctx)>]
146
+ # @return [Hash] GraphQL result
147
+ def as_json(only: nil, except: nil, context: {})
148
+ execute(Introspection.query(include_deprecated_args: true), only: only, except: except, context: context).to_h
326
149
  end
327
- end
328
150
 
329
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
330
- # @return [GraphQL::Schema::TypeMap] `{ name => type }` pairs of types in this schema
331
- def types
332
- @types ||= begin
333
- rebuild_artifacts
334
- @types
151
+ # Return the GraphQL IDL for the schema
152
+ # @param context [Hash]
153
+ # @param only [<#call(member, ctx)>]
154
+ # @param except [<#call(member, ctx)>]
155
+ # @return [String]
156
+ def to_definition(only: nil, except: nil, context: {})
157
+ GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
335
158
  end
336
- end
337
159
 
338
- # @api private
339
- def introspection_system
340
- @introspection_system ||= begin
341
- rebuild_artifacts
342
- @introspection_system
160
+ # Return the GraphQL::Language::Document IDL AST for the schema
161
+ # @return [GraphQL::Language::Document]
162
+ def to_document
163
+ GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
343
164
  end
344
- end
345
-
346
- # Returns a list of Arguments and Fields referencing a certain type
347
- # @param type_name [String]
348
- # @return [Hash]
349
- def references_to(type_name)
350
- rebuild_artifacts unless defined?(@type_reference_map)
351
- @type_reference_map.fetch(type_name, [])
352
- end
353
-
354
- # Returns a list of Union types in which a type is a member
355
- # @param type [GraphQL::ObjectType]
356
- # @return [Array<GraphQL::UnionType>] list of union types of which the type is a member
357
- def union_memberships(type)
358
- rebuild_artifacts unless defined?(@union_memberships)
359
- @union_memberships.fetch(type.name, [])
360
- end
361
165
 
362
- # Execute a query on itself. Raises an error if the schema definition is invalid.
363
- # @see {Query#initialize} for arguments.
364
- # @return [Hash] query result, ready to be serialized as JSON
365
- def execute(query_str = nil, **kwargs)
366
- if query_str
367
- kwargs[:query] = query_str
368
- end
369
- # Some of the query context _should_ be passed to the multiplex, too
370
- multiplex_context = if (ctx = kwargs[:context])
371
- {
372
- backtrace: ctx[:backtrace],
373
- tracers: ctx[:tracers],
374
- }
375
- else
376
- {}
377
- end
378
- # Since we're running one query, don't run a multiplex-level complexity analyzer
379
- all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
380
- all_results[0]
381
- end
382
-
383
- # Execute several queries on itself. Raises an error if the schema definition is invalid.
384
- # @example Run several queries at once
385
- # context = { ... }
386
- # queries = [
387
- # { query: params[:query_1], variables: params[:variables_1], context: context },
388
- # { query: params[:query_2], variables: params[:variables_2], context: context },
389
- # ]
390
- # results = MySchema.multiplex(queries)
391
- # render json: {
392
- # result_1: results[0],
393
- # result_2: results[1],
394
- # }
395
- #
396
- # @see {Query#initialize} for query keyword arguments
397
- # @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
398
- # @param queries [Array<Hash>] Keyword arguments for each query
399
- # @param context [Hash] Multiplex-level context
400
- # @return [Array<Hash>] One result for each query in the input
401
- def multiplex(queries, **kwargs)
402
- with_definition_error_check {
403
- GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
404
- }
405
- end
406
-
407
- # Search for a schema member using a string path
408
- # @example Finding a Field
409
- # Schema.find("Ensemble.musicians")
410
- #
411
- # @see {GraphQL::Schema::Finder} for more examples
412
- # @param path [String] A dot-separated path to the member
413
- # @raise [Schema::Finder::MemberNotFoundError] if path could not be found
414
- # @return [GraphQL::BaseType, GraphQL::Field, GraphQL::Argument, GraphQL::Directive] A GraphQL Schema Member
415
- def find(path)
416
- rebuild_artifacts unless defined?(@finder)
417
- @find_cache[path] ||= @finder.find(path)
418
- end
419
-
420
- # Resolve field named `field_name` for type `parent_type`.
421
- # Handles dynamic fields `__typename`, `__type` and `__schema`, too
422
- # @param parent_type [String, GraphQL::BaseType]
423
- # @param field_name [String]
424
- # @return [GraphQL::Field, nil] The field named `field_name` on `parent_type`
425
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
426
- def get_field(parent_type, field_name)
427
- with_definition_error_check do
428
- parent_type_name = case parent_type
429
- when GraphQL::BaseType
430
- parent_type.name
431
- when String
432
- parent_type
166
+ # @return [String, nil]
167
+ def description(new_description = nil)
168
+ if new_description
169
+ @description = new_description
170
+ elsif defined?(@description)
171
+ @description
433
172
  else
434
- raise "Unexpected parent_type: #{parent_type}"
435
- end
436
-
437
- defined_field = @instrumented_field_map[parent_type_name][field_name]
438
- if defined_field
439
- defined_field
440
- elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
441
- entry_point_field
442
- elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
443
- dynamic_field
444
- else
445
- nil
173
+ find_inherited_value(:description, nil)
446
174
  end
447
175
  end
448
- end
449
-
450
- # Fields for this type, after instrumentation is applied
451
- # @return [Hash<String, GraphQL::Field>]
452
- def get_fields(type)
453
- @instrumented_field_map[type.graphql_name]
454
- end
455
-
456
- def type_from_ast(ast_node)
457
- GraphQL::Schema::TypeExpression.build_type(self.types, ast_node)
458
- end
459
176
 
460
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
461
- # @param type_defn [GraphQL::InterfaceType, GraphQL::UnionType] the type whose members you want to retrieve
462
- # @param context [GraphQL::Query::Context] The context for the current query
463
- # @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
464
- def possible_types(type_defn, context = GraphQL::Query::NullContext)
465
- @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
466
- @possible_types.possible_types(type_defn, context)
467
- end
468
-
469
- # @see [GraphQL::Schema::Warden] Resticted access to root types
470
- # @return [GraphQL::ObjectType, nil]
471
- def root_type_for_operation(operation)
472
- case operation
473
- when "query"
474
- query
475
- when "mutation"
476
- mutation
477
- when "subscription"
478
- subscription
479
- else
480
- raise ArgumentError, "unknown operation type: #{operation}"
177
+ def find(path)
178
+ if !@finder
179
+ @find_cache = {}
180
+ @finder ||= GraphQL::Schema::Finder.new(self)
181
+ end
182
+ @find_cache[path] ||= @finder.find(path)
481
183
  end
482
- end
483
184
 
484
- def execution_strategy_for_operation(operation)
485
- case operation
486
- when "query"
487
- query_execution_strategy
488
- when "mutation"
489
- mutation_execution_strategy
490
- when "subscription"
491
- subscription_execution_strategy
492
- else
493
- raise ArgumentError, "unknown operation type: #{operation}"
185
+ def default_filter
186
+ GraphQL::Filter.new(except: default_mask)
494
187
  end
495
- end
496
188
 
497
- # Determine the GraphQL type for a given object.
498
- # This is required for unions and interfaces (including Relay's `Node` interface)
499
- # @see [GraphQL::Schema::Warden] Restricted access to members of a schema
500
- # @param type [GraphQL::UnionType, GraphQL:InterfaceType] the abstract type which is being resolved
501
- # @param object [Any] An application object which GraphQL is currently resolving on
502
- # @param ctx [GraphQL::Query::Context] The context for the current query
503
- # @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL
504
- def resolve_type(type, object, ctx = :__undefined__)
505
- check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
506
- if @resolve_type_proc.nil?
507
- raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
189
+ def default_mask(new_mask = nil)
190
+ if new_mask
191
+ @own_default_mask = new_mask
192
+ else
193
+ @own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
508
194
  end
509
- @resolve_type_proc.call(ok_type, ok_object, ok_ctx)
510
- end
511
- end
512
-
513
- # This is a compatibility hack so that instance-level and class-level
514
- # methods can get correctness checks without calling one another
515
- # @api private
516
- def check_resolved_type(type, object, ctx = :__undefined__)
517
- if ctx == :__undefined__
518
- # Old method signature
519
- ctx = object
520
- object = type
521
- type = nil
522
195
  end
523
196
 
524
- if object.is_a?(GraphQL::Schema::Object)
525
- object = object.object
197
+ def static_validator
198
+ GraphQL::StaticValidation::Validator.new(schema: self)
526
199
  end
527
200
 
528
- if type.respond_to?(:graphql_definition)
529
- type = type.graphql_definition
201
+ def use(plugin, **kwargs)
202
+ if kwargs.any?
203
+ plugin.use(self, **kwargs)
204
+ else
205
+ plugin.use(self)
206
+ end
207
+ own_plugins << [plugin, kwargs]
530
208
  end
531
209
 
532
- # Prefer a type-local function; fall back to the schema-level function
533
- type_proc = type && type.resolve_type_proc
534
- type_result = if type_proc
535
- type_proc.call(object, ctx)
536
- else
537
- yield(type, object, ctx)
210
+ def plugins
211
+ find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
538
212
  end
539
213
 
540
- if type_result.nil?
541
- nil
542
- else
543
- after_lazy(type_result) do |resolved_type_result|
544
- if resolved_type_result.respond_to?(:graphql_definition)
545
- resolved_type_result = resolved_type_result.graphql_definition
546
- end
547
- if !resolved_type_result.is_a?(GraphQL::BaseType)
548
- type_str = "#{resolved_type_result} (#{resolved_type_result.class.name})"
549
- raise "resolve_type(#{object}) returned #{type_str}, but it should return a GraphQL type"
214
+ # Build a map of `{ name => type }` and return it
215
+ # @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
216
+ # @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
217
+ def types(context = GraphQL::Query::NullContext)
218
+ all_types = non_introspection_types.merge(introspection_system.types)
219
+ visible_types = {}
220
+ all_types.each do |k, v|
221
+ visible_types[k] =if v.is_a?(Array)
222
+ visible_t = nil
223
+ v.each do |t|
224
+ if t.visible?(context)
225
+ if visible_t.nil?
226
+ visible_t = t
227
+ else
228
+ raise DuplicateNamesError, "Found two visible type definitions for `#{k}`: #{visible_t.inspect}, #{t.inspect}"
229
+ end
230
+ end
231
+ end
232
+ visible_t
550
233
  else
551
- resolved_type_result
234
+ v
552
235
  end
553
236
  end
237
+ visible_types
554
238
  end
555
- end
556
239
 
557
- def resolve_type=(new_resolve_type_proc)
558
- callable = GraphQL::BackwardsCompatibility.wrap_arity(new_resolve_type_proc, from: 2, to: 3, last: true, name: "Schema#resolve_type(type, obj, ctx)")
559
- @resolve_type_proc = callable
560
- end
240
+ # @param type_name [String]
241
+ # @return [Module, nil] A type, or nil if there's no type called `type_name`
242
+ def get_type(type_name, context = GraphQL::Query::NullContext)
243
+ local_entry = own_types[type_name]
244
+ type_defn = case local_entry
245
+ when nil
246
+ nil
247
+ when Array
248
+ visible_t = nil
249
+ warden = Warden.from_context(context)
250
+ local_entry.each do |t|
251
+ if warden.visible_type?(t, context)
252
+ if visible_t.nil?
253
+ visible_t = t
254
+ else
255
+ raise DuplicateNamesError, "Found two visible type definitions for `#{type_name}`: #{visible_t.inspect}, #{t.inspect}"
256
+ end
257
+ end
258
+ end
259
+ visible_t
260
+ when Module
261
+ local_entry
262
+ else
263
+ raise "Invariant: unexpected own_types[#{type_name.inspect}]: #{local_entry.inspect}"
264
+ end
561
265
 
562
- # Fetch an application object by its unique id
563
- # @param id [String] A unique identifier, provided previously by this GraphQL schema
564
- # @param ctx [GraphQL::Query::Context] The context for the current query
565
- # @return [Any] The application object identified by `id`
566
- def object_from_id(id, ctx)
567
- if @object_from_id_proc.nil?
568
- raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
569
- else
570
- @object_from_id_proc.call(id, ctx)
266
+ type_defn ||
267
+ introspection_system.types[type_name] || # todo context-specific introspection?
268
+ (superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
571
269
  end
572
- end
573
-
574
- # @param new_proc [#call] A new callable for fetching objects by ID
575
- def object_from_id=(new_proc)
576
- @object_from_id_proc = new_proc
577
- end
578
-
579
- # When we encounter a type error during query execution, we call this hook.
580
- #
581
- # You can use this hook to write a log entry,
582
- # add a {GraphQL::ExecutionError} to the response (with `ctx.add_error`)
583
- # or raise an exception and halt query execution.
584
- #
585
- # @example A `nil` is encountered by a non-null field
586
- # type_error ->(err, query_ctx) {
587
- # err.is_a?(GraphQL::InvalidNullError) # => true
588
- # }
589
- #
590
- # @example An object doesn't resolve to one of a {UnionType}'s members
591
- # type_error ->(err, query_ctx) {
592
- # err.is_a?(GraphQL::UnresolvedTypeError) # => true
593
- # }
594
- #
595
- # @see {DefaultTypeError} is the default behavior.
596
- # @param err [GraphQL::TypeError] The error encountered during execution
597
- # @param ctx [GraphQL::Query::Context] The context for the field where the error occurred
598
- # @return void
599
- def type_error(err, ctx)
600
- @type_error_proc.call(err, ctx)
601
- end
602
-
603
- # @param new_proc [#call] A new callable for handling type errors during execution
604
- def type_error=(new_proc)
605
- @type_error_proc = new_proc
606
- end
607
-
608
- # Can't delegate to `class`
609
- alias :_schema_class :class
610
- def_delegators :_schema_class, :visible?, :accessible?, :authorized?, :unauthorized_object, :unauthorized_field, :inaccessible_fields
611
- def_delegators :_schema_class, :directive
612
- def_delegators :_schema_class, :error_handler
613
-
614
- # A function to call when {#execute} receives an invalid query string
615
- #
616
- # @see {DefaultParseError} is the default behavior.
617
- # @param err [GraphQL::ParseError] The error encountered during parsing
618
- # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
619
- # @return void
620
- def parse_error(err, ctx)
621
- @parse_error_proc.call(err, ctx)
622
- end
623
270
 
624
- # @param new_proc [#call] A new callable for handling parse errors during execution
625
- def parse_error=(new_proc)
626
- @parse_error_proc = new_proc
627
- end
271
+ # @api private
272
+ attr_writer :connections
628
273
 
629
- # Get a unique identifier from this object
630
- # @param object [Any] An application object
631
- # @param type [GraphQL::BaseType] The current type definition
632
- # @param ctx [GraphQL::Query::Context] the context for the current query
633
- # @return [String] a unique identifier for `object` which clients can use to refetch it
634
- def id_from_object(object, type, ctx)
635
- if @id_from_object_proc.nil?
636
- raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
637
- else
638
- @id_from_object_proc.call(object, type, ctx)
274
+ # @return [GraphQL::Pagination::Connections] if installed
275
+ def connections
276
+ if defined?(@connections)
277
+ @connections
278
+ else
279
+ inherited_connections = find_inherited_value(:connections, nil)
280
+ # This schema is part of an inheritance chain which is using new connections,
281
+ # make a new instance, so we don't pollute the upstream one.
282
+ if inherited_connections
283
+ @connections = Pagination::Connections.new(schema: self)
284
+ else
285
+ nil
286
+ end
287
+ end
639
288
  end
640
- end
641
-
642
- # @param new_proc [#call] A new callable for generating unique IDs
643
- def id_from_object=(new_proc)
644
- @id_from_object_proc = new_proc
645
- end
646
289
 
647
- # Create schema with the result of an introspection query.
648
- # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY}
649
- # @return [GraphQL::Schema] the schema described by `input`
650
- def self.from_introspection(introspection_result)
651
- GraphQL::Schema::Loader.load(introspection_result)
652
- end
653
-
654
- # Create schema from an IDL schema or file containing an IDL definition.
655
- # @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
656
- # @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
657
- # @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
658
- # @return [GraphQL::Schema] the schema described by `document`
659
- def self.from_definition(definition_or_path, default_resolve: BuildFromDefinition::DefaultResolve, parser: BuildFromDefinition::DefaultParser)
660
- # If the file ends in `.graphql`, treat it like a filepath
661
- definition = if definition_or_path.end_with?(".graphql")
662
- File.read(definition_or_path)
663
- else
664
- definition_or_path
665
- end
666
- GraphQL::Schema::BuildFromDefinition.from_definition(definition, default_resolve: default_resolve, parser: parser)
667
- end
668
-
669
- # Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
670
- class InvalidDocumentError < Error; end;
671
-
672
- # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.
673
- def lazy_method_name(obj)
674
- @lazy_methods.get(obj)
675
- end
676
-
677
- # @return [Boolean] True if this object should be lazily resolved
678
- def lazy?(obj)
679
- !!lazy_method_name(obj)
680
- end
681
-
682
- # Return the GraphQL IDL for the schema
683
- # @param context [Hash]
684
- # @param only [<#call(member, ctx)>]
685
- # @param except [<#call(member, ctx)>]
686
- # @return [String]
687
- def to_definition(only: nil, except: nil, context: {})
688
- GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
689
- end
690
-
691
- # Return the GraphQL::Language::Document IDL AST for the schema
692
- # @param context [Hash]
693
- # @param only [<#call(member, ctx)>]
694
- # @param except [<#call(member, ctx)>]
695
- # @return [GraphQL::Language::Document]
696
- def to_document(only: nil, except: nil, context: {})
697
- GraphQL::Language::DocumentFromSchemaDefinition.new(self, only: only, except: except, context: context).document
698
- end
699
-
700
- # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
701
- # @param context [Hash]
702
- # @param only [<#call(member, ctx)>]
703
- # @param except [<#call(member, ctx)>]
704
- # @return [Hash] GraphQL result
705
- def as_json(only: nil, except: nil, context: {})
706
- execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
707
- end
708
-
709
- # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
710
- # @see {#as_json}
711
- # @return [String]
712
- def to_json(*args)
713
- JSON.pretty_generate(as_json(*args))
714
- end
715
-
716
- class << self
717
- extend Forwardable
718
- # For compatibility, these methods all:
719
- # - Cause the Schema instance to be created, if it hasn't been created yet
720
- # - Delegate to that instance
721
- # Eventually, the methods will be moved into this class, removing the need for the singleton.
722
- def_delegators :graphql_definition,
723
- # Schema structure
724
- :as_json, :to_json, :to_document, :to_definition, :ast_node,
725
- # Execution
726
- :execute, :multiplex,
727
- :static_validator, :introspection_system,
728
- :query_analyzers, :tracers, :instrumenters,
729
- :execution_strategy_for_operation,
730
- :validate, :multiplex_analyzers, :lazy?, :lazy_method_name, :after_lazy, :sync_lazy,
731
- # Configuration
732
- :analysis_engine, :analysis_engine=, :using_ast_analysis?, :interpreter?,
733
- :max_complexity=, :max_depth=,
734
- :error_bubbling=,
735
- :metadata,
736
- :default_mask,
737
- :default_filter, :redefine,
738
- :id_from_object_proc, :object_from_id_proc,
739
- :id_from_object=, :object_from_id=,
740
- :remove_handler,
741
- # Members
742
- :types, :get_fields, :find,
743
- :root_type_for_operation,
744
- :subscriptions,
745
- :union_memberships,
746
- :get_field, :root_types, :references_to, :type_from_ast,
747
- :possible_types,
748
- :disable_introspection_entry_points=,
749
- :disable_schema_introspection_entry_point=,
750
- :disable_type_introspection_entry_point=
751
-
752
- def graphql_definition
753
- @graphql_definition ||= to_graphql
754
- end
755
-
756
- def use(plugin, **options)
757
- own_plugins << [plugin, options]
290
+ def new_connections?
291
+ !!connections
758
292
  end
759
293
 
760
- def plugins
761
- find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
294
+ def query(new_query_object = nil)
295
+ if new_query_object
296
+ if @query_object
297
+ raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
298
+ else
299
+ @query_object = new_query_object
300
+ add_type_and_traverse(new_query_object, root: true)
301
+ nil
302
+ end
303
+ else
304
+ @query_object || find_inherited_value(:query)
305
+ end
762
306
  end
763
307
 
764
- def to_graphql
765
- schema_defn = self.new
766
- schema_defn.raise_definition_error = true
767
- schema_defn.query = query
768
- schema_defn.mutation = mutation
769
- schema_defn.subscription = subscription
770
- schema_defn.max_complexity = max_complexity
771
- schema_defn.error_bubbling = error_bubbling
772
- schema_defn.max_depth = max_depth
773
- schema_defn.default_max_page_size = default_max_page_size
774
- schema_defn.orphan_types = orphan_types
775
- schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
776
- schema_defn.disable_schema_introspection_entry_point = disable_schema_introspection_entry_point?
777
- schema_defn.disable_type_introspection_entry_point = disable_type_introspection_entry_point?
778
-
779
- prepped_dirs = {}
780
- directives.each { |k, v| prepped_dirs[k] = v.graphql_definition}
781
- schema_defn.directives = prepped_dirs
782
- schema_defn.introspection_namespace = introspection
783
- schema_defn.resolve_type = method(:resolve_type)
784
- schema_defn.object_from_id = method(:object_from_id)
785
- schema_defn.id_from_object = method(:id_from_object)
786
- schema_defn.type_error = method(:type_error)
787
- schema_defn.context_class = context_class
788
- schema_defn.cursor_encoder = cursor_encoder
789
- schema_defn.tracers.concat(tracers)
790
- schema_defn.query_analyzers.concat(query_analyzers)
791
-
792
- schema_defn.middleware.concat(all_middleware)
793
- schema_defn.multiplex_analyzers.concat(multiplex_analyzers)
794
- schema_defn.query_execution_strategy = query_execution_strategy
795
- schema_defn.mutation_execution_strategy = mutation_execution_strategy
796
- schema_defn.subscription_execution_strategy = subscription_execution_strategy
797
- all_instrumenters.each do |step, insts|
798
- insts.each do |inst|
799
- schema_defn.instrumenters[step] << inst
308
+ def mutation(new_mutation_object = nil)
309
+ if new_mutation_object
310
+ if @mutation_object
311
+ raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
312
+ else
313
+ @mutation_object = new_mutation_object
314
+ add_type_and_traverse(new_mutation_object, root: true)
315
+ nil
800
316
  end
317
+ else
318
+ @mutation_object || find_inherited_value(:mutation)
801
319
  end
802
- lazy_classes.each do |lazy_class, value_method|
803
- schema_defn.lazy_methods.set(lazy_class, value_method)
320
+ end
321
+
322
+ def subscription(new_subscription_object = nil)
323
+ if new_subscription_object
324
+ if @subscription_object
325
+ raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
326
+ else
327
+ @subscription_object = new_subscription_object
328
+ add_subscription_extension_if_necessary
329
+ add_type_and_traverse(new_subscription_object, root: true)
330
+ nil
331
+ end
332
+ else
333
+ @subscription_object || find_inherited_value(:subscription)
804
334
  end
805
- rescues.each do |err_class, handler|
806
- schema_defn.rescue_from(err_class, &handler)
335
+ end
336
+
337
+ # @see [GraphQL::Schema::Warden] Restricted access to root types
338
+ # @return [GraphQL::ObjectType, nil]
339
+ def root_type_for_operation(operation)
340
+ case operation
341
+ when "query"
342
+ query
343
+ when "mutation"
344
+ mutation
345
+ when "subscription"
346
+ subscription
347
+ else
348
+ raise ArgumentError, "unknown operation type: #{operation}"
807
349
  end
350
+ end
808
351
 
809
- if plugins.any?
810
- schema_plugins = plugins
811
- # TODO don't depend on .define
812
- schema_defn = schema_defn.redefine do
813
- schema_plugins.each do |plugin, options|
814
- if options.any?
815
- use(plugin, **options)
816
- else
817
- use(plugin)
352
+ def root_types
353
+ @root_types
354
+ end
355
+
356
+ # @param type [Module] The type definition whose possible types you want to see
357
+ # @return [Hash<String, Module>] All possible types, if no `type` is given.
358
+ # @return [Array<Module>] Possible types for `type`, if it's given.
359
+ def possible_types(type = nil, context = GraphQL::Query::NullContext)
360
+ if type
361
+ # TODO duck-typing `.possible_types` would probably be nicer here
362
+ if type.kind.union?
363
+ type.possible_types(context: context)
364
+ else
365
+ stored_possible_types = own_possible_types[type.graphql_name]
366
+ visible_possible_types = if stored_possible_types && type.kind.interface?
367
+ stored_possible_types.select do |possible_type|
368
+ possible_type.interfaces(context).include?(type)
818
369
  end
370
+ else
371
+ stored_possible_types
819
372
  end
373
+ visible_possible_types ||
374
+ introspection_system.possible_types[type.graphql_name] ||
375
+ (
376
+ superclass.respond_to?(:possible_types) ?
377
+ superclass.possible_types(type, context) :
378
+ EMPTY_ARRAY
379
+ )
820
380
  end
381
+ else
382
+ find_inherited_value(:possible_types, EMPTY_HASH)
383
+ .merge(own_possible_types)
384
+ .merge(introspection_system.possible_types)
821
385
  end
822
- # Do this after `plugins` since Interpreter is a plugin
823
- if schema_defn.query_execution_strategy != GraphQL::Execution::Interpreter
824
- schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
386
+ end
387
+
388
+ def union_memberships(type = nil)
389
+ if type
390
+ own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY)
391
+ inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
392
+ own_um + inherited_um
393
+ else
394
+ joined_um = own_union_memberships.dup
395
+ find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v|
396
+ um = joined_um[k] ||= []
397
+ um.concat(v)
398
+ end
399
+ joined_um
825
400
  end
826
- schema_defn.send(:rebuild_artifacts)
401
+ end
827
402
 
828
- schema_defn
403
+ # @api private
404
+ # @see GraphQL::Dataloader
405
+ def dataloader_class
406
+ @dataloader_class || GraphQL::Dataloader::NullDataloader
829
407
  end
830
408
 
831
- def query(new_query_object = nil)
832
- if new_query_object
833
- @query_object = new_query_object
409
+ attr_writer :dataloader_class
410
+
411
+ def references_to(to_type = nil, from: nil)
412
+ @own_references_to ||= Hash.new { |h, k| h[k] = [] }
413
+ if to_type
414
+ if !to_type.is_a?(String)
415
+ to_type = to_type.graphql_name
416
+ end
417
+
418
+ if from
419
+ @own_references_to[to_type] << from
420
+ else
421
+ own_refs = @own_references_to[to_type]
422
+ inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
423
+ own_refs + inherited_refs
424
+ end
834
425
  else
835
- query_object = @query_object || find_inherited_value(:query)
836
- query_object.respond_to?(:graphql_definition) ? query_object.graphql_definition : query_object
426
+ # `@own_references_to` can be quite large for big schemas,
427
+ # and generally speaking, we won't inherit any values.
428
+ # So optimize the most common case -- don't create a duplicate Hash.
429
+ inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
430
+ if inherited_value.any?
431
+ inherited_value.merge(@own_references_to)
432
+ else
433
+ @own_references_to
434
+ end
837
435
  end
838
436
  end
839
437
 
840
- def mutation(new_mutation_object = nil)
841
- if new_mutation_object
842
- @mutation_object = new_mutation_object
438
+ def type_from_ast(ast_node, context: nil)
439
+ type_owner = context ? context.warden : self
440
+ GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
441
+ end
442
+
443
+ def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
444
+ parent_type = case type_or_name
445
+ when LateBoundType
446
+ get_type(type_or_name.name, context)
447
+ when String
448
+ get_type(type_or_name, context)
449
+ when Module
450
+ type_or_name
843
451
  else
844
- mutation_object = @mutation_object || find_inherited_value(:mutation)
845
- mutation_object.respond_to?(:graphql_definition) ? mutation_object.graphql_definition : mutation_object
452
+ raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
846
453
  end
847
- end
848
454
 
849
- def subscription(new_subscription_object = nil)
850
- if new_subscription_object
851
- @subscription_object = new_subscription_object
455
+ if parent_type.kind.fields? && (field = parent_type.get_field(field_name, context))
456
+ field
457
+ elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
458
+ entry_point_field
459
+ elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
460
+ dynamic_field
852
461
  else
853
- subscription_object = @subscription_object || find_inherited_value(:subscription)
854
- subscription_object.respond_to?(:graphql_definition) ? subscription_object.graphql_definition : subscription_object
462
+ nil
855
463
  end
856
464
  end
857
465
 
466
+ def get_fields(type, context = GraphQL::Query::NullContext)
467
+ type.fields(context)
468
+ end
469
+
858
470
  def introspection(new_introspection_namespace = nil)
859
471
  if new_introspection_namespace
860
472
  @introspection = new_introspection_namespace
473
+ # reset this cached value:
474
+ @introspection_system = nil
861
475
  else
862
476
  @introspection || find_inherited_value(:introspection)
863
477
  end
864
478
  end
865
479
 
480
+ def introspection_system
481
+ if !@introspection_system
482
+ @introspection_system = Schema::IntrospectionSystem.new(self)
483
+ @introspection_system.resolve_late_bindings
484
+ end
485
+ @introspection_system
486
+ end
487
+
866
488
  def cursor_encoder(new_encoder = nil)
867
489
  if new_encoder
868
490
  @cursor_encoder = new_encoder
@@ -902,14 +524,75 @@ module GraphQL
902
524
  end
903
525
  end
904
526
 
527
+ attr_writer :validate_timeout
528
+
529
+ def validate_timeout(new_validate_timeout = nil)
530
+ if new_validate_timeout
531
+ @validate_timeout = new_validate_timeout
532
+ elsif defined?(@validate_timeout)
533
+ @validate_timeout
534
+ else
535
+ find_inherited_value(:validate_timeout)
536
+ end
537
+ end
538
+
539
+ # Validate a query string according to this schema.
540
+ # @param string_or_document [String, GraphQL::Language::Nodes::Document]
541
+ # @return [Array<GraphQL::StaticValidation::Error >]
542
+ def validate(string_or_document, rules: nil, context: nil)
543
+ doc = if string_or_document.is_a?(String)
544
+ GraphQL.parse(string_or_document)
545
+ else
546
+ string_or_document
547
+ end
548
+ query = GraphQL::Query.new(self, document: doc, context: context)
549
+ validator_opts = { schema: self }
550
+ rules && (validator_opts[:rules] = rules)
551
+ validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
552
+ res = validator.validate(query, timeout: validate_timeout, max_errors: validate_max_errors)
553
+ res[:errors]
554
+ end
555
+
556
+ attr_writer :validate_max_errors
557
+
558
+ def validate_max_errors(new_validate_max_errors = nil)
559
+ if new_validate_max_errors
560
+ @validate_max_errors = new_validate_max_errors
561
+ elsif defined?(@validate_max_errors)
562
+ @validate_max_errors
563
+ else
564
+ find_inherited_value(:validate_max_errors)
565
+ end
566
+ end
567
+
568
+ attr_writer :max_complexity
569
+
905
570
  def max_complexity(max_complexity = nil)
906
571
  if max_complexity
907
572
  @max_complexity = max_complexity
573
+ elsif defined?(@max_complexity)
574
+ @max_complexity
908
575
  else
909
- @max_complexity || find_inherited_value(:max_complexity)
576
+ find_inherited_value(:max_complexity)
910
577
  end
911
578
  end
912
579
 
580
+ attr_writer :analysis_engine
581
+
582
+ def analysis_engine
583
+ @analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
584
+ end
585
+
586
+ def using_ast_analysis?
587
+ true
588
+ end
589
+
590
+ def interpreter?
591
+ true
592
+ end
593
+
594
+ attr_writer :interpreter
595
+
913
596
  def error_bubbling(new_error_bubbling = nil)
914
597
  if !new_error_bubbling.nil?
915
598
  @error_bubbling = new_error_bubbling
@@ -918,24 +601,36 @@ module GraphQL
918
601
  end
919
602
  end
920
603
 
604
+ attr_writer :error_bubbling
605
+
606
+ attr_writer :max_depth
607
+
921
608
  def max_depth(new_max_depth = nil)
922
609
  if new_max_depth
923
610
  @max_depth = new_max_depth
611
+ elsif defined?(@max_depth)
612
+ @max_depth
924
613
  else
925
- @max_depth || find_inherited_value(:max_depth)
614
+ find_inherited_value(:max_depth)
926
615
  end
927
616
  end
928
617
 
929
618
  def disable_introspection_entry_points
930
619
  @disable_introspection_entry_points = true
620
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
621
+ @introspection_system = nil
931
622
  end
932
623
 
933
624
  def disable_schema_introspection_entry_point
934
625
  @disable_schema_introspection_entry_point = true
626
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
627
+ @introspection_system = nil
935
628
  end
936
629
 
937
630
  def disable_type_introspection_entry_point
938
631
  @disable_type_introspection_entry_point = true
632
+ # TODO: this clears the cache made in `def types`. But this is not a great solution.
633
+ @introspection_system = nil
939
634
  end
940
635
 
941
636
  def disable_introspection_entry_points?
@@ -964,6 +659,8 @@ module GraphQL
964
659
 
965
660
  def orphan_types(*new_orphan_types)
966
661
  if new_orphan_types.any?
662
+ new_orphan_types = new_orphan_types.flatten
663
+ add_type_and_traverse(new_orphan_types, root: false)
967
664
  own_orphan_types.concat(new_orphan_types.flatten)
968
665
  end
969
666
 
@@ -974,7 +671,15 @@ module GraphQL
974
671
  if superclass <= GraphQL::Schema
975
672
  superclass.default_execution_strategy
976
673
  else
977
- @default_execution_strategy ||= GraphQL::Execution::Execute
674
+ @default_execution_strategy ||= GraphQL::Execution::Interpreter
675
+ end
676
+ end
677
+
678
+ def default_analysis_engine
679
+ if superclass <= GraphQL::Schema
680
+ superclass.default_analysis_engine
681
+ else
682
+ @default_analysis_engine ||= GraphQL::Analysis::AST
978
683
  end
979
684
  end
980
685
 
@@ -988,12 +693,31 @@ module GraphQL
988
693
 
989
694
  def rescue_from(*err_classes, &handler_block)
990
695
  err_classes.each do |err_class|
991
- own_rescues[err_class] = handler_block
696
+ error_handler.rescue_from(err_class, handler_block)
992
697
  end
993
698
  end
994
699
 
995
- def rescues
996
- find_inherited_value(:rescues, EMPTY_HASH).merge(own_rescues)
700
+ # rubocop:disable Lint/DuplicateMethods
701
+ module ResolveTypeWithType
702
+ def resolve_type(type, obj, ctx)
703
+ first_resolved_type, resolved_value = if type.is_a?(Module) && type.respond_to?(:resolve_type)
704
+ type.resolve_type(obj, ctx)
705
+ else
706
+ super
707
+ end
708
+
709
+ after_lazy(first_resolved_type) do |resolved_type|
710
+ if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind))
711
+ if resolved_value
712
+ [resolved_type, resolved_value]
713
+ else
714
+ resolved_type
715
+ end
716
+ else
717
+ raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
718
+ end
719
+ end
720
+ end
997
721
  end
998
722
 
999
723
  def resolve_type(type, obj, ctx)
@@ -1003,6 +727,15 @@ module GraphQL
1003
727
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
1004
728
  end
1005
729
  end
730
+ # rubocop:enable Lint/DuplicateMethods
731
+
732
+ def inherited(child_class)
733
+ if self == GraphQL::Schema
734
+ child_class.directives(default_directives.values)
735
+ end
736
+ child_class.singleton_class.prepend(ResolveTypeWithType)
737
+ super
738
+ end
1006
739
 
1007
740
  def object_from_id(node_id, ctx)
1008
741
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
@@ -1012,12 +745,12 @@ module GraphQL
1012
745
  raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
1013
746
  end
1014
747
 
1015
- def visible?(member, context)
1016
- call_on_type_class(member, :visible?, context, default: true)
748
+ def visible?(member, ctx)
749
+ member.visible?(ctx)
1017
750
  end
1018
751
 
1019
- def accessible?(member, context)
1020
- call_on_type_class(member, :accessible?, context, default: true)
752
+ def accessible?(member, ctx)
753
+ member.accessible?(ctx)
1021
754
  end
1022
755
 
1023
756
  # This hook is called when a client tries to access one or more
@@ -1067,49 +800,63 @@ module GraphQL
1067
800
  unauthorized_object(unauthorized_error)
1068
801
  end
1069
802
 
1070
- def type_error(type_err, ctx)
1071
- DefaultTypeError.call(type_err, ctx)
803
+ def type_error(type_error, ctx)
804
+ case type_error
805
+ when GraphQL::InvalidNullError
806
+ ctx.errors << type_error
807
+ when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
808
+ raise type_error
809
+ when GraphQL::IntegerDecodingError
810
+ nil
811
+ end
1072
812
  end
1073
813
 
1074
- attr_writer :error_handler
814
+ # A function to call when {#execute} receives an invalid query string
815
+ #
816
+ # The default is to add the error to `context.errors`
817
+ # @param err [GraphQL::ParseError] The error encountered during parsing
818
+ # @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
819
+ # @return void
820
+ def parse_error(parse_err, ctx)
821
+ ctx.errors.push(parse_err)
822
+ end
1075
823
 
1076
- # @return [GraphQL::Execution::Errors, Class<GraphQL::Execution::Errors::NullErrorHandler>]
824
+ # @return [GraphQL::Execution::Errors]
1077
825
  def error_handler
1078
- @error_handler ||= GraphQL::Execution::Errors::NullErrorHandler
826
+ @error_handler ||= GraphQL::Execution::Errors.new(self)
1079
827
  end
1080
828
 
1081
829
  def lazy_resolve(lazy_class, value_method)
1082
- lazy_classes[lazy_class] = value_method
830
+ lazy_methods.set(lazy_class, value_method)
1083
831
  end
1084
832
 
1085
833
  def instrument(instrument_step, instrumenter, options = {})
1086
- step = if instrument_step == :field && options[:after_built_ins]
1087
- :field_after_built_ins
1088
- else
1089
- instrument_step
1090
- end
1091
-
1092
- own_instrumenters[step] << instrumenter
834
+ own_instrumenters[instrument_step] << instrumenter
1093
835
  end
1094
836
 
1095
- def directives(new_directives = nil)
1096
- if new_directives
1097
- new_directives.each {|d| directive(d) }
837
+ # Add several directives at once
838
+ # @param new_directives [Class]
839
+ def directives(*new_directives)
840
+ if new_directives.any?
841
+ new_directives.flatten.each { |d| directive(d) }
1098
842
  end
1099
843
 
1100
844
  find_inherited_value(:directives, default_directives).merge(own_directives)
1101
845
  end
1102
846
 
847
+ # Attach a single directive to this schema
848
+ # @param new_directive [Class]
849
+ # @return void
1103
850
  def directive(new_directive)
1104
- own_directives[new_directive.graphql_name] = new_directive
851
+ add_type_and_traverse(new_directive, root: false)
1105
852
  end
1106
853
 
1107
854
  def default_directives
1108
- {
1109
- "include" => GraphQL::Directive::IncludeDirective,
1110
- "skip" => GraphQL::Directive::SkipDirective,
1111
- "deprecated" => GraphQL::Directive::DeprecatedDirective,
1112
- }
855
+ @default_directives ||= {
856
+ "include" => GraphQL::Schema::Directive::Include,
857
+ "skip" => GraphQL::Schema::Directive::Skip,
858
+ "deprecated" => GraphQL::Schema::Directive::Deprecated,
859
+ }.freeze
1113
860
  end
1114
861
 
1115
862
  def tracer(new_tracer)
@@ -1121,9 +868,6 @@ module GraphQL
1121
868
  end
1122
869
 
1123
870
  def query_analyzer(new_analyzer)
1124
- if new_analyzer == GraphQL::Authorization::Analyzer
1125
- warn("The Authorization query analyzer is deprecated. Authorizing at query runtime is generally a better idea.")
1126
- end
1127
871
  own_query_analyzers << new_analyzer
1128
872
  end
1129
873
 
@@ -1131,14 +875,6 @@ module GraphQL
1131
875
  find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
1132
876
  end
1133
877
 
1134
- def middleware(new_middleware = nil)
1135
- if new_middleware
1136
- own_middleware << new_middleware
1137
- else
1138
- graphql_definition.middleware
1139
- end
1140
- end
1141
-
1142
878
  def multiplex_analyzer(new_analyzer)
1143
879
  own_multiplex_analyzers << new_analyzer
1144
880
  end
@@ -1147,188 +883,248 @@ module GraphQL
1147
883
  find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers
1148
884
  end
1149
885
 
1150
- private
1151
-
1152
- def lazy_classes
1153
- @lazy_classes ||= {}
1154
- end
1155
-
1156
- def own_plugins
1157
- @own_plugins ||= []
1158
- end
1159
-
1160
- def own_rescues
1161
- @own_rescues ||= {}
886
+ def sanitized_printer(new_sanitized_printer = nil)
887
+ if new_sanitized_printer
888
+ @own_sanitized_printer = new_sanitized_printer
889
+ else
890
+ @own_sanitized_printer || GraphQL::Language::SanitizedPrinter
891
+ end
1162
892
  end
1163
893
 
1164
- def own_orphan_types
1165
- @own_orphan_types ||= []
894
+ # Execute a query on itself.
895
+ # @see {Query#initialize} for arguments.
896
+ # @return [Hash] query result, ready to be serialized as JSON
897
+ def execute(query_str = nil, **kwargs)
898
+ if query_str
899
+ kwargs[:query] = query_str
900
+ end
901
+ # Some of the query context _should_ be passed to the multiplex, too
902
+ multiplex_context = if (ctx = kwargs[:context])
903
+ {
904
+ backtrace: ctx[:backtrace],
905
+ tracers: ctx[:tracers],
906
+ }
907
+ else
908
+ {}
909
+ end
910
+ # Since we're running one query, don't run a multiplex-level complexity analyzer
911
+ all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
912
+ all_results[0]
1166
913
  end
1167
914
 
1168
- def own_directives
1169
- @own_directives ||= {}
915
+ # Execute several queries on itself, concurrently.
916
+ #
917
+ # @example Run several queries at once
918
+ # context = { ... }
919
+ # queries = [
920
+ # { query: params[:query_1], variables: params[:variables_1], context: context },
921
+ # { query: params[:query_2], variables: params[:variables_2], context: context },
922
+ # ]
923
+ # results = MySchema.multiplex(queries)
924
+ # render json: {
925
+ # result_1: results[0],
926
+ # result_2: results[1],
927
+ # }
928
+ #
929
+ # @see {Query#initialize} for query keyword arguments
930
+ # @see {Execution::Multiplex#run_all} for multiplex keyword arguments
931
+ # @param queries [Array<Hash>] Keyword arguments for each query
932
+ # @param context [Hash] Multiplex-level context
933
+ # @return [Array<Hash>] One result for each query in the input
934
+ def multiplex(queries, **kwargs)
935
+ GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
1170
936
  end
1171
937
 
1172
- def all_instrumenters
1173
- inherited_instrumenters = find_inherited_value(:all_instrumenters) || Hash.new { |h,k| h[k] = [] }
938
+ def instrumenters
939
+ inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] }
1174
940
  inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own|
1175
941
  inherited + own
1176
942
  end
1177
943
  end
1178
944
 
1179
- def own_instrumenters
1180
- @own_instrumenters ||= Hash.new { |h,k| h[k] = [] }
945
+ # @api private
946
+ def add_subscription_extension_if_necessary
947
+ if !defined?(@subscription_extension_added) && subscription && self.subscriptions
948
+ @subscription_extension_added = true
949
+ subscription.all_field_definitions.each do |field|
950
+ field.extension(Subscriptions::DefaultSubscriptionResolveExtension)
951
+ end
952
+ end
1181
953
  end
1182
954
 
1183
- def own_tracers
1184
- @own_tracers ||= []
955
+ def query_stack_error(query, err)
956
+ query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
1185
957
  end
1186
958
 
1187
- def own_query_analyzers
1188
- @defined_query_analyzers ||= []
959
+ # Call the given block at the right time, either:
960
+ # - Right away, if `value` is not registered with `lazy_resolve`
961
+ # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
962
+ # @api private
963
+ def after_lazy(value, &block)
964
+ if lazy?(value)
965
+ GraphQL::Execution::Lazy.new do
966
+ result = sync_lazy(value)
967
+ # The returned result might also be lazy, so check it, too
968
+ after_lazy(result, &block)
969
+ end
970
+ else
971
+ yield(value) if block_given?
972
+ end
1189
973
  end
1190
974
 
1191
- def all_middleware
1192
- find_inherited_value(:all_middleware, EMPTY_ARRAY) + own_middleware
975
+ # Override this method to handle lazy objects in a custom way.
976
+ # @param value [Object] an instance of a class registered with {.lazy_resolve}
977
+ # @return [Object] A GraphQL-ready (non-lazy) object
978
+ # @api private
979
+ def sync_lazy(value)
980
+ lazy_method = lazy_method_name(value)
981
+ if lazy_method
982
+ synced_value = value.public_send(lazy_method)
983
+ sync_lazy(synced_value)
984
+ else
985
+ value
986
+ end
1193
987
  end
1194
988
 
1195
- def own_middleware
1196
- @own_middleware ||= []
989
+ # @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {#lazy_resolve}.
990
+ def lazy_method_name(obj)
991
+ lazy_methods.get(obj)
1197
992
  end
1198
993
 
1199
- def own_multiplex_analyzers
1200
- @own_multiplex_analyzers ||= []
994
+ # @return [Boolean] True if this object should be lazily resolved
995
+ def lazy?(obj)
996
+ !!lazy_method_name(obj)
1201
997
  end
1202
998
 
1203
- # Given this schema member, find the class-based definition object
1204
- # whose `method_name` should be treated as an application hook
1205
- # @see {.visible?}
1206
- # @see {.accessible?}
1207
- # @see {.authorized?}
1208
- def call_on_type_class(member, method_name, *args, default:)
1209
- member = if member.respond_to?(:metadata) && member.metadata
1210
- member.metadata[:type_class] || member
999
+ # Return a lazy if any of `maybe_lazies` are lazy,
1000
+ # otherwise, call the block eagerly and return the result.
1001
+ # @param maybe_lazies [Array]
1002
+ # @api private
1003
+ def after_any_lazies(maybe_lazies)
1004
+ if maybe_lazies.any? { |l| lazy?(l) }
1005
+ GraphQL::Execution::Lazy.all(maybe_lazies).then do |result|
1006
+ yield result
1007
+ end
1211
1008
  else
1212
- member
1009
+ yield maybe_lazies
1213
1010
  end
1011
+ end
1012
+
1013
+ private
1214
1014
 
1215
- if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
1216
- member = t
1015
+ # @param t [Module, Array<Module>]
1016
+ # @return [void]
1017
+ def add_type_and_traverse(t, root:)
1018
+ if root
1019
+ @root_types ||= []
1020
+ @root_types << t
1217
1021
  end
1022
+ new_types = Array(t)
1023
+ addition = Schema::Addition.new(schema: self, own_types: own_types, new_types: new_types)
1024
+ addition.types.each do |name, types_entry| # rubocop:disable Development/ContextIsPassedCop -- build-time, not query-time
1025
+ if (prev_entry = own_types[name])
1026
+ prev_entries = case prev_entry
1027
+ when Array
1028
+ prev_entry
1029
+ when Module
1030
+ own_types[name] = [prev_entry]
1031
+ else
1032
+ raise "Invariant: unexpected prev_entry at #{name.inspect} when adding #{t.inspect}"
1033
+ end
1218
1034
 
1219
- if member.respond_to?(method_name)
1220
- member.public_send(method_name, *args)
1221
- else
1222
- default
1035
+ case types_entry
1036
+ when Array
1037
+ prev_entries.concat(types_entry)
1038
+ prev_entries.uniq! # in case any are being re-visited
1039
+ when Module
1040
+ if !prev_entries.include?(types_entry)
1041
+ prev_entries << types_entry
1042
+ end
1043
+ else
1044
+ raise "Invariant: unexpected types_entry at #{name} when adding #{t.inspect}"
1045
+ end
1046
+ else
1047
+ if types_entry.is_a?(Array)
1048
+ types_entry.uniq!
1049
+ end
1050
+ own_types[name] = types_entry
1051
+ end
1223
1052
  end
1224
- end
1225
- end
1226
1053
 
1054
+ own_possible_types.merge!(addition.possible_types) { |key, old_val, new_val| old_val + new_val }
1055
+ own_union_memberships.merge!(addition.union_memberships)
1227
1056
 
1228
- def self.inherited(child_class)
1229
- child_class.singleton_class.class_eval do
1230
- prepend(MethodWrappers)
1231
- end
1232
- end
1057
+ addition.references.each { |thing, pointers|
1058
+ pointers.each { |pointer| references_to(thing, from: pointer) }
1059
+ }
1233
1060
 
1234
- module MethodWrappers
1235
- # Wrap the user-provided resolve-type in a correctness check
1236
- def resolve_type(type, obj, ctx = :__undefined__)
1237
- graphql_definition.check_resolved_type(type, obj, ctx) do |ok_type, ok_obj, ok_ctx|
1238
- super(ok_type, ok_obj, ok_ctx)
1061
+ addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
1062
+
1063
+ addition.arguments_with_default_values.each do |arg|
1064
+ arg.validate_default_value
1239
1065
  end
1240
1066
  end
1241
- end
1242
1067
 
1243
- # Call the given block at the right time, either:
1244
- # - Right away, if `value` is not registered with `lazy_resolve`
1245
- # - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
1246
- # @api private
1247
- def after_lazy(value)
1248
- if lazy?(value)
1249
- GraphQL::Execution::Lazy.new do
1250
- result = sync_lazy(value)
1251
- # The returned result might also be lazy, so check it, too
1252
- after_lazy(result) do |final_result|
1253
- yield(final_result) if block_given?
1068
+ def lazy_methods
1069
+ if !defined?(@lazy_methods)
1070
+ if inherited_map = find_inherited_value(:lazy_methods)
1071
+ # this isn't _completely_ inherited :S (Things added after `dup` won't work)
1072
+ @lazy_methods = inherited_map.dup
1073
+ else
1074
+ @lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
1075
+ @lazy_methods.set(GraphQL::Execution::Lazy, :value)
1076
+ @lazy_methods.set(GraphQL::Dataloader::Request, :load)
1254
1077
  end
1255
1078
  end
1256
- else
1257
- yield(value) if block_given?
1079
+ @lazy_methods
1258
1080
  end
1259
- end
1260
1081
 
1261
- # Override this method to handle lazy objects in a custom way.
1262
- # @param value [Object] an instance of a class registered with {.lazy_resolve}
1263
- # @param ctx [GraphQL::Query::Context] the context for this query
1264
- # @return [Object] A GraphQL-ready (non-lazy) object
1265
- def self.sync_lazy(value)
1266
- if block_given?
1267
- # This was already hit by the instance, just give it back
1268
- yield(value)
1269
- else
1270
- # This was called directly on the class, hit the instance
1271
- # which has the lazy method map
1272
- self.graphql_definition.sync_lazy(value)
1082
+ def own_types
1083
+ @own_types ||= {}
1273
1084
  end
1274
- end
1275
1085
 
1276
- # @see Schema.sync_lazy for a hook to override
1277
- # @api private
1278
- def sync_lazy(value)
1279
- self.class.sync_lazy(value) { |v|
1280
- lazy_method = lazy_method_name(v)
1281
- if lazy_method
1282
- synced_value = value.public_send(lazy_method)
1283
- sync_lazy(synced_value)
1284
- else
1285
- v
1286
- end
1287
- }
1288
- end
1086
+ def non_introspection_types
1087
+ find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
1088
+ end
1289
1089
 
1290
- protected
1090
+ def own_plugins
1091
+ @own_plugins ||= []
1092
+ end
1291
1093
 
1292
- def rescues?
1293
- !!@rescue_middleware
1294
- end
1094
+ def own_orphan_types
1095
+ @own_orphan_types ||= []
1096
+ end
1295
1097
 
1296
- # Lazily create a middleware and add it to the schema
1297
- # (Don't add it if it's not used)
1298
- def rescue_middleware
1299
- @rescue_middleware ||= GraphQL::Schema::RescueMiddleware.new.tap { |m| middleware.insert(0, m) }
1300
- end
1098
+ def own_possible_types
1099
+ @own_possible_types ||= {}
1100
+ end
1301
1101
 
1302
- private
1303
-
1304
- def rebuild_artifacts
1305
- if @rebuilding_artifacts
1306
- raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build."
1307
- else
1308
- @rebuilding_artifacts = true
1309
- @introspection_system = Schema::IntrospectionSystem.new(self)
1310
- traversal = Traversal.new(self)
1311
- @types = traversal.type_map
1312
- @root_types = [query, mutation, subscription]
1313
- @instrumented_field_map = traversal.instrumented_field_map
1314
- @type_reference_map = traversal.type_reference_map
1315
- @union_memberships = traversal.union_memberships
1316
- @find_cache = {}
1317
- @finder = Finder.new(self)
1318
- end
1319
- ensure
1320
- @rebuilding_artifacts = false
1321
- end
1102
+ def own_union_memberships
1103
+ @own_union_memberships ||= {}
1104
+ end
1322
1105
 
1323
- class CyclicalDefinitionError < GraphQL::Error
1324
- end
1106
+ def own_directives
1107
+ @own_directives ||= {}
1108
+ end
1325
1109
 
1326
- def with_definition_error_check
1327
- if @definition_error
1328
- raise @definition_error
1329
- else
1330
- yield
1110
+ def own_instrumenters
1111
+ @own_instrumenters ||= Hash.new { |h,k| h[k] = [] }
1112
+ end
1113
+
1114
+ def own_tracers
1115
+ @own_tracers ||= []
1116
+ end
1117
+
1118
+ def own_query_analyzers
1119
+ @defined_query_analyzers ||= []
1120
+ end
1121
+
1122
+ def own_multiplex_analyzers
1123
+ @own_multiplex_analyzers ||= []
1331
1124
  end
1332
1125
  end
1126
+
1127
+ # Install these here so that subclasses will also install it.
1128
+ self.connections = GraphQL::Pagination::Connections.new(schema: self)
1333
1129
  end
1334
1130
  end