graphql 1.7.6 → 1.8.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 (289) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/function_generator.rb +1 -1
  3. data/lib/generators/graphql/install_generator.rb +14 -8
  4. data/lib/generators/graphql/loader_generator.rb +1 -1
  5. data/lib/generators/graphql/mutation_generator.rb +6 -1
  6. data/lib/generators/graphql/templates/function.erb +2 -2
  7. data/lib/generators/graphql/templates/loader.erb +2 -2
  8. data/lib/generators/graphql/templates/schema.erb +1 -1
  9. data/lib/graphql/argument.rb +25 -19
  10. data/lib/graphql/backtrace/tracer.rb +16 -22
  11. data/lib/graphql/backtrace.rb +1 -1
  12. data/lib/graphql/backwards_compatibility.rb +2 -3
  13. data/lib/graphql/base_type.rb +31 -31
  14. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +14 -0
  15. data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
  16. data/lib/graphql/define/assign_object_field.rb +5 -12
  17. data/lib/graphql/deprecated_dsl.rb +42 -0
  18. data/lib/graphql/directive.rb +1 -0
  19. data/lib/graphql/enum_type.rb +3 -1
  20. data/lib/graphql/execution/execute.rb +21 -13
  21. data/lib/graphql/execution/instrumentation.rb +82 -0
  22. data/lib/graphql/execution/lazy/lazy_method_map.rb +1 -1
  23. data/lib/graphql/execution/lazy/resolve.rb +1 -3
  24. data/lib/graphql/execution/multiplex.rb +12 -29
  25. data/lib/graphql/execution.rb +1 -0
  26. data/lib/graphql/field.rb +21 -4
  27. data/lib/graphql/function.rb +14 -0
  28. data/lib/graphql/input_object_type.rb +3 -1
  29. data/lib/graphql/interface_type.rb +5 -3
  30. data/lib/graphql/internal_representation/node.rb +26 -14
  31. data/lib/graphql/internal_representation/visit.rb +3 -6
  32. data/lib/graphql/introspection/base_object.rb +16 -0
  33. data/lib/graphql/introspection/directive_location_enum.rb +11 -7
  34. data/lib/graphql/introspection/directive_type.rb +23 -16
  35. data/lib/graphql/introspection/dynamic_fields.rb +11 -0
  36. data/lib/graphql/introspection/entry_points.rb +29 -0
  37. data/lib/graphql/introspection/enum_value_type.rb +16 -11
  38. data/lib/graphql/introspection/field_type.rb +21 -12
  39. data/lib/graphql/introspection/input_value_type.rb +26 -23
  40. data/lib/graphql/introspection/schema_field.rb +7 -2
  41. data/lib/graphql/introspection/schema_type.rb +36 -22
  42. data/lib/graphql/introspection/type_by_name_field.rb +10 -2
  43. data/lib/graphql/introspection/type_kind_enum.rb +10 -6
  44. data/lib/graphql/introspection/type_type.rb +85 -23
  45. data/lib/graphql/introspection/typename_field.rb +1 -0
  46. data/lib/graphql/introspection.rb +3 -10
  47. data/lib/graphql/language/block_string.rb +47 -0
  48. data/lib/graphql/language/document_from_schema_definition.rb +280 -0
  49. data/lib/graphql/language/generation.rb +3 -182
  50. data/lib/graphql/language/lexer.rb +144 -69
  51. data/lib/graphql/language/lexer.rl +15 -4
  52. data/lib/graphql/language/nodes.rb +141 -78
  53. data/lib/graphql/language/parser.rb +677 -630
  54. data/lib/graphql/language/parser.y +18 -12
  55. data/lib/graphql/language/printer.rb +361 -0
  56. data/lib/graphql/language/token.rb +10 -3
  57. data/lib/graphql/language.rb +3 -0
  58. data/lib/graphql/non_null_type.rb +1 -1
  59. data/lib/graphql/object_type.rb +1 -6
  60. data/lib/graphql/query/arguments.rb +63 -32
  61. data/lib/graphql/query/context.rb +32 -2
  62. data/lib/graphql/query/literal_input.rb +4 -1
  63. data/lib/graphql/query/null_context.rb +1 -1
  64. data/lib/graphql/query/result.rb +1 -1
  65. data/lib/graphql/query/variables.rb +21 -3
  66. data/lib/graphql/query.rb +19 -6
  67. data/lib/graphql/railtie.rb +109 -0
  68. data/lib/graphql/relay/connection_resolve.rb +3 -0
  69. data/lib/graphql/relay/connection_type.rb +5 -3
  70. data/lib/graphql/relay/edge_type.rb +2 -1
  71. data/lib/graphql/relay/global_id_resolve.rb +5 -1
  72. data/lib/graphql/relay/mongo_relation_connection.rb +40 -0
  73. data/lib/graphql/relay/mutation/instrumentation.rb +1 -1
  74. data/lib/graphql/relay/mutation/resolve.rb +5 -1
  75. data/lib/graphql/relay/relation_connection.rb +14 -19
  76. data/lib/graphql/relay/type_extensions.rb +30 -0
  77. data/lib/graphql/relay.rb +2 -0
  78. data/lib/graphql/scalar_type.rb +14 -2
  79. data/lib/graphql/schema/argument.rb +92 -0
  80. data/lib/graphql/schema/build_from_definition.rb +64 -18
  81. data/lib/graphql/schema/enum.rb +85 -0
  82. data/lib/graphql/schema/enum_value.rb +74 -0
  83. data/lib/graphql/schema/field.rb +372 -0
  84. data/lib/graphql/schema/finder.rb +153 -0
  85. data/lib/graphql/schema/input_object.rb +87 -0
  86. data/lib/graphql/schema/interface.rb +105 -0
  87. data/lib/graphql/schema/introspection_system.rb +93 -0
  88. data/lib/graphql/schema/late_bound_type.rb +32 -0
  89. data/lib/graphql/schema/list.rb +32 -0
  90. data/lib/graphql/schema/loader.rb +2 -2
  91. data/lib/graphql/schema/member/accepts_definition.rb +152 -0
  92. data/lib/graphql/schema/member/base_dsl_methods.rb +100 -0
  93. data/lib/graphql/schema/member/build_type.rb +137 -0
  94. data/lib/graphql/schema/member/cached_graphql_definition.rb +26 -0
  95. data/lib/graphql/schema/member/graphql_type_names.rb +21 -0
  96. data/lib/graphql/schema/member/has_arguments.rb +50 -0
  97. data/lib/graphql/schema/member/has_fields.rb +130 -0
  98. data/lib/graphql/schema/member/instrumentation.rb +115 -0
  99. data/lib/graphql/schema/member/type_system_helpers.rb +34 -0
  100. data/lib/graphql/schema/member.rb +28 -0
  101. data/lib/graphql/schema/middleware_chain.rb +5 -1
  102. data/lib/graphql/schema/mutation.rb +138 -0
  103. data/lib/graphql/schema/non_null.rb +38 -0
  104. data/lib/graphql/schema/object.rb +81 -0
  105. data/lib/graphql/schema/printer.rb +33 -266
  106. data/lib/graphql/schema/relay_classic_mutation.rb +87 -0
  107. data/lib/graphql/schema/rescue_middleware.rb +8 -7
  108. data/lib/graphql/schema/resolver.rb +122 -0
  109. data/lib/graphql/schema/scalar.rb +35 -0
  110. data/lib/graphql/schema/traversal.rb +102 -22
  111. data/lib/graphql/schema/union.rb +36 -0
  112. data/lib/graphql/schema/validation.rb +3 -2
  113. data/lib/graphql/schema.rb +381 -12
  114. data/lib/graphql/static_validation/definition_dependencies.rb +1 -1
  115. data/lib/graphql/static_validation/literal_validator.rb +16 -4
  116. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +6 -6
  117. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -1
  118. data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
  119. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +11 -1
  120. data/lib/graphql/static_validation/validation_context.rb +1 -1
  121. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +7 -5
  122. data/lib/graphql/subscriptions/instrumentation.rb +5 -1
  123. data/lib/graphql/subscriptions/serialize.rb +2 -0
  124. data/lib/graphql/subscriptions.rb +90 -16
  125. data/lib/graphql/tracing/data_dog_tracing.rb +49 -0
  126. data/lib/graphql/tracing/new_relic_tracing.rb +26 -0
  127. data/lib/graphql/tracing/platform_tracing.rb +20 -7
  128. data/lib/graphql/tracing/scout_tracing.rb +2 -2
  129. data/lib/graphql/tracing.rb +1 -0
  130. data/lib/graphql/unresolved_type_error.rb +3 -2
  131. data/lib/graphql/upgrader/member.rb +894 -0
  132. data/lib/graphql/upgrader/schema.rb +37 -0
  133. data/lib/graphql/version.rb +1 -1
  134. data/lib/graphql.rb +5 -25
  135. data/readme.md +2 -2
  136. data/spec/dummy/app/channels/graphql_channel.rb +23 -2
  137. data/spec/dummy/log/development.log +239 -0
  138. data/spec/dummy/log/test.log +410 -0
  139. data/spec/dummy/test/system/action_cable_subscription_test.rb +4 -0
  140. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-x/-xYZjAnuuzgR79fcznLTQtSdh6AARxu8FcQ_J6p7L3U.cache +0 -0
  141. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/13/13HiV12xyoQvT-1L39ZzLwMZxjyaGMiENmfw7f-QTIc.cache +0 -0
  142. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3W/3Wtf5pCWdqq0AB-iB0Y9uUNrTkruRxIEf1XFn_BETU0.cache +1 -0
  143. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5i/5iguGafb4hOn8262Kn8Q37ogNN9MxxQKGKNzHAzUcvI.cache +1 -0
  144. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8m/8mj2T6yy847Mc2Z7k3Xzh8O91hhVJt3NrPe8ASNDlIA.cache +1 -0
  145. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DT/DTQyMpr4ABZYQetsdRJ5A7S4jf1r3ie4FGOR7GZBNSs.cache +3 -0
  146. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dq/DqJ5_yJPrP5iLlOQyTQsjAVI5FE5LCVDkED0f7GgsSo.cache +3 -0
  147. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/F8/F8MUNRzORGFgr329fNM0xLaoWCXdv3BIalT7dsvLfjs.cache +0 -0
  148. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KB/KB07ZaKNC5uXJ7TjLi-WqnY6g7dq8wWp_8N3HNjBNxg.cache +0 -0
  149. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rw/RwDuCV-XpnCtjNkvhpJfBuxXMk0b5AD3L9eR6M-wcy0.cache +3 -0
  150. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/UL/ULdjhhb0bRuqmaG7XSZlFYzGYCXTDnqZuJBTWRlzqgw.cache +0 -0
  151. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Up/UpPNgh0yYoUsyMDh5zWqe_U6qJIyTC6-dxMMAs1vvlM.cache +1 -0
  152. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wg/Wguh-szFGTI1gaL6npYwPekMXflugRei7F_mOyRucXg.cache +0 -0
  153. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/X-/X-khLYMA9mqFRPg3zAi86mREDxpKl4bdKYp3uF6WHos.cache +0 -0
  154. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bi/BIkdhfxsezxM4q-HZ4oCNTq97WEJTigcq0tpX2cDvbY.cache +0 -0
  155. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ff/FfxmA4CMHQZT7exx0G7NS1Wpcnny0vzp-Jhc2H36bp8.cache +1 -0
  156. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gE/gEiiG4GZNy_djEjK2pHm_NgA-gyhLZhdQvo0Yt96GqE.cache +0 -0
  157. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gn/gnA9ZSqpjccNL2m8pe_jBvY6SinXlCzXDWyop83Od8s.cache +1 -0
  158. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lO/lOAan3cMwCE_Hli6gsDML88xFNfn0nxPmvrSkW7eEOw.cache +1 -0
  159. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m1/M1pv8MJEPLXGLvS8QxVh3DSO9cI4mRt5FHFWdrvUj6o.cache +2 -0
  160. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m7/m77qH7ZqH0_0SmwJbiKGDd-aLau1Dav847DC6ge46zY.cache +1 -0
  161. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sj/sjRjnjRB37lH2vrgtkdJ8Cz84__IJ978IuKTM7HcztI.cache +0 -0
  162. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/um/um1JrirR4hJhK-1rE-HywlyCi5ibgxHVrReiujZBWJM.cache +1 -0
  163. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v4/v4fwVytD7ITcE0_GDbslZEYud8a5Okm85fV1o7SDl6g.cache +0 -0
  164. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/v_/v_0PAQt0iipQjFP5zjgkkk9Stnpf4VzvnMv67d1Keuw.cache +1 -0
  165. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wd/wdT9U4MKxe1PyqNjVuCKMpCl3dxGCIRJIlwUTfh2DQU.cache +1 -0
  166. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xI/xIaxut_fEIhKBDqljTNwYaADK9kj3gG0ESrfHs-5_og.cache +3 -0
  167. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/y0/y0SJOqIx2fn1SKqOkAihsQow0trRJrSIyAswufVuoA8.cache +0 -0
  168. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zg/zgpzeaX-KZErHyGJ1aBH3ZusweNXMneVZule88XsIJI.cache +1 -0
  169. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zy/zYFltDy-8VC-uKq2BVEiJJyYXNFvVzAKuMlR3ZIYZsk.cache +0 -0
  170. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  171. data/spec/fixtures/upgrader/account.original.rb +19 -0
  172. data/spec/fixtures/upgrader/account.transformed.rb +20 -0
  173. data/spec/fixtures/upgrader/blame_range.original.rb +43 -0
  174. data/spec/fixtures/upgrader/blame_range.transformed.rb +30 -0
  175. data/spec/fixtures/upgrader/date_time.original.rb +24 -0
  176. data/spec/fixtures/upgrader/date_time.transformed.rb +23 -0
  177. data/spec/fixtures/upgrader/delete_project.original.rb +28 -0
  178. data/spec/fixtures/upgrader/delete_project.transformed.rb +27 -0
  179. data/spec/fixtures/upgrader/gist_order_field.original.rb +14 -0
  180. data/spec/fixtures/upgrader/gist_order_field.transformed.rb +13 -0
  181. data/spec/fixtures/upgrader/increment_count.original.rb +59 -0
  182. data/spec/fixtures/upgrader/increment_count.transformed.rb +50 -0
  183. data/spec/fixtures/upgrader/photo.original.rb +10 -0
  184. data/spec/fixtures/upgrader/photo.transformed.rb +12 -0
  185. data/spec/fixtures/upgrader/release_order.original.rb +15 -0
  186. data/spec/fixtures/upgrader/release_order.transformed.rb +14 -0
  187. data/spec/fixtures/upgrader/starrable.original.rb +49 -0
  188. data/spec/fixtures/upgrader/starrable.transformed.rb +46 -0
  189. data/spec/fixtures/upgrader/subscribable.original.rb +55 -0
  190. data/spec/fixtures/upgrader/subscribable.transformed.rb +51 -0
  191. data/spec/fixtures/upgrader/type_x.original.rb +65 -0
  192. data/spec/fixtures/upgrader/type_x.transformed.rb +56 -0
  193. data/spec/generators/graphql/function_generator_spec.rb +26 -0
  194. data/spec/generators/graphql/install_generator_spec.rb +1 -1
  195. data/spec/generators/graphql/loader_generator_spec.rb +24 -0
  196. data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
  197. data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
  198. data/spec/graphql/argument_spec.rb +21 -0
  199. data/spec/graphql/backtrace_spec.rb +10 -0
  200. data/spec/graphql/base_type_spec.rb +42 -0
  201. data/spec/graphql/boolean_type_spec.rb +3 -3
  202. data/spec/graphql/directive_spec.rb +3 -1
  203. data/spec/graphql/enum_type_spec.rb +18 -5
  204. data/spec/graphql/execution/execute_spec.rb +4 -4
  205. data/spec/graphql/execution/instrumentation_spec.rb +165 -0
  206. data/spec/graphql/execution/multiplex_spec.rb +2 -2
  207. data/spec/graphql/execution_error_spec.rb +18 -0
  208. data/spec/graphql/float_type_spec.rb +2 -2
  209. data/spec/graphql/id_type_spec.rb +1 -1
  210. data/spec/graphql/input_object_type_spec.rb +15 -2
  211. data/spec/graphql/int_type_spec.rb +2 -2
  212. data/spec/graphql/interface_type_spec.rb +12 -0
  213. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
  214. data/spec/graphql/introspection/schema_type_spec.rb +2 -0
  215. data/spec/graphql/language/block_string_spec.rb +70 -0
  216. data/spec/graphql/language/document_from_schema_definition_spec.rb +770 -0
  217. data/spec/graphql/language/generation_spec.rb +21 -186
  218. data/spec/graphql/language/lexer_spec.rb +21 -1
  219. data/spec/graphql/language/nodes_spec.rb +21 -12
  220. data/spec/graphql/language/parser_spec.rb +1 -1
  221. data/spec/graphql/language/printer_spec.rb +203 -0
  222. data/spec/graphql/object_type_spec.rb +22 -0
  223. data/spec/graphql/query/arguments_spec.rb +25 -15
  224. data/spec/graphql/query/context_spec.rb +18 -0
  225. data/spec/graphql/query/executor_spec.rb +2 -1
  226. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -8
  227. data/spec/graphql/query/variables_spec.rb +42 -1
  228. data/spec/graphql/query_spec.rb +31 -5
  229. data/spec/graphql/rake_task_spec.rb +3 -1
  230. data/spec/graphql/relay/base_connection_spec.rb +1 -1
  231. data/spec/graphql/relay/connection_instrumentation_spec.rb +2 -2
  232. data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
  233. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  234. data/spec/graphql/relay/mongo_relation_connection_spec.rb +474 -0
  235. data/spec/graphql/relay/mutation_spec.rb +9 -7
  236. data/spec/graphql/relay/range_add_spec.rb +5 -1
  237. data/spec/graphql/relay/relation_connection_spec.rb +65 -1
  238. data/spec/graphql/schema/argument_spec.rb +87 -0
  239. data/spec/graphql/schema/build_from_definition_spec.rb +89 -5
  240. data/spec/graphql/schema/enum_spec.rb +74 -0
  241. data/spec/graphql/schema/field_spec.rb +225 -0
  242. data/spec/graphql/schema/finder_spec.rb +135 -0
  243. data/spec/graphql/schema/input_object_spec.rb +111 -0
  244. data/spec/graphql/schema/instrumentation_spec.rb +40 -0
  245. data/spec/graphql/schema/interface_spec.rb +185 -0
  246. data/spec/graphql/schema/introspection_system_spec.rb +39 -0
  247. data/spec/graphql/schema/member/accepts_definition_spec.rb +111 -0
  248. data/spec/graphql/schema/member/build_type_spec.rb +17 -0
  249. data/spec/graphql/schema/member/has_fields_spec.rb +129 -0
  250. data/spec/graphql/schema/member/type_system_helpers_spec.rb +63 -0
  251. data/spec/graphql/schema/mutation_spec.rb +148 -0
  252. data/spec/graphql/schema/object_spec.rb +175 -0
  253. data/spec/graphql/schema/printer_spec.rb +111 -15
  254. data/spec/graphql/schema/relay_classic_mutation_spec.rb +38 -0
  255. data/spec/graphql/schema/rescue_middleware_spec.rb +11 -0
  256. data/spec/graphql/schema/resolver_spec.rb +131 -0
  257. data/spec/graphql/schema/scalar_spec.rb +95 -0
  258. data/spec/graphql/schema/traversal_spec.rb +31 -0
  259. data/spec/graphql/schema/union_spec.rb +65 -0
  260. data/spec/graphql/schema/validation_spec.rb +1 -1
  261. data/spec/graphql/schema/warden_spec.rb +11 -11
  262. data/spec/graphql/schema_spec.rb +55 -12
  263. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +10 -2
  264. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  265. data/spec/graphql/string_type_spec.rb +3 -3
  266. data/spec/graphql/subscriptions_spec.rb +273 -184
  267. data/spec/graphql/tracing/active_support_notifications_tracing_spec.rb +1 -1
  268. data/spec/graphql/tracing/new_relic_tracing_spec.rb +47 -0
  269. data/spec/graphql/tracing/platform_tracing_spec.rb +60 -1
  270. data/spec/graphql/union_type_spec.rb +1 -1
  271. data/spec/graphql/upgrader/member_spec.rb +516 -0
  272. data/spec/graphql/upgrader/schema_spec.rb +82 -0
  273. data/spec/spec_helper.rb +8 -0
  274. data/spec/support/dummy/schema.rb +53 -24
  275. data/spec/support/jazz.rb +544 -0
  276. data/spec/support/lazy_helpers.rb +21 -23
  277. data/spec/support/new_relic.rb +24 -0
  278. data/spec/support/star_trek/data.rb +109 -0
  279. data/spec/support/star_trek/schema.rb +388 -0
  280. data/spec/support/star_wars/data.rb +6 -7
  281. data/spec/support/star_wars/schema.rb +127 -171
  282. metadata +233 -11
  283. data/lib/graphql/introspection/arguments_field.rb +0 -7
  284. data/lib/graphql/introspection/enum_values_field.rb +0 -18
  285. data/lib/graphql/introspection/fields_field.rb +0 -13
  286. data/lib/graphql/introspection/input_fields_field.rb +0 -12
  287. data/lib/graphql/introspection/interfaces_field.rb +0 -11
  288. data/lib/graphql/introspection/of_type_field.rb +0 -6
  289. data/lib/graphql/introspection/possible_types_field.rb +0 -11
