graphql_cody 1.13.0

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