graphql 1.9.17 → 2.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

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