@@ -3,7 +3,10 @@ require "graphql/schema/base_64_encoder"
3
3
  require "graphql/schema/catchall_middleware"
4
4
  require "graphql/schema/default_parse_error"
5
5
  require "graphql/schema/default_type_error"
6
+ require "graphql/schema/finder"
6
7
  require "graphql/schema/invalid_type_error"
8
+ require "graphql/schema/introspection_system"
9
+ require "graphql/schema/late_bound_type"
7
10
  require "graphql/schema/middleware_chain"
8
11
  require "graphql/schema/null_mask"
9
12
  require "graphql/schema/possible_types"
@@ -16,6 +19,23 @@ require "graphql/schema/validation"
16
19
  require "graphql/schema/warden"
17
20
  require "graphql/schema/build_from_definition"
18
21
 
22
+
23
+ require "graphql/schema/member"
24
+ require "graphql/schema/list"
25
+ require "graphql/schema/non_null"
26
+ require "graphql/schema/argument"
27
+ require "graphql/schema/enum_value"
28
+ require "graphql/schema/enum"
29
+ require "graphql/schema/field"
30
+ require "graphql/schema/input_object"
31
+ require "graphql/schema/interface"
32
+ require "graphql/schema/resolver"
33
+ require "graphql/schema/mutation"
34
+ require "graphql/schema/relay_classic_mutation"
35
+ require "graphql/schema/object"
36
+ require "graphql/schema/scalar"
37
+ require "graphql/schema/union"
38
+
19
39
  module GraphQL
