graphql_cody 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (444) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +5 -0
  3. data/MIT-LICENSE +20 -0
  4. data/lib/generators/graphql/core.rb +74 -0
  5. data/lib/generators/graphql/enum_generator.rb +33 -0
  6. data/lib/generators/graphql/install_generator.rb +190 -0
  7. data/lib/generators/graphql/interface_generator.rb +27 -0
  8. data/lib/generators/graphql/loader_generator.rb +21 -0
  9. data/lib/generators/graphql/mutation_generator.rb +55 -0
  10. data/lib/generators/graphql/object_generator.rb +79 -0
  11. data/lib/generators/graphql/relay.rb +63 -0
  12. data/lib/generators/graphql/relay_generator.rb +21 -0
  13. data/lib/generators/graphql/scalar_generator.rb +20 -0
  14. data/lib/generators/graphql/templates/base_argument.erb +6 -0
  15. data/lib/generators/graphql/templates/base_connection.erb +8 -0
  16. data/lib/generators/graphql/templates/base_edge.erb +8 -0
  17. data/lib/generators/graphql/templates/base_enum.erb +6 -0
  18. data/lib/generators/graphql/templates/base_field.erb +7 -0
  19. data/lib/generators/graphql/templates/base_input_object.erb +7 -0
  20. data/lib/generators/graphql/templates/base_interface.erb +9 -0
  21. data/lib/generators/graphql/templates/base_mutation.erb +10 -0
  22. data/lib/generators/graphql/templates/base_object.erb +7 -0
  23. data/lib/generators/graphql/templates/base_scalar.erb +6 -0
  24. data/lib/generators/graphql/templates/base_union.erb +6 -0
  25. data/lib/generators/graphql/templates/enum.erb +7 -0
  26. data/lib/generators/graphql/templates/graphql_controller.erb +52 -0
  27. data/lib/generators/graphql/templates/interface.erb +8 -0
  28. data/lib/generators/graphql/templates/loader.erb +19 -0
  29. data/lib/generators/graphql/templates/mutation.erb +16 -0
  30. data/lib/generators/graphql/templates/mutation_type.erb +12 -0
  31. data/lib/generators/graphql/templates/node_type.erb +9 -0
  32. data/lib/generators/graphql/templates/object.erb +8 -0
  33. data/lib/generators/graphql/templates/query_type.erb +15 -0
  34. data/lib/generators/graphql/templates/scalar.erb +15 -0
  35. data/lib/generators/graphql/templates/schema.erb +27 -0
  36. data/lib/generators/graphql/templates/union.erb +7 -0
  37. data/lib/generators/graphql/type_generator.rb +98 -0
  38. data/lib/generators/graphql/union_generator.rb +33 -0
  39. data/lib/graphql/analysis/analyze_query.rb +98 -0
  40. data/lib/graphql/analysis/ast/analyzer.rb +84 -0
  41. data/lib/graphql/analysis/ast/field_usage.rb +51 -0
  42. data/lib/graphql/analysis/ast/max_query_complexity.rb +23 -0
  43. data/lib/graphql/analysis/ast/max_query_depth.rb +22 -0
  44. data/lib/graphql/analysis/ast/query_complexity.rb +230 -0
  45. data/lib/graphql/analysis/ast/query_depth.rb +56 -0
  46. data/lib/graphql/analysis/ast/visitor.rb +268 -0
  47. data/lib/graphql/analysis/ast.rb +91 -0
  48. data/lib/graphql/analysis/field_usage.rb +45 -0
  49. data/lib/graphql/analysis/max_query_complexity.rb +26 -0
  50. data/lib/graphql/analysis/max_query_depth.rb +26 -0
  51. data/lib/graphql/analysis/query_complexity.rb +88 -0
  52. data/lib/graphql/analysis/query_depth.rb +43 -0
  53. data/lib/graphql/analysis/reducer_state.rb +48 -0
  54. data/lib/graphql/analysis.rb +9 -0
  55. data/lib/graphql/analysis_error.rb +5 -0
  56. data/lib/graphql/argument.rb +131 -0
  57. data/lib/graphql/authorization.rb +82 -0
  58. data/lib/graphql/backtrace/inspect_result.rb +50 -0
  59. data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
  60. data/lib/graphql/backtrace/table.rb +159 -0
  61. data/lib/graphql/backtrace/traced_error.rb +54 -0
  62. data/lib/graphql/backtrace/tracer.rb +81 -0
  63. data/lib/graphql/backtrace.rb +64 -0
  64. data/lib/graphql/backwards_compatibility.rb +61 -0
  65. data/lib/graphql/base_type.rb +230 -0
  66. data/lib/graphql/boolean_type.rb +2 -0
  67. data/lib/graphql/coercion_error.rb +13 -0
  68. data/lib/graphql/compatibility/execution_specification/counter_schema.rb +53 -0
  69. data/lib/graphql/compatibility/execution_specification/specification_schema.rb +200 -0
  70. data/lib/graphql/compatibility/execution_specification.rb +436 -0
  71. data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +111 -0
  72. data/lib/graphql/compatibility/lazy_execution_specification.rb +215 -0
  73. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +87 -0
  74. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +79 -0
  75. data/lib/graphql/compatibility/query_parser_specification.rb +266 -0
  76. data/lib/graphql/compatibility/schema_parser_specification.rb +682 -0
  77. data/lib/graphql/compatibility.rb +5 -0
  78. data/lib/graphql/dataloader/null_dataloader.rb +22 -0
  79. data/lib/graphql/dataloader/request.rb +19 -0
  80. data/lib/graphql/dataloader/request_all.rb +19 -0
  81. data/lib/graphql/dataloader/source.rb +155 -0
  82. data/lib/graphql/dataloader.rb +308 -0
  83. data/lib/graphql/define/assign_argument.rb +12 -0
  84. data/lib/graphql/define/assign_connection.rb +13 -0
  85. data/lib/graphql/define/assign_enum_value.rb +18 -0
  86. data/lib/graphql/define/assign_global_id_field.rb +11 -0
  87. data/lib/graphql/define/assign_mutation_function.rb +34 -0
  88. data/lib/graphql/define/assign_object_field.rb +42 -0
  89. data/lib/graphql/define/defined_object_proxy.rb +53 -0
  90. data/lib/graphql/define/instance_definable.rb +240 -0
  91. data/lib/graphql/define/no_definition_error.rb +7 -0
  92. data/lib/graphql/define/non_null_with_bang.rb +16 -0
  93. data/lib/graphql/define/type_definer.rb +31 -0
  94. data/lib/graphql/define.rb +31 -0
  95. data/lib/graphql/deprecated_dsl.rb +55 -0
  96. data/lib/graphql/deprecation.rb +9 -0
  97. data/lib/graphql/dig.rb +19 -0
  98. data/lib/graphql/directive/deprecated_directive.rb +2 -0
  99. data/lib/graphql/directive/include_directive.rb +2 -0
  100. data/lib/graphql/directive/skip_directive.rb +2 -0
  101. data/lib/graphql/directive.rb +107 -0
  102. data/lib/graphql/enum_type.rb +133 -0
  103. data/lib/graphql/execution/directive_checks.rb +37 -0
  104. data/lib/graphql/execution/errors.rb +163 -0
  105. data/lib/graphql/execution/execute.rb +333 -0
  106. data/lib/graphql/execution/flatten.rb +40 -0
  107. data/lib/graphql/execution/instrumentation.rb +92 -0
  108. data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
  109. data/lib/graphql/execution/interpreter/arguments.rb +88 -0
  110. data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
  111. data/lib/graphql/execution/interpreter/execution_errors.rb +29 -0
  112. data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
  113. data/lib/graphql/execution/interpreter/resolve.rb +70 -0
  114. data/lib/graphql/execution/interpreter/runtime.rb +949 -0
  115. data/lib/graphql/execution/interpreter.rb +122 -0
  116. data/lib/graphql/execution/lazy/lazy_method_map.rb +98 -0
  117. data/lib/graphql/execution/lazy/resolve.rb +91 -0
  118. data/lib/graphql/execution/lazy.rb +83 -0
  119. data/lib/graphql/execution/lookahead.rb +307 -0
  120. data/lib/graphql/execution/multiplex.rb +214 -0
  121. data/lib/graphql/execution/typecast.rb +50 -0
  122. data/lib/graphql/execution.rb +11 -0
  123. data/lib/graphql/execution_error.rb +58 -0
  124. data/lib/graphql/field/resolve.rb +59 -0
  125. data/lib/graphql/field.rb +226 -0
  126. data/lib/graphql/filter.rb +53 -0
  127. data/lib/graphql/float_type.rb +2 -0
  128. data/lib/graphql/function.rb +128 -0
  129. data/lib/graphql/id_type.rb +2 -0
  130. data/lib/graphql/input_object_type.rb +138 -0
  131. data/lib/graphql/int_type.rb +2 -0
  132. data/lib/graphql/integer_decoding_error.rb +17 -0
  133. data/lib/graphql/integer_encoding_error.rb +36 -0
  134. data/lib/graphql/interface_type.rb +72 -0
  135. data/lib/graphql/internal_representation/document.rb +27 -0
  136. data/lib/graphql/internal_representation/node.rb +206 -0
  137. data/lib/graphql/internal_representation/print.rb +51 -0
  138. data/lib/graphql/internal_representation/rewrite.rb +184 -0
  139. data/lib/graphql/internal_representation/scope.rb +88 -0
  140. data/lib/graphql/internal_representation/visit.rb +36 -0
  141. data/lib/graphql/internal_representation.rb +7 -0
  142. data/lib/graphql/introspection/base_object.rb +13 -0
  143. data/lib/graphql/introspection/directive_location_enum.rb +15 -0
  144. data/lib/graphql/introspection/directive_type.rb +29 -0
  145. data/lib/graphql/introspection/dynamic_fields.rb +17 -0
  146. data/lib/graphql/introspection/entry_points.rb +35 -0
  147. data/lib/graphql/introspection/enum_value_type.rb +23 -0
  148. data/lib/graphql/introspection/field_type.rb +28 -0
  149. data/lib/graphql/introspection/input_value_type.rb +67 -0
  150. data/lib/graphql/introspection/introspection_query.rb +7 -0
  151. data/lib/graphql/introspection/schema_type.rb +44 -0
  152. data/lib/graphql/introspection/type_kind_enum.rb +13 -0
  153. data/lib/graphql/introspection/type_type.rb +95 -0
  154. data/lib/graphql/introspection.rb +114 -0
  155. data/lib/graphql/invalid_name_error.rb +11 -0
  156. data/lib/graphql/invalid_null_error.rb +50 -0
  157. data/lib/graphql/language/block_string.rb +99 -0
  158. data/lib/graphql/language/cache.rb +37 -0
  159. data/lib/graphql/language/definition_slice.rb +41 -0
  160. data/lib/graphql/language/document_from_schema_definition.rb +347 -0
  161. data/lib/graphql/language/generation.rb +24 -0
  162. data/lib/graphql/language/lexer.rb +1467 -0
  163. data/lib/graphql/language/lexer.rl +258 -0
  164. data/lib/graphql/language/nodes.rb +707 -0
  165. data/lib/graphql/language/parser.rb +1974 -0
  166. data/lib/graphql/language/parser.y +544 -0
  167. data/lib/graphql/language/printer.rb +366 -0
  168. data/lib/graphql/language/sanitized_printer.rb +222 -0
  169. data/lib/graphql/language/token.rb +34 -0
  170. data/lib/graphql/language/visitor.rb +242 -0
  171. data/lib/graphql/language.rb +36 -0
  172. data/lib/graphql/list_type.rb +80 -0
  173. data/lib/graphql/load_application_object_failed_error.rb +22 -0
  174. data/lib/graphql/name_validator.rb +11 -0
  175. data/lib/graphql/non_null_type.rb +71 -0
  176. data/lib/graphql/object_type.rb +130 -0
  177. data/lib/graphql/pagination/active_record_relation_connection.rb +48 -0
  178. data/lib/graphql/pagination/array_connection.rb +77 -0
  179. data/lib/graphql/pagination/connection.rb +226 -0
  180. data/lib/graphql/pagination/connections.rb +160 -0
  181. data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
  182. data/lib/graphql/pagination/relation_connection.rb +196 -0
  183. data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
  184. data/lib/graphql/pagination.rb +6 -0
  185. data/lib/graphql/parse_error.rb +24 -0
  186. data/lib/graphql/query/arguments.rb +189 -0
  187. data/lib/graphql/query/arguments_cache.rb +24 -0
  188. data/lib/graphql/query/context.rb +371 -0
  189. data/lib/graphql/query/executor.rb +52 -0
  190. data/lib/graphql/query/fingerprint.rb +26 -0
  191. data/lib/graphql/query/input_validation_result.rb +43 -0
  192. data/lib/graphql/query/literal_input.rb +136 -0
  193. data/lib/graphql/query/null_context.rb +55 -0
  194. data/lib/graphql/query/result.rb +63 -0
  195. data/lib/graphql/query/serial_execution/field_resolution.rb +92 -0
  196. data/lib/graphql/query/serial_execution/operation_resolution.rb +19 -0
  197. data/lib/graphql/query/serial_execution/selection_resolution.rb +23 -0
  198. data/lib/graphql/query/serial_execution/value_resolution.rb +87 -0
  199. data/lib/graphql/query/serial_execution.rb +40 -0
  200. data/lib/graphql/query/validation_pipeline.rb +139 -0
  201. data/lib/graphql/query/variable_validation_error.rb +44 -0
  202. data/lib/graphql/query/variables.rb +78 -0
  203. data/lib/graphql/query.rb +454 -0
  204. data/lib/graphql/railtie.rb +117 -0
  205. data/lib/graphql/rake_task/validate.rb +63 -0
  206. data/lib/graphql/rake_task.rb +145 -0
  207. data/lib/graphql/relay/array_connection.rb +83 -0
  208. data/lib/graphql/relay/base_connection.rb +189 -0
  209. data/lib/graphql/relay/connection_instrumentation.rb +54 -0
  210. data/lib/graphql/relay/connection_resolve.rb +43 -0
  211. data/lib/graphql/relay/connection_type.rb +41 -0
  212. data/lib/graphql/relay/edge.rb +27 -0
  213. data/lib/graphql/relay/edge_type.rb +19 -0
  214. data/lib/graphql/relay/edges_instrumentation.rb +39 -0
  215. data/lib/graphql/relay/global_id_resolve.rb +18 -0
  216. data/lib/graphql/relay/mongo_relation_connection.rb +50 -0
  217. data/lib/graphql/relay/mutation/instrumentation.rb +23 -0
  218. data/lib/graphql/relay/mutation/resolve.rb +56 -0
  219. data/lib/graphql/relay/mutation/result.rb +38 -0
  220. data/lib/graphql/relay/mutation.rb +106 -0
  221. data/lib/graphql/relay/node.rb +39 -0
  222. data/lib/graphql/relay/page_info.rb +7 -0
  223. data/lib/graphql/relay/range_add.rb +59 -0
  224. data/lib/graphql/relay/relation_connection.rb +188 -0
  225. data/lib/graphql/relay/type_extensions.rb +32 -0
  226. data/lib/graphql/relay.rb +18 -0
  227. data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
  228. data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
  229. data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
  230. data/lib/graphql/rubocop.rb +4 -0
  231. data/lib/graphql/runtime_type_error.rb +5 -0
  232. data/lib/graphql/scalar_type.rb +91 -0
  233. data/lib/graphql/schema/addition.rb +247 -0
  234. data/lib/graphql/schema/argument.rb +383 -0
  235. data/lib/graphql/schema/base_64_bp.rb +26 -0
  236. data/lib/graphql/schema/base_64_encoder.rb +21 -0
  237. data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +47 -0
  238. data/lib/graphql/schema/build_from_definition/resolve_map.rb +78 -0
  239. data/lib/graphql/schema/build_from_definition.rb +477 -0
  240. data/lib/graphql/schema/built_in_types.rb +12 -0
  241. data/lib/graphql/schema/catchall_middleware.rb +35 -0
  242. data/lib/graphql/schema/default_parse_error.rb +10 -0
  243. data/lib/graphql/schema/default_type_error.rb +17 -0
  244. data/lib/graphql/schema/directive/deprecated.rb +18 -0
  245. data/lib/graphql/schema/directive/feature.rb +66 -0
  246. data/lib/graphql/schema/directive/flagged.rb +57 -0
  247. data/lib/graphql/schema/directive/include.rb +25 -0
  248. data/lib/graphql/schema/directive/skip.rb +25 -0
  249. data/lib/graphql/schema/directive/transform.rb +60 -0
  250. data/lib/graphql/schema/directive.rb +210 -0
  251. data/lib/graphql/schema/enum.rb +193 -0
  252. data/lib/graphql/schema/enum_value.rb +97 -0
  253. data/lib/graphql/schema/field/connection_extension.rb +76 -0
  254. data/lib/graphql/schema/field/scope_extension.rb +22 -0
  255. data/lib/graphql/schema/field.rb +880 -0
  256. data/lib/graphql/schema/field_extension.rb +69 -0
  257. data/lib/graphql/schema/find_inherited_value.rb +36 -0
  258. data/lib/graphql/schema/finder.rb +155 -0
  259. data/lib/graphql/schema/input_object.rb +253 -0
  260. data/lib/graphql/schema/interface.rb +136 -0
  261. data/lib/graphql/schema/introspection_system.rb +169 -0
  262. data/lib/graphql/schema/invalid_type_error.rb +7 -0
  263. data/lib/graphql/schema/late_bound_type.rb +33 -0
  264. data/lib/graphql/schema/list.rb +75 -0
  265. data/lib/graphql/schema/loader.rb +226 -0
  266. data/lib/graphql/schema/member/accepts_definition.rb +159 -0
  267. data/lib/graphql/schema/member/base_dsl_methods.rb +129 -0
  268. data/lib/graphql/schema/member/build_type.rb +180 -0
  269. data/lib/graphql/schema/member/cached_graphql_definition.rb +31 -0
  270. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  271. data/lib/graphql/schema/member/has_arguments.rb +332 -0
  272. data/lib/graphql/schema/member/has_ast_node.rb +20 -0
  273. data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
  274. data/lib/graphql/schema/member/has_directives.rb +98 -0
  275. data/lib/graphql/schema/member/has_fields.rb +163 -0
  276. data/lib/graphql/schema/member/has_interfaces.rb +90 -0
  277. data/lib/graphql/schema/member/has_path.rb +25 -0
  278. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  279. data/lib/graphql/schema/member/has_validators.rb +31 -0
  280. data/lib/graphql/schema/member/instrumentation.rb +131 -0
  281. data/lib/graphql/schema/member/relay_shortcuts.rb +47 -0
  282. data/lib/graphql/schema/member/scoped.rb +21 -0
  283. data/lib/graphql/schema/member/type_system_helpers.rb +38 -0
  284. data/lib/graphql/schema/member/validates_input.rb +33 -0
  285. data/lib/graphql/schema/member.rb +161 -0
  286. data/lib/graphql/schema/middleware_chain.rb +82 -0
  287. data/lib/graphql/schema/mutation.rb +94 -0
  288. data/lib/graphql/schema/non_null.rb +67 -0
  289. data/lib/graphql/schema/null_mask.rb +11 -0
  290. data/lib/graphql/schema/object.rb +150 -0
  291. data/lib/graphql/schema/possible_types.rb +44 -0
  292. data/lib/graphql/schema/printer.rb +100 -0
  293. data/lib/graphql/schema/relay_classic_mutation.rb +160 -0
  294. data/lib/graphql/schema/rescue_middleware.rb +60 -0
  295. data/lib/graphql/schema/resolver/has_payload_type.rb +96 -0
  296. data/lib/graphql/schema/resolver.rb +397 -0
  297. data/lib/graphql/schema/scalar.rb +69 -0
  298. data/lib/graphql/schema/subscription.rb +155 -0
  299. data/lib/graphql/schema/timeout.rb +123 -0
  300. data/lib/graphql/schema/timeout_middleware.rb +88 -0
  301. data/lib/graphql/schema/traversal.rb +228 -0
  302. data/lib/graphql/schema/type_expression.rb +43 -0
  303. data/lib/graphql/schema/type_membership.rb +48 -0
  304. data/lib/graphql/schema/union.rb +95 -0
  305. data/lib/graphql/schema/unique_within_type.rb +34 -0
  306. data/lib/graphql/schema/validation.rb +313 -0
  307. data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
  308. data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
  309. data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
  310. data/lib/graphql/schema/validator/format_validator.rb +48 -0
  311. data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
  312. data/lib/graphql/schema/validator/length_validator.rb +59 -0
  313. data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
  314. data/lib/graphql/schema/validator/required_validator.rb +68 -0
  315. data/lib/graphql/schema/validator.rb +174 -0
  316. data/lib/graphql/schema/warden.rb +409 -0
  317. data/lib/graphql/schema/wrapper.rb +29 -0
  318. data/lib/graphql/schema.rb +1945 -0
  319. data/lib/graphql/static_validation/all_rules.rb +40 -0
  320. data/lib/graphql/static_validation/base_visitor.rb +217 -0
  321. data/lib/graphql/static_validation/default_visitor.rb +15 -0
  322. data/lib/graphql/static_validation/definition_dependencies.rb +198 -0
  323. data/lib/graphql/static_validation/error.rb +46 -0
  324. data/lib/graphql/static_validation/interpreter_visitor.rb +14 -0
  325. data/lib/graphql/static_validation/literal_validator.rb +139 -0
  326. data/lib/graphql/static_validation/no_validate_visitor.rb +10 -0
  327. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +66 -0
  328. data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +48 -0
  329. data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +31 -0
  330. data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +30 -0
  331. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +71 -0
  332. data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +37 -0
  333. data/lib/graphql/static_validation/rules/directives_are_defined.rb +23 -0
  334. data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +29 -0
  335. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +65 -0
  336. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +31 -0
  337. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +30 -0
  338. data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +32 -0
  339. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +73 -0
  340. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +31 -0
  341. data/lib/graphql/static_validation/rules/fields_will_merge.rb +418 -0
  342. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +53 -0
  343. data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +30 -0
  344. data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +29 -0
  345. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +73 -0
  346. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +35 -0
  347. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +39 -0
  348. data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +29 -0
  349. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +21 -0
  350. data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +29 -0
  351. data/lib/graphql/static_validation/rules/fragments_are_named.rb +16 -0
  352. data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +26 -0
  353. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +37 -0
  354. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +30 -0
  355. data/lib/graphql/static_validation/rules/fragments_are_used.rb +32 -0
  356. data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +29 -0
  357. data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
  358. data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
  359. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +17 -0
  360. data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +26 -0
  361. data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +41 -0
  362. data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +25 -0
  363. data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +36 -0
  364. data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +28 -0
  365. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +37 -0
  366. data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +35 -0
  367. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +59 -0
  368. data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +35 -0
  369. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +17 -0
  370. data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +26 -0
  371. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +50 -0
  372. data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +29 -0
  373. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +46 -0
  374. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +39 -0
  375. data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +24 -0
  376. data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +29 -0
  377. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +153 -0
  378. data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +38 -0
  379. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +39 -0
  380. data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +32 -0
  381. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +155 -0
  382. data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +37 -0
  383. data/lib/graphql/static_validation/type_stack.rb +216 -0
  384. data/lib/graphql/static_validation/validation_context.rb +49 -0
  385. data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
  386. data/lib/graphql/static_validation/validator.rb +96 -0
  387. data/lib/graphql/static_validation.rb +19 -0
  388. data/lib/graphql/string_encoding_error.rb +20 -0
  389. data/lib/graphql/string_type.rb +2 -0
  390. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +245 -0
  391. data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
  392. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  393. data/lib/graphql/subscriptions/event.rb +144 -0
  394. data/lib/graphql/subscriptions/instrumentation.rb +79 -0
  395. data/lib/graphql/subscriptions/serialize.rb +138 -0
  396. data/lib/graphql/subscriptions/subscription_root.rb +76 -0
  397. data/lib/graphql/subscriptions.rb +299 -0
  398. data/lib/graphql/tracing/active_support_notifications_tracing.rb +35 -0
  399. data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
  400. data/lib/graphql/tracing/appsignal_tracing.rb +51 -0
  401. data/lib/graphql/tracing/data_dog_tracing.rb +76 -0
  402. data/lib/graphql/tracing/new_relic_tracing.rb +51 -0
  403. data/lib/graphql/tracing/platform_tracing.rb +139 -0
  404. data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +32 -0
  405. data/lib/graphql/tracing/prometheus_tracing.rb +67 -0
  406. data/lib/graphql/tracing/scout_tracing.rb +54 -0
  407. data/lib/graphql/tracing/skylight_tracing.rb +70 -0
  408. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  409. data/lib/graphql/tracing.rb +95 -0
  410. data/lib/graphql/type_kinds.rb +77 -0
  411. data/lib/graphql/types/big_int.rb +23 -0
  412. data/lib/graphql/types/boolean.rb +18 -0
  413. data/lib/graphql/types/float.rb +19 -0
  414. data/lib/graphql/types/id.rb +24 -0
  415. data/lib/graphql/types/int.rb +36 -0
  416. data/lib/graphql/types/iso_8601_date.rb +34 -0
  417. data/lib/graphql/types/iso_8601_date_time.rb +65 -0
  418. data/lib/graphql/types/json.rb +25 -0
  419. data/lib/graphql/types/relay/base_connection.rb +39 -0
  420. data/lib/graphql/types/relay/base_edge.rb +29 -0
  421. data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
  422. data/lib/graphql/types/relay/default_relay.rb +27 -0
  423. data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
  424. data/lib/graphql/types/relay/has_node_field.rb +41 -0
  425. data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
  426. data/lib/graphql/types/relay/node.rb +15 -0
  427. data/lib/graphql/types/relay/node_behaviors.rb +15 -0
  428. data/lib/graphql/types/relay/node_field.rb +25 -0
  429. data/lib/graphql/types/relay/nodes_field.rb +27 -0
  430. data/lib/graphql/types/relay/page_info.rb +11 -0
  431. data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
  432. data/lib/graphql/types/relay.rb +41 -0
  433. data/lib/graphql/types/string.rb +29 -0
  434. data/lib/graphql/types.rb +11 -0
  435. data/lib/graphql/unauthorized_error.rb +29 -0
  436. data/lib/graphql/unauthorized_field_error.rb +23 -0
  437. data/lib/graphql/union_type.rb +115 -0
  438. data/lib/graphql/unresolved_type_error.rb +35 -0
  439. data/lib/graphql/upgrader/member.rb +937 -0
  440. data/lib/graphql/upgrader/schema.rb +38 -0
  441. data/lib/graphql/version.rb +4 -0
  442. data/lib/graphql.rb +168 -0
  443. data/readme.md +49 -0
  444. metadata +714 -0
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+ require "fiber"
3
+ require "graphql/execution/interpreter/argument_value"
4
+ require "graphql/execution/interpreter/arguments"
5
+ require "graphql/execution/interpreter/arguments_cache"
6
+ require "graphql/execution/interpreter/execution_errors"
7
+ require "graphql/execution/interpreter/runtime"
8
+ require "graphql/execution/interpreter/resolve"
9
+ require "graphql/execution/interpreter/handles_raw_value"
10
+
11
+ module GraphQL
12
+ module Execution
13
+ class Interpreter
14
+ def initialize
15
+ end
16
+
17
+ # Support `Executor` :S
18
+ def execute(_operation, _root_type, query)
19
+ runtime = evaluate(query)
20
+ sync_lazies(query: query)
21
+ runtime.final_result
22
+ end
23
+
24
+ def self.use(schema_class)
25
+ if schema_class.interpreter?
26
+ definition_line = caller(2, 1).first
27
+ GraphQL::Deprecation.warn("GraphQL::Execution::Interpreter is now the default; remove `use GraphQL::Execution::Interpreter` from the schema definition (#{definition_line})")
28
+ else
29
+ schema_class.query_execution_strategy(self)
30
+ schema_class.mutation_execution_strategy(self)
31
+ schema_class.subscription_execution_strategy(self)
32
+ schema_class.add_subscription_extension_if_necessary
33
+ end
34
+ end
35
+
36
+ def self.begin_multiplex(multiplex)
37
+ # Since this is basically the batching context,
38
+ # share it for a whole multiplex
39
+ multiplex.context[:interpreter_instance] ||= self.new
40
+ end
41
+
42
+ def self.begin_query(query, multiplex)
43
+ # The batching context is shared by the multiplex,
44
+ # so fetch it out and use that instance.
45
+ interpreter =
46
+ query.context.namespace(:interpreter)[:interpreter_instance] =
47
+ multiplex.context[:interpreter_instance]
48
+ interpreter.evaluate(query)
49
+ query
50
+ end
51
+
52
+ def self.finish_multiplex(_results, multiplex)
53
+ interpreter = multiplex.context[:interpreter_instance]
54
+ interpreter.sync_lazies(multiplex: multiplex)
55
+ end
56
+
57
+ def self.finish_query(query, _multiplex)
58
+ {
59
+ "data" => query.context.namespace(:interpreter)[:runtime].final_result
60
+ }
61
+ end
62
+
63
+ # Run the eager part of `query`
64
+ # @return {Interpreter::Runtime}
65
+ def evaluate(query)
66
+ # Although queries in a multiplex _share_ an Interpreter instance,
67
+ # they also have another item of state, which is private to that query
68
+ # in particular, assign it here:
69
+ runtime = Runtime.new(query: query)
70
+ query.context.namespace(:interpreter)[:runtime] = runtime
71
+
72
+ query.trace("execute_query", {query: query}) do
73
+ runtime.run_eager
74
+ end
75
+
76
+ runtime
77
+ end
78
+
79
+ # Run the lazy part of `query` or `multiplex`.
80
+ # @return [void]
81
+ def sync_lazies(query: nil, multiplex: nil)
82
+ tracer = query || multiplex
83
+ if query.nil? && multiplex.queries.length == 1
84
+ query = multiplex.queries[0]
85
+ end
86
+ queries = multiplex ? multiplex.queries : [query]
87
+ final_values = queries.map do |query|
88
+ runtime = query.context.namespace(:interpreter)[:runtime]
89
+ # it might not be present if the query has an error
90
+ runtime ? runtime.final_result : nil
91
+ end
92
+ final_values.compact!
93
+ tracer.trace("execute_query_lazy", {multiplex: multiplex, query: query}) do
94
+ Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
95
+ end
96
+ queries.each do |query|
97
+ runtime = query.context.namespace(:interpreter)[:runtime]
98
+ if runtime
99
+ runtime.delete_interpreter_context(:current_path)
100
+ runtime.delete_interpreter_context(:current_field)
101
+ runtime.delete_interpreter_context(:current_object)
102
+ runtime.delete_interpreter_context(:current_arguments)
103
+ end
104
+ end
105
+ nil
106
+ end
107
+
108
+ class ListResultFailedError < GraphQL::Error
109
+ def initialize(value:, path:, field:)
110
+ message = "Failed to build a GraphQL list result for field `#{field.path}` at path `#{path.join(".")}`.\n".dup
111
+
112
+ message << "Expected `#{value.inspect}` (#{value.class}) to implement `.each` to satisfy the GraphQL return type `#{field.type.to_type_signature}`.\n"
113
+
114
+ if field.connection?
115
+ message << "\nThis field was treated as a Relay-style connection; add `connection: false` to the `field(...)` to disable this behavior."
116
+ end
117
+ super(message)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+ require 'thread'
3
+ begin
4
+ require 'concurrent'
5
+ rescue LoadError
6
+ # no problem, we'll fallback to our own map
7
+ end
8
+
9
+ module GraphQL
10
+ module Execution
11
+ class Lazy
12
+ # {GraphQL::Schema} uses this to match returned values to lazy resolution methods.
13
+ # Methods may be registered for classes, they apply to its subclasses also.
14
+ # The result of this lookup is cached for future resolutions.
15
+ # Instances of this class are thread-safe.
16
+ # @api private
17
+ # @see {Schema#lazy?} looks up values from this map
18
+ class LazyMethodMap
19
+ def initialize(use_concurrent: defined?(Concurrent::Map))
20
+ @storage = use_concurrent ? Concurrent::Map.new : ConcurrentishMap.new
21
+ end
22
+
23
+ def initialize_copy(other)
24
+ @storage = other.storage.dup
25
+ end
26
+
27
+ # @param lazy_class [Class] A class which represents a lazy value (subclasses may also be used)
28
+ # @param lazy_value_method [Symbol] The method to call on this class to get its value
29
+ def set(lazy_class, lazy_value_method)
30
+ @storage[lazy_class] = lazy_value_method
31
+ end
32
+
33
+ # @param value [Object] an object which may have a `lazy_value_method` registered for its class or superclasses
34
+ # @return [Symbol, nil] The `lazy_value_method` for this object, or nil
35
+ def get(value)
36
+ @storage.compute_if_absent(value.class) { find_superclass_method(value.class) }
37
+ end
38
+
39
+ def each
40
+ @storage.each_pair { |k, v| yield(k, v) }
41
+ end
42
+
43
+ protected
44
+
45
+ attr_reader :storage
46
+
47
+ private
48
+
49
+ def find_superclass_method(value_class)
50
+ @storage.each_pair { |lazy_class, lazy_value_method|
51
+ return lazy_value_method if value_class < lazy_class
52
+ }
53
+ nil
54
+ end
55
+
56
+ # Mock the Concurrent::Map API
57
+ class ConcurrentishMap
58
+ extend Forwardable
59
+ # Technically this should be under the mutex too,
60
+ # but I know it's only used when the lock is already acquired.
61
+ def_delegators :@storage, :each_pair, :size
62
+
63
+ def initialize
64
+ @semaphore = Mutex.new
65
+ # Access to this hash must always be managed by the mutex
66
+ # since it may be modified at runtime
67
+ @storage = {}
68
+ end
69
+
70
+ def []=(key, value)
71
+ @semaphore.synchronize {
72
+ @storage[key] = value
73
+ }
74
+ end
75
+
76
+ def compute_if_absent(key)
77
+ @semaphore.synchronize {
78
+ @storage.fetch(key) { @storage[key] = yield }
79
+ }
80
+ end
81
+
82
+ def initialize_copy(other)
83
+ @semaphore = Mutex.new
84
+ @storage = other.copy_storage
85
+ end
86
+
87
+ protected
88
+
89
+ def copy_storage
90
+ @semaphore.synchronize {
91
+ @storage.dup
92
+ }
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ class Lazy
5
+ # Helpers for dealing with data structures containing {Lazy} instances
6
+ # @api private
7
+ module Resolve
8
+ # Mutate `value`, replacing {Lazy} instances in place with their resolved values
9
+ # @return [void]
10
+
11
+ # This object can be passed like an array, but it doesn't allocate an
12
+ # array until it's used.
13
+ #
14
+ # There's one crucial difference: you have to _capture_ the result
15
+ # of `#<<`. (This _works_ with arrays but isn't required, since it has a side-effect.)
16
+ # @api private
17
+ module NullAccumulator
18
+ def self.<<(item)
19
+ [item]
20
+ end
21
+
22
+ def self.empty?
23
+ true
24
+ end
25
+ end
26
+
27
+ def self.resolve(value)
28
+ lazies = resolve_in_place(value)
29
+ deep_sync(lazies)
30
+ end
31
+
32
+ def self.resolve_in_place(value)
33
+ acc = each_lazy(NullAccumulator, value)
34
+
35
+ if acc.empty?
36
+ Lazy::NullResult
37
+ else
38
+ Lazy.new {
39
+ acc.each_with_index { |ctx, idx|
40
+ acc[idx] = ctx.value.value
41
+ }
42
+ resolve_in_place(acc)
43
+ }
44
+ end
45
+ end
46
+
47
+ # If `value` is a collection,
48
+ # add any {Lazy} instances in the collection
49
+ # to `acc`
50
+ # @return [void]
51
+ def self.each_lazy(acc, value)
52
+ case value
53
+ when Hash
54
+ value.each do |key, field_result|
55
+ acc = each_lazy(acc, field_result)
56
+ end
57
+ when Array
58
+ value.each do |field_result|
59
+ acc = each_lazy(acc, field_result)
60
+ end
61
+ when Query::Context::SharedMethods
62
+ field_value = value.value
63
+ case field_value
64
+ when Lazy
65
+ acc = acc << value
66
+ when Enumerable # shortcut for Hash & Array
67
+ acc = each_lazy(acc, field_value)
68
+ end
69
+ end
70
+
71
+ acc
72
+ end
73
+
74
+ # Traverse `val`, triggering resolution for each {Lazy}.
75
+ # These {Lazy}s are expected to mutate their owner data structures
76
+ # during resolution! (They're created with the `.then` calls in `resolve_in_place`).
77
+ # @return [void]
78
+ def self.deep_sync(val)
79
+ case val
80
+ when Lazy
81
+ deep_sync(val.value)
82
+ when Array
83
+ val.each { |v| deep_sync(v.value) }
84
+ when Hash
85
+ val.each { |k, v| deep_sync(v.value) }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+ require "graphql/execution/lazy/lazy_method_map"
3
+ require "graphql/execution/lazy/resolve"
4
+
5
+ module GraphQL
6
+ module Execution
7
+ # This wraps a value which is available, but not yet calculated, like a promise or future.
8
+ #
9
+ # Calling `#value` will trigger calculation & return the "lazy" value.
10
+ #
11
+ # This is an itty-bitty promise-like object, with key differences:
12
+ # - It has only two states, not-resolved and resolved
13
+ # - It has no error-catching functionality
14
+ # @api private
15
+ class Lazy
16
+ # Traverse `val`, lazily resolving any values along the way
17
+ # @param val [Object] A data structure containing mixed plain values and `Lazy` instances
18
+ # @return void
19
+ def self.resolve(val)
20
+ Resolve.resolve(val)
21
+ end
22
+
23
+ attr_reader :path, :field
24
+
25
+ # Create a {Lazy} which will get its inner value by calling the block
26
+ # @param path [Array<String, Integer>]
27
+ # @param field [GraphQL::Schema::Field]
28
+ # @param get_value_func [Proc] a block to get the inner value (later)
29
+ def initialize(path: nil, field: nil, &get_value_func)
30
+ @get_value_func = get_value_func
31
+ @resolved = false
32
+ @path = path
33
+ @field = field
34
+ end
35
+
36
+ # @return [Object] The wrapped value, calling the lazy block if necessary
37
+ def value
38
+ if !@resolved
39
+ @resolved = true
40
+ @value = begin
41
+ v = @get_value_func.call
42
+ if v.is_a?(Lazy)
43
+ v = v.value
44
+ end
45
+ v
46
+ rescue GraphQL::ExecutionError => err
47
+ err
48
+ end
49
+ end
50
+
51
+ # `SKIP` was made into a subclass of `GraphQL::Error` to improve runtime performance
52
+ # (fewer clauses in a hot `case` block), but now it requires special handling here.
53
+ # I think it's still worth it for the performance win, but if the number of special
54
+ # cases grows, then maybe it's worth rethinking somehow.
55
+ if @value.is_a?(StandardError) && @value != GraphQL::Execution::Execute::SKIP
56
+ raise @value
57
+ else
58
+ @value
59
+ end
60
+ end
61
+
62
+ # @return [Lazy] A {Lazy} whose value depends on another {Lazy}, plus any transformations in `block`
63
+ def then
64
+ self.class.new {
65
+ yield(value)
66
+ }
67
+ end
68
+
69
+ # @param lazies [Array<Object>] Maybe-lazy objects
70
+ # @return [Lazy] A lazy which will sync all of `lazies`
71
+ def self.all(lazies)
72
+ self.new {
73
+ lazies.map { |l| l.is_a?(Lazy) ? l.value : l }
74
+ }
75
+ end
76
+
77
+ # This can be used for fields which _had no_ lazy results
78
+ # @api private
79
+ NullResult = Lazy.new(){}
80
+ NullResult.value
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,307 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Execution
4
+ # Lookahead creates a uniform interface to inspect the forthcoming selections.
5
+ #
6
+ # It assumes that the AST it's working with is valid. (So, it's safe to use
7
+ # during execution, but if you're using it directly, be sure to validate first.)
8
+ #
9
+ # A field may get access to its lookahead by adding `extras: [:lookahead]`
10
+ # to its configuration.
11
+ #
12
+ # @example looking ahead in a field
13
+ # field :articles, [Types::Article], null: false,
14
+ # extras: [:lookahead]
15
+ #
16
+ # # For example, imagine a faster database call
17
+ # # may be issued when only some fields are requested.
18
+ # #
19
+ # # Imagine that _full_ fetch must be made to satisfy `fullContent`,
20
+ # # we can look ahead to see if we need that field. If we do,
21
+ # # we make the expensive database call instead of the cheap one.
22
+ # def articles(lookahead:)
23
+ # if lookahead.selects?(:full_content)
24
+ # fetch_full_articles(object)
25
+ # else
26
+ # fetch_preview_articles(object)
27
+ # end
28
+ # end
29
+ class Lookahead
30
+ # @param query [GraphQL::Query]
31
+ # @param ast_nodes [Array<GraphQL::Language::Nodes::Field>, Array<GraphQL::Language::Nodes::OperationDefinition>]
32
+ # @param field [GraphQL::Schema::Field] if `ast_nodes` are fields, this is the field definition matching those nodes
33
+ # @param root_type [Class] if `ast_nodes` are operation definition, this is the root type for that operation
34
+ def initialize(query:, ast_nodes:, field: nil, root_type: nil, owner_type: nil)
35
+ @ast_nodes = ast_nodes.freeze
36
+ @field = field
37
+ @root_type = root_type
38
+ @query = query
39
+ @selected_type = @field ? @field.type.unwrap : root_type
40
+ @owner_type = owner_type
41
+ end
42
+
43
+ # @return [Array<GraphQL::Language::Nodes::Field>]
44
+ attr_reader :ast_nodes
45
+
46
+ # @return [GraphQL::Schema::Field]
47
+ attr_reader :field
48
+
49
+ # @return [GraphQL::Schema::Object, GraphQL::Schema::Union, GraphQL::Schema::Interface]
50
+ attr_reader :owner_type
51
+
52
+ # @return [Hash<Symbol, Object>]
53
+ def arguments
54
+ if defined?(@arguments)
55
+ @arguments
56
+ else
57
+ @arguments = if @field
58
+ @query.schema.after_lazy(@query.arguments_for(@ast_nodes.first, @field)) do |args|
59
+ args.is_a?(Execution::Interpreter::Arguments) ? args.keyword_arguments : args
60
+ end
61
+ else
62
+ nil
63
+ end
64
+ end
65
+ end
66
+
67
+ # True if this node has a selection on `field_name`.
68
+ # If `field_name` is a String, it is treated as a GraphQL-style (camelized)
69
+ # field name and used verbatim. If `field_name` is a Symbol, it is
70
+ # treated as a Ruby-style (underscored) name and camelized before comparing.
71
+ #
72
+ # If `arguments:` is provided, each provided key/value will be matched
73
+ # against the arguments in the next selection. This method will return false
74
+ # if any of the given `arguments:` are not present and matching in the next selection.
75
+ # (But, the next selection may contain _more_ than the given arguments.)
76
+ # @param field_name [String, Symbol]
77
+ # @param arguments [Hash] Arguments which must match in the selection
78
+ # @return [Boolean]
79
+ def selects?(field_name, arguments: nil)
80
+ selection(field_name, arguments: arguments).selected?
81
+ end
82
+
83
+ # @return [Boolean] True if this lookahead represents a field that was requested
84
+ def selected?
85
+ true
86
+ end
87
+
88
+ # Like {#selects?}, but can be used for chaining.
89
+ # It returns a null object (check with {#selected?})
90
+ # @return [GraphQL::Execution::Lookahead]
91
+ def selection(field_name, selected_type: @selected_type, arguments: nil)
92
+ next_field_name = normalize_name(field_name)
93
+
94
+ next_field_defn = get_class_based_field(selected_type, next_field_name)
95
+ if next_field_defn
96
+ next_nodes = []
97
+ @ast_nodes.each do |ast_node|
98
+ ast_node.selections.each do |selection|
99
+ find_selected_nodes(selection, next_field_name, next_field_defn, arguments: arguments, matches: next_nodes)
100
+ end
101
+ end
102
+
103
+ if next_nodes.any?
104
+ Lookahead.new(query: @query, ast_nodes: next_nodes, field: next_field_defn, owner_type: selected_type)
105
+ else
106
+ NULL_LOOKAHEAD
107
+ end
108
+ else
109
+ NULL_LOOKAHEAD
110
+ end
111
+ end
112
+
113
+ # Like {#selection}, but for all nodes.
114
+ # It returns a list of Lookaheads for all Selections
115
+ #
116
+ # If `arguments:` is provided, each provided key/value will be matched
117
+ # against the arguments in each selection. This method will filter the selections
118
+ # if any of the given `arguments:` do not match the given selection.
119
+ #
120
+ # @example getting the name of a selection
121
+ # def articles(lookahead:)
122
+ # next_lookaheads = lookahead.selections # => [#<GraphQL::Execution::Lookahead ...>, ...]
123
+ # next_lookaheads.map(&:name) #=> [:full_content, :title]
124
+ # end
125
+ #
126
+ # @param arguments [Hash] Arguments which must match in the selection
127
+ # @return [Array<GraphQL::Execution::Lookahead>]
128
+ def selections(arguments: nil)
129
+ subselections_by_type = {}
130
+ subselections_on_type = subselections_by_type[@selected_type] = {}
131
+
132
+ @ast_nodes.each do |node|
133
+ find_selections(subselections_by_type, subselections_on_type, @selected_type, node.selections, arguments)
134
+ end
135
+
136
+ subselections = []
137
+
138
+ subselections_by_type.each do |type, ast_nodes_by_response_key|
139
+ ast_nodes_by_response_key.each do |response_key, ast_nodes|
140
+ field_defn = get_class_based_field(type, ast_nodes.first.name)
141
+ lookahead = Lookahead.new(query: @query, ast_nodes: ast_nodes, field: field_defn, owner_type: type)
142
+ subselections.push(lookahead)
143
+ end
144
+ end
145
+
146
+ subselections
147
+ end
148
+
149
+ # The method name of the field.
150
+ # It returns the method_sym of the Lookahead's field.
151
+ #
152
+ # @example getting the name of a selection
153
+ # def articles(lookahead:)
154
+ # article.selection(:full_content).name # => :full_content
155
+ # # ...
156
+ # end
157
+ #
158
+ # @return [Symbol]
159
+ def name
160
+ @field && @field.original_name
161
+ end
162
+
163
+ def inspect
164
+ "#<GraphQL::Execution::Lookahead #{@field ? "@field=#{@field.path.inspect}": "@root_type=#{@root_type}"} @ast_nodes.size=#{@ast_nodes.size}>"
165
+ end
166
+
167
+ # This is returned for {Lookahead#selection} when a non-existent field is passed
168
+ class NullLookahead < Lookahead
169
+ # No inputs required here.
170
+ def initialize
171
+ end
172
+
173
+ def selected?
174
+ false
175
+ end
176
+
177
+ def selects?(*)
178
+ false
179
+ end
180
+
181
+ def selection(*)
182
+ NULL_LOOKAHEAD
183
+ end
184
+
185
+ def selections(*)
186
+ []
187
+ end
188
+
189
+ def inspect
190
+ "#<GraphQL::Execution::Lookahead::NullLookahead>"
191
+ end
192
+ end
193
+
194
+ # A singleton, so that misses don't come with overhead.
195
+ NULL_LOOKAHEAD = NullLookahead.new
196
+
197
+ private
198
+
199
+ # If it's a symbol, stringify and camelize it
200
+ def normalize_name(name)
201
+ if name.is_a?(Symbol)
202
+ Schema::Member::BuildType.camelize(name.to_s)
203
+ else
204
+ name
205
+ end
206
+ end
207
+
208
+ def normalize_keyword(keyword)
209
+ if keyword.is_a?(String)
210
+ Schema::Member::BuildType.underscore(keyword).to_sym
211
+ else
212
+ keyword
213
+ end
214
+ end
215
+
216
+ # Wrap get_field and ensure that it returns a GraphQL::Schema::Field.
217
+ # Remove this when legacy execution is removed.
218
+ def get_class_based_field(type, name)
219
+ f = @query.get_field(type, name)
220
+ f && f.type_class
221
+ end
222
+
223
+ def skipped_by_directive?(ast_selection)
224
+ ast_selection.directives.each do |directive|
225
+ dir_defn = @query.schema.directives.fetch(directive.name)
226
+ directive_class = dir_defn.type_class
227
+ if directive_class
228
+ dir_args = @query.arguments_for(directive, dir_defn)
229
+ return true unless directive_class.static_include?(dir_args, @query.context)
230
+ end
231
+ end
232
+ false
233
+ end
234
+
235
+ def find_selections(subselections_by_type, selections_on_type, selected_type, ast_selections, arguments)
236
+ ast_selections.each do |ast_selection|
237
+ next if skipped_by_directive?(ast_selection)
238
+
239
+ case ast_selection
240
+ when GraphQL::Language::Nodes::Field
241
+ response_key = ast_selection.alias || ast_selection.name
242
+ if selections_on_type.key?(response_key)
243
+ selections_on_type[response_key] << ast_selection
244
+ elsif arguments.nil? || arguments.empty?
245
+ selections_on_type[response_key] = [ast_selection]
246
+ else
247
+ field_defn = get_class_based_field(selected_type, ast_selection.name)
248
+ if arguments_match?(arguments, field_defn, ast_selection)
249
+ selections_on_type[response_key] = [ast_selection]
250
+ end
251
+ end
252
+ when GraphQL::Language::Nodes::InlineFragment
253
+ on_type = selected_type
254
+ subselections_on_type = selections_on_type
255
+ if (t = ast_selection.type)
256
+ # Assuming this is valid, that `t` will be found.
257
+ on_type = @query.get_type(t.name).type_class
258
+ subselections_on_type = subselections_by_type[on_type] ||= {}
259
+ end
260
+ find_selections(subselections_by_type, subselections_on_type, on_type, ast_selection.selections, arguments)
261
+ when GraphQL::Language::Nodes::FragmentSpread
262
+ frag_defn = @query.fragments[ast_selection.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{ast_selection.name} (found: #{@query.fragments.keys})")
263
+ # Again, assuming a valid AST
264
+ on_type = @query.get_type(frag_defn.type.name).type_class
265
+ subselections_on_type = subselections_by_type[on_type] ||= {}
266
+ find_selections(subselections_by_type, subselections_on_type, on_type, frag_defn.selections, arguments)
267
+ else
268
+ raise "Invariant: Unexpected selection type: #{ast_selection.class}"
269
+ end
270
+ end
271
+ end
272
+
273
+ # If a selection on `node` matches `field_name` (which is backed by `field_defn`)
274
+ # and matches the `arguments:` constraints, then add that node to `matches`
275
+ def find_selected_nodes(node, field_name, field_defn, arguments:, matches:)
276
+ return if skipped_by_directive?(node)
277
+ case node
278
+ when GraphQL::Language::Nodes::Field
279
+ if node.name == field_name
280
+ if arguments.nil? || arguments.empty?
281
+ # No constraint applied
282
+ matches << node
283
+ elsif arguments_match?(arguments, field_defn, node)
284
+ matches << node
285
+ end
286
+ end
287
+ when GraphQL::Language::Nodes::InlineFragment
288
+ node.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
289
+ when GraphQL::Language::Nodes::FragmentSpread
290
+ frag_defn = @query.fragments[node.name] || raise("Invariant: Can't look ahead to nonexistent fragment #{node.name} (found: #{@query.fragments.keys})")
291
+ frag_defn.selections.each { |s| find_selected_nodes(s, field_name, field_defn, arguments: arguments, matches: matches) }
292
+ else
293
+ raise "Unexpected selection comparison on #{node.class.name} (#{node})"
294
+ end
295
+ end
296
+
297
+ def arguments_match?(arguments, field_defn, field_node)
298
+ query_kwargs = @query.arguments_for(field_node, field_defn)
299
+ arguments.all? do |arg_name, arg_value|
300
+ arg_name = normalize_keyword(arg_name)
301
+ # Make sure the constraint is present with a matching value
302
+ query_kwargs.key?(arg_name) && query_kwargs[arg_name] == arg_value
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end