graphql 1.9.17 → 2.0.21

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