20
40
  # A GraphQL schema which may be queried with {GraphQL::Query}.
21
41
  #
@@ -49,6 +69,7 @@ module GraphQL
49
69
  # end
50
70
  #
51
71
  class Schema
72
+ extend GraphQL::Schema::Member::AcceptsDefinition
52
73
  include GraphQL::Define::InstanceDefinable
53
74
  accepts_definitions \
54
75
  :query, :mutation, :subscription,
@@ -80,7 +101,9 @@ module GraphQL
80
101
  :orphan_types, :directives,
81
102
  :query_analyzers, :multiplex_analyzers, :instrumenters, :lazy_methods,
82
103
  :cursor_encoder,
83
- :raise_definition_error
104
+ :ast_node,
105
+ :raise_definition_error,
106
+ :introspection_namespace
84
107
 
85
108
  # Single, long-lived instance of the provided subscriptions class, if there is one.
86
109
  # @return [GraphQL::Subscriptions]
@@ -93,6 +116,10 @@ module GraphQL
93
116
  # @see {Query.new} for query-specific filters with `except:`
94
117
  attr_accessor :default_mask
95
118
 
119
+ # @see {GraphQL::Query::Context} The parent class of these classes
120
+ # @return [Class] Instantiated for each query
121
+ attr_accessor :context_class
122
+
96
123
  class << self
