graphql 1.9.21 → 2.0.16

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