97
124
  attr_accessor :default_execution_strategy
98
125
  end
@@ -137,6 +164,9 @@ module GraphQL
137
164
  @subscription_execution_strategy = self.class.default_execution_strategy
138
165
  @default_mask = GraphQL::Schema::NullMask
139
166
  @rebuilding_artifacts = false
167
+ @context_class = GraphQL::Query::Context
168
+ @introspection_namespace = nil
169
+ @introspection_system = nil
140
170
  end
141
171
 
142
172
  def initialize_copy(other)
@@ -164,6 +194,7 @@ module GraphQL
164
194
  # This will be rebuilt when it's requested
165
195
  # or during a later `define` call
166
196
  @types = nil
197
+ @introspection_system = nil
167
198
  end
168
199
 
169
200
  def rescue_from(*args, &block)
@@ -174,6 +205,9 @@ module GraphQL
174
205
  rescue_middleware.remove_handler(*args, &block)
175
206
  end
176
207
 
208
+ # For forwards-compatibility with Schema classes
209
+ alias :graphql_definition :itself
210
+
177
211
  # Validate a query string according to this schema.
178
212
  # @param string_or_document [String, GraphQL::Language::Nodes::Document]
179
213
  # @return [Array<GraphQL::StaticValidation::Message>]
@@ -239,6 +273,14 @@ module GraphQL
239
273
  end
240
274
  end
241
275
 
276
+ # @api private
277
+ def introspection_system
278
+ @introspection_system ||= begin
279
+ rebuild_artifacts
280
+ @introspection_system
281
+ end
282
+ end
283
+
242
284
  # Returns a list of Arguments and Fields referencing a certain type
243
285
  # @param type_name [String]
244
286
  # @return [Hash]
@@ -300,6 +342,19 @@ module GraphQL
300
342
  }
301
343
  end
302
344
 
345
+ # Search for a schema member using a string path
346
+ # @example Finding a Field
347
+ # Schema.find("Ensemble.musicians")
348
+ #
349
+ # @see {GraphQL::Schema::Finder} for more examples
350
+ # @param path [String] A dot-separated path to the member
351
+ # @raise [Schema::Finder::MemberNotFoundError] if path could not be found
352
+ # @return [GraphQL::BaseType, GraphQL::Field, GraphQL::Argument, GraphQL::Directive] A GraphQL Schema Member
353
+ def find(path)
354
+ rebuild_artifacts unless defined?(@finder)
355
+ @find_cache[path] ||= @finder.find(path)
356
+ end
357
+
303
358
  # Resolve field named `field_name` for type `parent_type`.
304
359
  # Handles dynamic fields `__typename`, `__type` and `__schema`, too
305
360
  # @param parent_type [String, GraphQL::BaseType]
@@ -320,12 +375,10 @@ module GraphQL
320
375
  defined_field = @instrumented_field_map[parent_type_name][field_name]
321
376
  if defined_field
322
377
  defined_field
323
- elsif field_name == "__typename"
324
- GraphQL::Introspection::TypenameField
325
- elsif field_name == "__schema" && parent_type == query
326
- GraphQL::Introspection::SchemaField
327
- elsif field_name == "__type" && parent_type == query
328
- GraphQL::Introspection::TypeByNameField
378
+ elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
379
+ entry_point_field
380
+ elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
381
+ dynamic_field
329
382
  else
330
383
  nil
331
384
  end
@@ -386,6 +439,18 @@ module GraphQL
386
439
  # @param ctx [GraphQL::Query::Context] The context for the current query
387
440
  # @return [GraphQL::ObjectType] The type for exposing `object` in GraphQL
388
441
  def resolve_type(type, object, ctx = :__undefined__)
442
+ check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
443
+ if @resolve_type_proc.nil?
444
+ raise(NotImplementedError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
445
+ end
446
+ @resolve_type_proc.call(ok_type, ok_object, ok_ctx)
447
+ end
448
+ end
449
+
450
+ # This is a compatibility hack so that instance-level and class-level
451
+ # methods can get correctness checks without calling one another
452
+ # @api private
453
+ def check_resolved_type(type, object, ctx = :__undefined__)
389
454
  if ctx == :__undefined__
390
455
  # Old method signature
391
456
  ctx = object
@@ -393,15 +458,20 @@ module GraphQL
393
458
  type = nil
394
459
  end
395
460
 
461
+ if object.is_a?(GraphQL::Schema::Object)
462
+ object = object.object
463
+ end
464
+
396
465
  # Prefer a type-local function; fall back to the schema-level function
397
466
  type_proc = type && type.resolve_type_proc
398
467
  type_result = if type_proc
399
468
  type_proc.call(object, ctx)
400
469
  else
401
- if @resolve_type_proc.nil?
402
- raise(NotImplementedError, "Can't determine GraphQL type for: #{object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
403
- end
404
- @resolve_type_proc.call(type, object, ctx)
470
+ yield(type, object, ctx)
471
+ end
472
+
473
+ if type_result.respond_to?(:graphql_definition)
474
+ type_result = type_result.graphql_definition
405
475
  end
406
476
 
407
477
  if type_result.nil?
@@ -542,13 +612,19 @@ module GraphQL
542
612
  GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
543
613
  end
544
614
 
615
+ # Return the GraphQL::Language::Document IDL AST for the schema
616
+ # @return [GraphQL::Language::Document]
617
+ def to_document
618
+ GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
619
+ end
620
+
545
621
  # Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
546
622
  # @param context [Hash]
547
623
  # @param only [<#call(member, ctx)>]
548
624
  # @param except [<#call(member, ctx)>]
549
625
  # @return [Hash] GraphQL result
550
626
  def as_json(only: nil, except: nil, context: {})
551
- execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context)
627
+ execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
552
628
  end
553
629
 
554
630
  # Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
@@ -558,6 +634,295 @@ module GraphQL
558
634
  JSON.pretty_generate(as_json(*args))
559
635
  end
560
636
 
637
+ class << self
638
+ extend Forwardable
639
+ # For compatibility, these methods all:
640
+ # - Cause the Schema instance to be created, if it hasn't been created yet
641
+ # - Delegate to that instance
642
+ # Eventually, the methods will be moved into this class, removing the need for the singleton.
643
+ def_delegators :graphql_definition,
644
+ # Schema structure
645
+ :as_json, :to_json, :to_document, :to_definition,
646
+ # Execution
647
+ :execute, :multiplex,
648
+ :static_validator, :introspection_system,
649
+ :query_analyzers, :middleware, :tracers, :instrumenters,
650
+ :query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
651
+ :validate, :multiplex_analyzers, :lazy?, :lazy_method_name,
652
+ # Configuration
653
+ :max_complexity=, :max_depth=,
654
+ :metadata,
655
+ :default_filter, :redefine,
656
+ :id_from_object_proc, :object_from_id_proc,
657
+ :id_from_object=, :object_from_id=, :type_error,
658
+ :remove_handler,
659
+ # Members
660
+ :types, :get_fields, :find,
661
+ :root_type_for_operation,
662
+ :subscriptions,
663
+ :union_memberships,
664
+ :get_field, :root_types, :references_to, :type_from_ast,
665
+ :possible_types, :get_field
666
+
667
+ def graphql_definition
668
+ @graphql_definition ||= to_graphql
669
+ end
670
+
671
+ def use(plugin, options = {})
672
+ plugins << [plugin, options]
673
+ end
674
+
675
+ def plugins
676
+ @plugins ||= []
677
+ end
678
+
679
+ def to_graphql
680
+ schema_defn = self.new
681
+ schema_defn.raise_definition_error = true
682
+ schema_defn.query = query
683
+ schema_defn.mutation = mutation
684
+ schema_defn.subscription = subscription
685
+ schema_defn.max_complexity = max_complexity
686
+ schema_defn.max_depth = max_depth
687
+ schema_defn.default_max_page_size = default_max_page_size
688
+ schema_defn.orphan_types = orphan_types
689
+ schema_defn.directives = directives
690
+ schema_defn.introspection_namespace = introspection
691
+ schema_defn.resolve_type = method(:resolve_type)
692
+ schema_defn.object_from_id = method(:object_from_id)
693
+ schema_defn.id_from_object = method(:id_from_object)
694
+ schema_defn.type_error = method(:type_error)
695
+ schema_defn.context_class = context_class
696
+ schema_defn.cursor_encoder = cursor_encoder
697
+ schema_defn.tracers.concat(defined_tracers)
698
+ schema_defn.query_analyzers.concat(defined_query_analyzers)
699
+ schema_defn.middleware.concat(defined_middleware)
700
+ schema_defn.multiplex_analyzers.concat(defined_multiplex_analyzers)
701
+ defined_instrumenters.each do |step, insts|
702
+ insts.each do |inst|
703
+ schema_defn.instrumenters[step] << inst
704
+ end
705
+ end
706
+ schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
707
+ lazy_classes.each do |lazy_class, value_method|
708
+ schema_defn.lazy_methods.set(lazy_class, value_method)
709
+ end
710
+ if @rescues
711
+ @rescues.each do |err_class, handler|
712
+ schema_defn.rescue_from(err_class, &handler)
713
+ end
714
+ end
715
+
716
+ if plugins.any?
717
+ schema_plugins = plugins
718
+ # TODO don't depend on .define
719
+ schema_defn = schema_defn.redefine do
720
+ schema_plugins.each do |plugin, options|
721
+ if options.any?
722
+ use(plugin, **options)
723
+ else
724
+ use(plugin)
725
+ end
726
+ end
727
+ end
728
+ end
729
+ schema_defn.send(:rebuild_artifacts)
730
+
731
+ schema_defn
732
+ end
733
+
734
+ def query(new_query_object = nil)
735
+ if new_query_object
736
+ @query_object = new_query_object
737
+ else
738
+ @query_object.respond_to?(:graphql_definition) ? @query_object.graphql_definition : @query_object
739
+ end
740
+ end
741
+
742
+ def mutation(new_mutation_object = nil)
743
+ if new_mutation_object
744
+ @mutation_object = new_mutation_object
745
+ else
746
+ @mutation_object.respond_to?(:graphql_definition) ? @mutation_object.graphql_definition : @mutation_object
747
+ end
748
+ end
749
+
750
+ def subscription(new_subscription_object = nil)
751
+ if new_subscription_object
752
+ @subscription_object = new_subscription_object
753
+ else
754
+ @subscription_object.respond_to?(:graphql_definition) ? @subscription_object.graphql_definition : @subscription_object
755
+ end
756
+ end
757
+
758
+ def introspection(new_introspection_namespace = nil)
759
+ if new_introspection_namespace
760
+ @introspection = new_introspection_namespace
761
+ else
762
+ @introspection
763
+ end
764
+ end
765
+
766
+ def cursor_encoder(new_encoder = nil)
767
+ if new_encoder
768
+ @cursor_encoder = new_encoder
769
+ end
770
+ @cursor_encoder || Base64Encoder
771
+ end
772
+
773
+ def default_max_page_size(new_default_max_page_size = nil)
774
+ if new_default_max_page_size
775
+ @default_max_page_size = new_default_max_page_size
776
+ else
777
+ @default_max_page_size
778
+ end
779
+ end
780
+
781
+ def max_complexity(max_complexity = nil)
782
+ if max_complexity
783
+ @max_complexity = max_complexity
784
+ else
785
+ @max_complexity
786
+ end
787
+ end
788
+
789
+ def max_depth(new_max_depth = nil)
790
+ if new_max_depth
791
+ @max_depth = new_max_depth
792
+ else
793
+ @max_depth
794
+ end
795
+ end
796
+
797
+ def orphan_types(*new_orphan_types)
798
+ if new_orphan_types.any?
799
+ @orphan_types = new_orphan_types.flatten
800
+ else
801
+ @orphan_types || []
802
+ end
803
+ end
804
+
805
+ def default_execution_strategy
806
+ if superclass <= GraphQL::Schema
807
+ superclass.default_execution_strategy
808
+ else
809
+ @default_execution_strategy
810
+ end
811
+ end
812
+
813
+ def context_class(new_context_class = nil)
814
+ if new_context_class
815
+ @context_class = new_context_class
816
+ else
817
+ @context_class || GraphQL::Query::Context
818
+ end
819
+ end
820
+
821
+ def rescue_from(err_class, &handler_block)
822
+ @rescues ||= {}
823
+ @rescues[err_class] = handler_block
824
+ end
825
+
826
+ def resolve_type(type, obj, ctx)
827
+ raise NotImplementedError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
828
+ end
829
+
830
+ def object_from_id(node_id, ctx)
831
+ raise NotImplementedError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to use the `node` field (tried to load from id `#{node_id}`)"
832
+ end
833
+
834
+ def id_from_object(object, type, ctx)
835
+ raise NotImplementedError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
836
+ end
837
+
838
+ def type_error(type_err, ctx)
839
+ DefaultTypeError.call(type_err, ctx)
840
+ end
841
+
842
+ def lazy_resolve(lazy_class, value_method)
843
+ lazy_classes[lazy_class] = value_method
844
+ end
845
+
846
+ def instrument(instrument_step, instrumenter, options = {})
847
+ step = if instrument_step == :field && options[:after_built_ins]
848
+ :field_after_built_ins
849
+ else
850
+ instrument_step
851
+ end
852
+ defined_instrumenters[step] << instrumenter
853
+ end
854
+
855
+ def directives(new_directives = nil)
856
+ if new_directives
857
+ @directives = new_directives.reduce({}) { |m, d| m[d.name] = d; m }
858
+ end
859
+
860
+ @directives ||= directives(DIRECTIVES)
861
+ end
862
+
863
+ def tracer(new_tracer)
864
+ defined_tracers << new_tracer
865
+ end
866
+
867
+ def query_analyzer(new_analyzer)
868
+ defined_query_analyzers << new_analyzer
869
+ end
870
+
871
+ def middleware(new_middleware = nil)
872
+ if new_middleware
873
+ defined_middleware << new_middleware
874
+ else
875
+ graphql_definition.middleware
876
+ end
877
+ end
878
+
879
+ def multiplex_analyzer(new_analyzer)
880
+ defined_multiplex_analyzers << new_analyzer
881
+ end
882
+
883
+ private
884
+
885
+ def lazy_classes
886
+ @lazy_classes ||= {}
887
+ end
888
+
889
+ def defined_instrumenters
890
+ @defined_instrumenters ||= Hash.new { |h,k| h[k] = [] }
891
+ end
892
+
893
+ def defined_tracers
894
+ @defined_tracers ||= []
895
+ end
896
+
897
+ def defined_query_analyzers
898
+ @defined_query_analyzers ||= []
899
+ end
900
+
901
+ def defined_middleware
902
+ @defined_middleware ||= []
903
+ end
904
+
905
+ def defined_multiplex_analyzers
906
+ @defined_multiplex_analyzers ||= []
907
+ end
908
+ end
909
+
910
+
911
+ def self.inherited(child_class)
912
+ child_class.singleton_class.class_eval do
913
+ prepend(MethodWrappers)
914
+ end
915
+ end
916
+
917
+ module MethodWrappers
918
+ # Wrap the user-provided resolve-type in a correctness check
919
+ def resolve_type(type, obj, ctx = :__undefined__)
920
+ graphql_definition.check_resolved_type(type, obj, ctx) do |ok_type, ok_obj, ok_ctx|
921
+ super(ok_type, ok_obj, ok_ctx)
922
+ end
923
+ end
924
+ end
925
+
561
926
  protected
562
927
 
563
928
  def rescues?
@@ -578,6 +943,7 @@ module GraphQL
578
943
  GraphQL::Relay::ConnectionInstrumentation,
579
944
  GraphQL::Relay::EdgesInstrumentation,
580
945
  GraphQL::Relay::Mutation::Instrumentation,
946
+ GraphQL::Schema::Member::Instrumentation,
581
947
  ]
582
948
 
583
949
  def rebuild_artifacts
@@ -585,12 +951,15 @@ module GraphQL
585
951
  raise CyclicalDefinitionError, "Part of the schema build process re-triggered the schema build process, causing an infinite loop. Avoid using Schema#types, Schema#possible_types, and Schema#get_field during schema build."
586
952
  else
587
953
  @rebuilding_artifacts = true
954
+ @introspection_system = Schema::IntrospectionSystem.new(self)
588
955
  traversal = Traversal.new(self)
589
956
  @types = traversal.type_map
590
957
  @root_types = [query, mutation, subscription]
591
958
  @instrumented_field_map = traversal.instrumented_field_map
592
959
  @type_reference_map = traversal.type_reference_map
593
960
  @union_memberships = traversal.union_memberships
961
+ @find_cache = {}
962
+ @finder = Finder.new(self)
594
963
  end
595
964
  ensure
596
965
  @rebuilding_artifacts = false
@@ -103,7 +103,7 @@ module GraphQL
103
103
  end
104
104
 
105
105
  class NodeWithPath
106
- extend GraphQL::Delegate
106
+ extend Forwardable
107
107
  attr_reader :node, :path
108
108
  def initialize(node, path)
109
109
  @node = node
@@ -16,23 +16,35 @@ module GraphQL
16
16
  elsif type.kind.list?
17
17
  item_type = type.of_type
18
18
  ensure_array(ast_value).all? { |val| validate(val, item_type) }
19
- elsif type.kind.scalar? && !ast_value.is_a?(GraphQL::Language::Nodes::AbstractNode) && !ast_value.is_a?(Array)
19
+ elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
20
+ true
21
+ elsif type.kind.scalar? && constant_scalar?(ast_value)
20
22
  type.valid_input?(ast_value, @context)
21
23
  elsif type.kind.enum? && ast_value.is_a?(GraphQL::Language::Nodes::Enum)
22
24
  type.valid_input?(ast_value.name, @context)
23
25
  elsif type.kind.input_object? && ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
24
26
  required_input_fields_are_present(type, ast_value) &&
25
27
  present_input_field_values_are_valid(type, ast_value)
26
- elsif ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
27
- true
28
28
  else
29
29
  false
30
30
  end
31
31
  end
32
32
 
33
-
34
33
  private
35
34
 
35
+ # The GraphQL grammar supports variables embedded within scalars but graphql.js
36
+ # doesn't support it so we won't either for simplicity
37
+ def constant_scalar?(ast_value)
38
+ if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
39
+ false
40
+ elsif ast_value.is_a?(Array)
41
+ ast_value.all? { |element| constant_scalar?(element) }
42
+ elsif ast_value.is_a?(GraphQL::Language::Nodes::InputObject)
43
+ ast_value.arguments.all? { |arg| constant_scalar?(arg.value) }
44
+ else
45
+ true
46
+ end
47
+ end
36
48
 
37
49
  def required_input_fields_are_present(type, ast_node)
38
50
  required_field_names = @warden.arguments(type)
@@ -16,14 +16,14 @@ module GraphQL
16
16
  private
17
17
 
18
18
  def validate_field(context, ast_field, parent_type, parent)
19
- if parent_type.kind.union? && ast_field.name != '__typename'
20
- context.errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent, context: context)
21
- return GraphQL::Language::Visitor::SKIP
22
- end
23
-
24
19
  field = context.warden.get_field(parent_type, ast_field.name)
20
+
25
21
  if field.nil?
26
- context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
22
+ if parent_type.kind.union?
23
+ context.errors << message("Selections can't be made directly on unions (see selections on #{parent_type.name})", parent, context: context)
24
+ else
25
+ context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
26
+ end
27
27
  return GraphQL::Language::Visitor::SKIP
28
28
  end
29
29
  end
@@ -24,7 +24,11 @@ module GraphQL
24
24
  msg = if resolved_type.nil?
25
25
  nil
26
26
  elsif resolved_type.kind.scalar? && ast_node.selections.any?
27
- "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
27
+ if ast_node.selections.first.is_a?(GraphQL::Language::Nodes::InlineFragment)
28
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has inline fragments [#{ast_node.selections.map(&:type).map(&:name).join(", ")}])"
29
+ else
30
+ "Selections can't be made on scalars (%{node_name} returns #{resolved_type.name} but has selections [#{ast_node.selections.map(&:name).join(", ")}])"
31
+ end
28
32
  elsif resolved_type.kind.object? && ast_node.selections.none?
29
33
  "Objects must have selections (%{node_name} returns #{resolved_type.name} but has no selections)"
30
34
  else
@@ -2,6 +2,9 @@
2
2
  module GraphQL
3
3
  module StaticValidation
4
4
  class FieldsWillMerge
5
+ # Special handling for fields without arguments
6
+ NO_ARGS = {}.freeze
7
+
5
8
  def validate(context)
6
9
  context.each_irep_node do |node|
7
10
  if node.ast_nodes.size > 1
@@ -16,15 +19,19 @@ module GraphQL
16
19
 
17
20
  # Check for incompatible / non-identical arguments on this node:
18
21
  args = node.ast_nodes.map do |n|
19
- n.arguments.reduce({}) do |memo, a|
20
- arg_value = a.value
21
- memo[a.name] = case arg_value
22
- when GraphQL::Language::Nodes::AbstractNode
23
- arg_value.to_query_string
24
- else
25
- GraphQL::Language.serialize(arg_value)
22
+ if n.arguments.any?
23
+ n.arguments.reduce({}) do |memo, a|
24
+ arg_value = a.value
25
+ memo[a.name] = case arg_value
26
+ when GraphQL::Language::Nodes::AbstractNode
27
+ arg_value.to_query_string
28
+ else
29
+ GraphQL::Language.serialize(arg_value)
30
+ end
31
+ memo
26
32
  end
27
- memo
33
+ else
34
+ NO_ARGS
28
35
  end
29
36
  end
30
37
  args.uniq!
@@ -102,7 +102,17 @@ module GraphQL
102
102
  def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
103
103
  spreads = spreads_for_context[node] - visited_fragments
104
104
  spreads.each do |spread_name|
105
- def_node, variables = fragment_definitions.find { |def_node, vars| def_node.name == spread_name }
105
+ def_node = nil
106
+ variables = nil
107
+ # Implement `.find` by hand to avoid Ruby's internal allocations
108
+ fragment_definitions.each do |frag_def_node, vars|
109
+ if frag_def_node.name == spread_name
110
+ def_node = frag_def_node
111
+ variables = vars
112
+ break
113
+ end
114
+ end
115
+
106
116
  next if !def_node
107
117
  visited_fragments << spread_name
108
118
  variables.each do |name, child_usage|
@@ -12,7 +12,7 @@ module GraphQL
12
12
  # It also provides limited access to the {TypeStack} instance,
13
13
  # which tracks state as you climb in and out of different fields.
14
14
  class ValidationContext
15
- extend GraphQL::Delegate
15
+ extend Forwardable
16
16
 
17
17
  attr_reader :query, :schema,
18
18
  :document, :errors, :visitor,