rails-graphql 0.2.1 → 1.0.0.beta

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 (297) hide show
  1. checksums.yaml +4 -4
  2. data/ext/console.rb +18 -0
  3. data/ext/extconf.h +3 -0
  4. data/ext/extconf.rb +1 -54
  5. data/ext/gql_parser.c +646 -0
  6. data/ext/shared.c +482 -0
  7. data/ext/shared.h +177 -0
  8. data/lib/gql_parser.so +0 -0
  9. data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
  10. data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
  11. data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
  12. data/lib/rails/graphql/alternative/field_set.rb +36 -0
  13. data/lib/rails/graphql/alternative/mutation.rb +17 -0
  14. data/lib/rails/graphql/alternative/query.rb +93 -0
  15. data/lib/rails/graphql/alternative/subscription.rb +17 -0
  16. data/lib/rails/graphql/alternative.rb +20 -0
  17. data/lib/rails/graphql/argument.rb +21 -24
  18. data/lib/rails/graphql/callback.rb +24 -9
  19. data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
  20. data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
  21. data/lib/rails/graphql/collectors/json_collector.rb +22 -17
  22. data/lib/rails/graphql/collectors.rb +4 -4
  23. data/lib/rails/graphql/config.rb +130 -15
  24. data/lib/rails/graphql/directive/cached_directive.rb +33 -0
  25. data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
  26. data/lib/rails/graphql/directive/include_directive.rb +5 -4
  27. data/lib/rails/graphql/directive/skip_directive.rb +5 -4
  28. data/lib/rails/graphql/directive.rb +118 -63
  29. data/lib/rails/graphql/errors.rb +33 -4
  30. data/lib/rails/graphql/event.rb +16 -5
  31. data/lib/rails/graphql/field/authorized_field.rb +19 -3
  32. data/lib/rails/graphql/field/input_field.rb +11 -10
  33. data/lib/rails/graphql/field/mutation_field.rb +42 -7
  34. data/lib/rails/graphql/field/output_field.rb +102 -13
  35. data/lib/rails/graphql/field/proxied_field.rb +31 -22
  36. data/lib/rails/graphql/field/resolved_field.rb +26 -24
  37. data/lib/rails/graphql/field/scoped_config.rb +10 -4
  38. data/lib/rails/graphql/field/subscription_field.rb +140 -0
  39. data/lib/rails/graphql/field/typed_field.rb +43 -22
  40. data/lib/rails/graphql/field.rb +70 -56
  41. data/lib/rails/graphql/global_id.rb +85 -0
  42. data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
  43. data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
  44. data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
  45. data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -0
  46. data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
  47. data/lib/rails/graphql/helpers/instantiable.rb +15 -0
  48. data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
  49. data/lib/rails/graphql/helpers/registerable.rb +44 -62
  50. data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
  51. data/lib/rails/graphql/helpers/with_arguments.rb +31 -27
  52. data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
  53. data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
  54. data/lib/rails/graphql/helpers/with_description.rb +71 -0
  55. data/lib/rails/graphql/helpers/with_directives.rb +54 -30
  56. data/lib/rails/graphql/helpers/with_events.rb +21 -23
  57. data/lib/rails/graphql/helpers/with_fields.rb +76 -22
  58. data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
  59. data/lib/rails/graphql/helpers/with_name.rb +43 -0
  60. data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
  61. data/lib/rails/graphql/helpers/with_owner.rb +8 -7
  62. data/lib/rails/graphql/helpers/with_schema_fields.rb +137 -55
  63. data/lib/rails/graphql/helpers/with_validator.rb +9 -9
  64. data/lib/rails/graphql/helpers.rb +10 -3
  65. data/lib/rails/graphql/introspection.rb +43 -36
  66. data/lib/rails/graphql/railtie.rb +88 -33
  67. data/lib/rails/graphql/railties/base_generator.rb +3 -9
  68. data/lib/rails/graphql/railties/channel.rb +157 -0
  69. data/lib/rails/graphql/railties/controller.rb +62 -17
  70. data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
  71. data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
  72. data/lib/rails/graphql/request/arguments.rb +24 -49
  73. data/lib/rails/graphql/request/backtrace.rb +191 -0
  74. data/lib/rails/graphql/request/component/field.rb +86 -65
  75. data/lib/rails/graphql/request/component/fragment.rb +72 -24
  76. data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
  77. data/lib/rails/graphql/request/component/operation.rb +63 -31
  78. data/lib/rails/graphql/request/component/spread.rb +68 -25
  79. data/lib/rails/graphql/request/component/typename.rb +27 -12
  80. data/lib/rails/graphql/request/component.rb +75 -36
  81. data/lib/rails/graphql/request/context.rb +18 -8
  82. data/lib/rails/graphql/request/errors.rb +16 -6
  83. data/lib/rails/graphql/request/event.rb +19 -8
  84. data/lib/rails/graphql/request/helpers/directives.rb +68 -27
  85. data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
  86. data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
  87. data/lib/rails/graphql/request/prepared_data.rb +98 -0
  88. data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
  89. data/lib/rails/graphql/request/steps/organizable.rb +110 -48
  90. data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
  91. data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
  92. data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
  93. data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
  94. data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
  95. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
  96. data/lib/rails/graphql/request/strategy.rb +131 -75
  97. data/lib/rails/graphql/request/subscription.rb +80 -0
  98. data/lib/rails/graphql/request.rb +305 -86
  99. data/lib/rails/graphql/schema.rb +240 -48
  100. data/lib/rails/graphql/shortcuts.rb +22 -3
  101. data/lib/rails/graphql/source/active_record/builders.rb +49 -35
  102. data/lib/rails/graphql/source/active_record_source.rb +70 -54
  103. data/lib/rails/graphql/source/base.rb +111 -0
  104. data/lib/rails/graphql/source/builder.rb +128 -0
  105. data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
  106. data/lib/rails/graphql/source.rb +89 -213
  107. data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
  108. data/lib/rails/graphql/subscription/provider/base.rb +191 -0
  109. data/lib/rails/graphql/subscription/provider.rb +18 -0
  110. data/lib/rails/graphql/subscription/store/base.rb +145 -0
  111. data/lib/rails/graphql/subscription/store/memory.rb +127 -0
  112. data/lib/rails/graphql/subscription/store.rb +19 -0
  113. data/lib/rails/graphql/subscription.rb +17 -0
  114. data/lib/rails/graphql/to_gql.rb +29 -32
  115. data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
  116. data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
  117. data/lib/rails/graphql/type/enum.rb +34 -48
  118. data/lib/rails/graphql/type/input.rb +74 -23
  119. data/lib/rails/graphql/type/interface.rb +16 -26
  120. data/lib/rails/graphql/type/object/directive_object.rb +4 -4
  121. data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
  122. data/lib/rails/graphql/type/object/field_object.rb +24 -6
  123. data/lib/rails/graphql/type/object/input_value_object.rb +3 -3
  124. data/lib/rails/graphql/type/object/schema_object.rb +5 -8
  125. data/lib/rails/graphql/type/object/type_object.rb +29 -19
  126. data/lib/rails/graphql/type/object.rb +26 -23
  127. data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
  128. data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
  129. data/lib/rails/graphql/type/scalar/binary_scalar.rb +3 -3
  130. data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
  131. data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
  132. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
  133. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -3
  134. data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
  135. data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
  136. data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
  137. data/lib/rails/graphql/type/scalar/json_scalar.rb +39 -0
  138. data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
  139. data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
  140. data/lib/rails/graphql/type/scalar.rb +25 -22
  141. data/lib/rails/graphql/type/union.rb +14 -16
  142. data/lib/rails/graphql/type.rb +34 -25
  143. data/lib/rails/graphql/type_map.rb +256 -164
  144. data/lib/rails/graphql/uri.rb +166 -0
  145. data/lib/rails/graphql/version.rb +15 -3
  146. data/lib/rails/graphql.rake +3 -0
  147. data/lib/rails/graphql.rb +85 -52
  148. data/lib/rails-graphql.rb +1 -1
  149. data/test/assets/en.yml +29 -0
  150. data/test/assets/introspection-mem.txt +1 -1
  151. data/test/assets/mem.gql +18 -45
  152. data/test/assets/mysql.gql +392 -0
  153. data/test/assets/sqlite.gql +21 -12
  154. data/test/assets/translate.gql +335 -0
  155. data/test/config.rb +18 -8
  156. data/test/graphql/schema_test.rb +12 -19
  157. data/test/graphql/source_test.rb +8 -75
  158. data/test/graphql/type/enum_test.rb +207 -203
  159. data/test/graphql/type/input_test.rb +14 -9
  160. data/test/graphql/type/interface_test.rb +4 -4
  161. data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
  162. data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
  163. data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
  164. data/test/graphql/type_map_test.rb +51 -66
  165. data/test/graphql/type_test.rb +0 -19
  166. data/test/graphql_test.rb +1 -1
  167. data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
  168. data/test/integration/config.rb +36 -3
  169. data/test/integration/customization_test.rb +39 -0
  170. data/test/integration/global_id_test.rb +99 -0
  171. data/test/integration/memory/star_wars_introspection_test.rb +24 -16
  172. data/test/integration/memory/star_wars_query_test.rb +54 -3
  173. data/test/integration/memory/star_wars_validation_test.rb +1 -1
  174. data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
  175. data/test/integration/persisted_query_test.rb +87 -0
  176. data/test/integration/resolver_precedence_test.rb +154 -0
  177. data/test/integration/schemas/memory.rb +22 -7
  178. data/test/integration/schemas/mysql.rb +62 -0
  179. data/test/integration/schemas/sqlite.rb +21 -12
  180. data/test/integration/sqlite/star_wars_global_id_test.rb +83 -0
  181. data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
  182. data/test/integration/sqlite/star_wars_query_test.rb +14 -1
  183. data/test/integration/translate_test.rb +61 -0
  184. data/test/test_ext.rb +16 -13
  185. metadata +108 -157
  186. data/ext/depend +0 -3
  187. data/ext/graphqlparser/Ast.cpp +0 -346
  188. data/ext/graphqlparser/Ast.h +0 -1214
  189. data/ext/graphqlparser/AstNode.h +0 -36
  190. data/ext/graphqlparser/AstVisitor.h +0 -137
  191. data/ext/graphqlparser/GraphQLParser.cpp +0 -76
  192. data/ext/graphqlparser/GraphQLParser.h +0 -55
  193. data/ext/graphqlparser/JsonVisitor.cpp +0 -161
  194. data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
  195. data/ext/graphqlparser/JsonVisitor.h +0 -121
  196. data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
  197. data/ext/graphqlparser/VERSION +0 -1
  198. data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
  199. data/ext/graphqlparser/c/GraphQLAst.h +0 -180
  200. data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
  201. data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
  202. data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
  203. data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  204. data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
  205. data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  206. data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
  207. data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
  208. data/ext/graphqlparser/c/GraphQLParser.h +0 -54
  209. data/ext/graphqlparser/dump_json_ast.cpp +0 -48
  210. data/ext/graphqlparser/lexer.lpp +0 -324
  211. data/ext/graphqlparser/parser.ypp +0 -693
  212. data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
  213. data/ext/graphqlparser/parsergen/lexer.h +0 -528
  214. data/ext/graphqlparser/parsergen/location.hh +0 -189
  215. data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
  216. data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
  217. data/ext/graphqlparser/parsergen/position.hh +0 -179
  218. data/ext/graphqlparser/parsergen/stack.hh +0 -156
  219. data/ext/graphqlparser/syntaxdefs.h +0 -19
  220. data/ext/libgraphqlparser/AstNode.h +0 -36
  221. data/ext/libgraphqlparser/CMakeLists.txt +0 -148
  222. data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
  223. data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
  224. data/ext/libgraphqlparser/GraphQLParser.h +0 -55
  225. data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
  226. data/ext/libgraphqlparser/JsonVisitor.h +0 -121
  227. data/ext/libgraphqlparser/LICENSE +0 -22
  228. data/ext/libgraphqlparser/README.clang-tidy +0 -7
  229. data/ext/libgraphqlparser/README.md +0 -84
  230. data/ext/libgraphqlparser/ast/ast.ast +0 -203
  231. data/ext/libgraphqlparser/ast/ast.py +0 -61
  232. data/ext/libgraphqlparser/ast/c.py +0 -100
  233. data/ext/libgraphqlparser/ast/c.pyc +0 -0
  234. data/ext/libgraphqlparser/ast/c_impl.py +0 -61
  235. data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
  236. data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
  237. data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
  238. data/ext/libgraphqlparser/ast/casing.py +0 -26
  239. data/ext/libgraphqlparser/ast/casing.pyc +0 -0
  240. data/ext/libgraphqlparser/ast/cxx.py +0 -197
  241. data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
  242. data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
  243. data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
  244. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
  245. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
  246. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
  247. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
  248. data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
  249. data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
  250. data/ext/libgraphqlparser/ast/js.py +0 -65
  251. data/ext/libgraphqlparser/ast/license.py +0 -10
  252. data/ext/libgraphqlparser/ast/license.pyc +0 -0
  253. data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
  254. data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
  255. data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
  256. data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
  257. data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
  258. data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
  259. data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
  260. data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
  261. data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
  262. data/ext/libgraphqlparser/cmake/version.cmake +0 -16
  263. data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
  264. data/ext/libgraphqlparser/go/README.md +0 -20
  265. data/ext/libgraphqlparser/go/callbacks.go +0 -18
  266. data/ext/libgraphqlparser/go/gotest.go +0 -64
  267. data/ext/libgraphqlparser/lexer.lpp +0 -324
  268. data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
  269. data/ext/libgraphqlparser/parser.ypp +0 -693
  270. data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
  271. data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
  272. data/ext/libgraphqlparser/parsergen/location.hh +0 -189
  273. data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
  274. data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
  275. data/ext/libgraphqlparser/parsergen/position.hh +0 -179
  276. data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
  277. data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
  278. data/ext/libgraphqlparser/python/README.md +0 -5
  279. data/ext/libgraphqlparser/python/example.py +0 -31
  280. data/ext/libgraphqlparser/syntaxdefs.h +0 -19
  281. data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
  282. data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
  283. data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
  284. data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
  285. data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
  286. data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
  287. data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
  288. data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
  289. data/ext/libgraphqlparser/test/valgrind.supp +0 -33
  290. data/ext/version.cpp +0 -21
  291. data/lib/graphqlparser.so +0 -0
  292. data/lib/rails/graphql/native/functions.rb +0 -38
  293. data/lib/rails/graphql/native/location.rb +0 -41
  294. data/lib/rails/graphql/native/pointers.rb +0 -23
  295. data/lib/rails/graphql/native/visitor.rb +0 -349
  296. data/lib/rails/graphql/native.rb +0 -56
  297. data/test/integration/schemas/authorization.rb +0 -12
@@ -1,80 +1,104 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # = GraphQL Request Component
7
7
  #
8
8
  # Component is an abstraction of any possible type of object represented
9
9
  # by a not of the document of a request. This class helps building
10
10
  # cross-component features, like holding event listeners, setting up
11
- # commom initializer and providing helpers
11
+ # common initializer and providing helpers
12
12
  class Component
13
13
  extend ActiveSupport::Autoload
14
14
 
15
15
  include Request::Organizable
16
- include Request::Prepareable
17
- include Request::Resolveable
16
+ include Request::Preparable
17
+ include Request::Resolvable
18
18
 
19
19
  class << self
20
20
  # Return the kind of the component
21
21
  def kind
22
22
  @kind ||= name.demodulize.underscore.to_sym
23
23
  end
24
-
25
- # Helper to memoize results from parent delegation
26
- def parent_memoize(*methods)
27
- methods.each do |method_name|
28
- define_method(method_name) do
29
- result = parent.public_send(method_name)
30
- define_singleton_method(method_name) { result }
31
- result
32
- end
33
- end
34
- end
35
24
  end
36
25
 
37
- attr_reader :data
38
-
39
- delegate :schema, :visitor, :response, :strategy, to: :request
26
+ delegate :visitor, :response, :strategy, to: :request
40
27
  delegate :find_type!, :find_directive!, :trigger_event, to: :strategy
41
- delegate :memo, to: :operation
28
+ delegate :memo, :schema, to: :operation
42
29
  delegate :kind, to: :class
43
30
 
44
31
  alias of_type? is_a?
45
32
 
46
33
  eager_autoload do
47
34
  autoload :Field
48
- autoload :Fragment
49
35
  autoload :Operation
50
- autoload :Spread
51
- autoload :Typename
52
36
  end
53
37
 
54
- def initialize(node, data)
38
+ autoload :Fragment
39
+ autoload :Spread
40
+ autoload :Typename
41
+
42
+ def initialize(node)
55
43
  @node = node
56
- @data = data
57
44
  end
58
45
 
59
46
  # Check if the component is in a invalid state
60
47
  def invalid?
61
- defined?(@invalid) && @invalid
48
+ defined?(@invalid) && @invalid.present?
49
+ end
50
+
51
+ # Check if the component is marked as skipped
52
+ def skipped?
53
+ defined?(@skipped) && @skipped
54
+ end
55
+
56
+ # Just a fancy name for invalid or skipped
57
+ def unresolvable?
58
+ invalid? || skipped?
62
59
  end
63
60
 
64
61
  # Mark the component as invalid
65
- def invalidate!
66
- @invalid = true
62
+ def invalidate!(type = true)
63
+ @invalid = type
64
+ end
65
+
66
+ # Skip the component
67
+ def skip!
68
+ @skipped = true
67
69
  end
68
70
 
69
- # Normaly, components are not assignable, only fields are
71
+ # Normally, components are not assignable, only fields are
70
72
  def assignable?
71
73
  false
72
74
  end
73
75
 
76
+ # Get an identifier of the component
77
+ def hash
78
+ @node.hash
79
+ end
80
+
81
+ # Build the cache object
82
+ def cache_dump
83
+ hash = { node: @node }
84
+ hash[:invalid] = @invalid if defined?(@invalid) && @invalid != :authorization
85
+ hash[:skipped] = @skipped if defined?(@skipped) && @skipped
86
+ hash.merge!(super)
87
+ hash
88
+ end
89
+
90
+ # Organize from cache data
91
+ def cache_load(data)
92
+ @node = data[:node]
93
+ @invalid = data[:invalid] if data.key?(:invalid)
94
+ @skipped = data[:skipped] if data.key?(:skipped)
95
+ super
96
+ end
97
+
74
98
  protected
75
99
 
76
100
  # It's extremely important to have a way to access the current request
77
- # since not all objects stores s direct pointer to it
101
+ # since not all objects stores a direct pointer to it
78
102
  def request
79
103
  raise NotImplementedError
80
104
  end
@@ -86,13 +110,28 @@ module Rails # :nodoc:
86
110
 
87
111
  # Run a given block and ensure to capture exceptions to set them as
88
112
  # errors
89
- def capture_exception(stage, invalidate = false)
90
- yield
91
- rescue StandardError => error
92
- invalidate! if invalidate
113
+ def report_exception(error)
114
+ Backtrace.print(error, self, request)
115
+
93
116
  stack_path = request.stack_to_path
94
117
  stack_path << gql_name if respond_to?(:gql_name) && gql_name.present?
95
- request.exception_to_error(error, @node, path: stack_path, stage: stage.to_s)
118
+ request.exception_to_error(error, self, path: stack_path, stage: strategy.stage.to_s)
119
+ end
120
+
121
+ private
122
+
123
+ # Properly transform values to string gid
124
+ def all_to_gid(enum)
125
+ (enum.is_a?(Enumerable) ? enum : enum.then).each do |item|
126
+ item.to_gid.to_s
127
+ end
128
+ end
129
+
130
+ # Properly recover values from gid string
131
+ def all_from_gid(enum)
132
+ (enum.is_a?(Enumerable) ? enum : enum.then).each do |item|
133
+ GraphQL::GlobalID.find(item)
134
+ end
96
135
  end
97
136
  end
98
137
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # = GraphQL Request Context
7
7
  #
8
8
  # This class is used as context for the response while processing fields,
@@ -23,9 +23,14 @@ module Rails # :nodoc:
23
23
  @stack.shift
24
24
  end
25
25
 
26
- # Find the parent object
27
- def parent
28
- @stack[1]
26
+ # Return a duplicated version of the stack, for safety
27
+ def stack
28
+ @stack.dup
29
+ end
30
+
31
+ # Get a value at the given +index+
32
+ def at(index)
33
+ @stack[index]
29
34
  end
30
35
 
31
36
  # Get all ancestors objects
@@ -33,10 +38,15 @@ module Rails # :nodoc:
33
38
  @stack[1..-1]
34
39
  end
35
40
 
41
+ # Find the parent object
42
+ def parent
43
+ at(1)
44
+ end
45
+
36
46
  # Get the current value, which basically means basically the first item
37
- # on the current stafck
47
+ # on the current stack
38
48
  def current_value
39
- @stack[0]
49
+ at(0)
40
50
  end
41
51
 
42
52
  # Change the current value, either form hits or the actual value
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # = GraphQL Request Errors
7
7
  #
8
- # This class is inspired by +ActiveModel::Erros+. The idea is to hold all
8
+ # This class is inspired by +ActiveModel::Errors+. The idea is to hold all
9
9
  # the errors that happened during the execution of a request. It also
10
10
  # helps to export such information to the result object.
11
11
  class Errors
@@ -27,7 +27,7 @@ module Rails # :nodoc:
27
27
  @items.deep_dup
28
28
  end
29
29
 
30
- # Add +message+ to the list of errors. Any other keywork argument will
30
+ # Add +message+ to the list of errors. Any other keyword argument will
31
31
  # be used on set on the +:extensions+ part.
32
32
  #
33
33
  # ==== Options
@@ -42,12 +42,22 @@ module Rails # :nodoc:
42
42
  item['locations'] ||= [{ line: line.to_i, column: col.to_i }] \
43
43
  if line.present? && col.present?
44
44
 
45
- item['path'] = path if path.present? && path.is_a?(Array)
45
+ item['path'] = path if path.present? && path.is_a?(::Array)
46
46
  item['extensions'] = extra.deep_stringify_keys if extra.present?
47
47
  item['locations']&.map!(&:stringify_keys)
48
48
 
49
49
  @items << item.compact
50
50
  end
51
+
52
+ # Dump the necessary information from errors to a cached operation
53
+ def cache_dump
54
+ @items.select { |item| item.dig('extensions', 'stage') == 'organize' }
55
+ end
56
+
57
+ # Load the necessary information from a cached request data
58
+ def cache_load(data)
59
+ @items += data
60
+ end
51
61
  end
52
62
  end
53
63
  end
@@ -1,18 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # = GraphQL Request Event
7
7
  #
8
8
  # A small extension of the original event to support extra methods and
9
9
  # helpers when performing events during a request
10
10
  class Event < GraphQL::Event
11
- OBJECT_BASED_READERS = %i[fragment operation spread].freeze
11
+ OBJECT_BASED_READERS = %i[fragment spread].freeze
12
12
 
13
- delegate :schema, :errors, :context, to: :request
13
+ delegate :errors, :context, to: :request
14
14
  delegate :instance_for, to: :strategy
15
- delegate :memo, to: :source
15
+ delegate :memo, :schema, to: :source
16
+ delegate :subscription_field, to: :schema
16
17
 
17
18
  attr_reader :strategy, :request, :index
18
19
 
@@ -33,6 +34,11 @@ module Rails # :nodoc:
33
34
  super(name, source, **data)
34
35
  end
35
36
 
37
+ # If the source is a field, than also compare to the actual field
38
+ def same_source?(other)
39
+ super || (source.try(:kind) == :field && source.field == other)
40
+ end
41
+
36
42
  # Provide a way to access the current field value
37
43
  def current_value
38
44
  resolver&.current_value
@@ -45,6 +51,11 @@ module Rails # :nodoc:
45
51
  resolver&.override_value(value)
46
52
  end
47
53
 
54
+ # Get the operation for the current source
55
+ def operation
56
+ (object.kind == :operation) ? object : source.operation
57
+ end
58
+
48
59
  # Return the strategy context as the resolver
49
60
  def resolver
50
61
  strategy.context
@@ -52,7 +63,7 @@ module Rails # :nodoc:
52
63
 
53
64
  # Return the actual field when the source is a request field
54
65
  def field
55
- source.field if source.try(:kind) === :field
66
+ source.field if source.try(:kind) == :field
56
67
  end
57
68
 
58
69
  # Check if the event source is of the given +type+
@@ -99,7 +110,7 @@ module Rails # :nodoc:
99
110
  # it will return the object
100
111
  def method_missing(method_name, *args, **xargs, &block)
101
112
  if OBJECT_BASED_READERS.include?(method_name)
102
- object if object.kind === method_name
113
+ object if object.kind == method_name
103
114
  elsif current_value&.respond_to?(method_name)
104
115
  current_value&.public_send(method_name, *args, **xargs, &block)
105
116
  else
@@ -1,62 +1,103 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # Helper module to collect the directives from fragments, operations, and
7
7
  # fields.
8
8
  module Directives
9
9
  # Get the list of listeners from directives set during the request only
10
- def all_listeners
11
- directives.map(&:all_listeners).reduce(:+) || Set.new
10
+ def directive_listeners
11
+ return unless directives?
12
+ return @directive_listeners if defined?(@directive_listeners)
13
+ @directive_listeners = directives.map(&:all_listeners).compact.reduce(:+)
12
14
  end
13
15
 
16
+ alias all_listeners directive_listeners
17
+
14
18
  # Get the list of events from directives set during the request only and
15
19
  # then caches it by request
16
- def all_events
17
- @all_events ||= directives.map(&:all_events).inject({}) do |lhash, rhash|
18
- Helpers.merge_hash_array(lhash, rhash)
20
+ def directive_events
21
+ return unless directives?
22
+ @directive_events ||= begin
23
+ directives.map(&:all_events).compact.inject({}) do |lhash, rhash|
24
+ Helpers.merge_hash_array(lhash, rhash)
25
+ end
19
26
  end
20
27
  end
21
28
 
29
+ alias all_events directive_events
30
+
31
+ # Check if the current component is using a directive
32
+ def using?(item)
33
+ return false unless directives?
34
+
35
+ directive = (item.is_a?(Symbol) || item.is_a?(String)) ? find_directive!(item) : item
36
+ (directive < GraphQL::Directive) && directives.any?(directive)
37
+ end
38
+
39
+ # Build the cache object
40
+ def cache_dump
41
+ return super unless defined?(@directives)
42
+
43
+ super.merge(directives: all_to_gid(@directives.transform_values))
44
+ end
45
+
46
+ # Organize from cache data
47
+ def cache_load(data)
48
+ return super unless data.key?(:directives)
49
+
50
+ @directives = all_from_gid(data[:directives].transform_values).freeze
51
+
52
+ super
53
+ end
54
+
22
55
  protected
23
56
 
24
57
  # Make sure to always return a set
25
58
  def directives
26
- @directives || Set.new
59
+ @directives if directives?
27
60
  end
28
61
 
29
- alias all_directives directives
62
+ # Check if any execution directive was added
63
+ def directives?
64
+ defined?(@directives)
65
+ end
30
66
 
31
67
  # Helper parser for directives that also collect necessary variables
32
- def parse_directives(location = nil)
33
- list = []
34
-
35
- visitor.collect_directives(*data[:directives]) do |data|
36
- instance = find_directive!(data[:name])
68
+ def parse_directives(nodes, location = nil)
69
+ return if nodes.nil?
37
70
 
38
- args = directive_arguments(instance)
39
- args = collect_arguments(args, data[:arguments]) do |errors|
40
- "Invalid arguments for @#{instance.gql_name} directive" \
41
- " added to #{gql_name} #{kind}: #{errors}."
71
+ list = nil
72
+ nodes.each do |(name, arguments)|
73
+ instance = find_directive!(name.to_s)
74
+ values = arguments&.each_with_object({}) do |(name, value, var_name), hash|
75
+ hash[name.to_s] = var_name.nil? ? value : var_name
42
76
  end
43
77
 
44
- list << instance.new(request.build(Request::Arguments, args))
45
- end unless data[:directives].blank?
78
+ args = directive_arguments(instance)
79
+ args = collect_arguments(args, values)
46
80
 
47
- if list.present?
48
- event = Event.new(:attach, strategy, self, phase: :execution)
49
- list = GraphQL.directives_to_set(list, [], event, location: location || kind)
81
+ (list ||= []) << instance.new(request.build(Request::Arguments, args))
82
+ rescue ArgumentsError => error
83
+ raise ArgumentsError, (+<<~MSG).squish
84
+ Invalid arguments for @#{instance.gql_name} directive
85
+ added to #{gql_name} #{kind}: #{error.message}.
86
+ MSG
50
87
  end
51
88
 
89
+ event = Event.new(:attach, strategy, self, phase: :execution)
90
+ list = GraphQL.directives_to_set(list, [], event, location: location || kind)
91
+
52
92
  @directives = list.freeze
53
93
  end
54
94
 
55
95
  # Get and cache all the arguments for this given +directive+
56
96
  def directive_arguments(directive)
57
- request.cache(:arguments)[directive] ||= begin
58
- result = directive.all_arguments
59
- result.each_value.map(&:gql_name).zip(result.each_value).to_h
97
+ request.nested_cache(:arguments, directive) do
98
+ directive.all_arguments&.each_value&.with_object({}) do |directive, hash|
99
+ hash[directive.gql_name] = directive
100
+ end
60
101
  end
61
102
  end
62
103
  end
@@ -1,43 +1,70 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
5
- class Request # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
+ class Request
6
6
  # Helper module to collect the fields from fragments, operations, and also
7
7
  # other fields.
8
8
  module SelectionSet
9
9
  attr_reader :selection
10
10
 
11
+ # Build the cache object
12
+ def cache_dump
13
+ return super unless defined?(@selection)
14
+
15
+ selection = @selection.transform_values do |field|
16
+ field.cache_dump.merge(type: field.class)
17
+ end
18
+
19
+ super.merge(selection: selection)
20
+ end
21
+
22
+ # Organize from cache data
23
+ def cache_load(data)
24
+ return super unless data.key?(:selection)
25
+
26
+ @selection = data[:selection].transform_values do |data|
27
+ component = request.build_from_cache(data[:type])
28
+ component.instance_variable_set(:@parent, self)
29
+ component.cache_load(data)
30
+ component
31
+ end.freeze
32
+
33
+ super
34
+ end
35
+
11
36
  protected
12
37
 
13
- # Helper parser for selection fields that also asssign the actual
38
+ # Helper parser for selection fields that also assign the actual
14
39
  # field defined under the schema structure
15
- def parse_selection
40
+ def parse_selection(nodes)
41
+ return if nodes.nil?
42
+
16
43
  @selection = {}
17
44
  assigners = Hash.new { |h, k| h[k] = [] }
18
45
 
19
- visitor.collect_fields(*data[:selection]) do |kind, node, data|
20
- component = add_component(kind, node, data)
21
- assigners[component.name] << component if component.assignable?
22
- end unless data[:selection].nil? || data[:selection].null?
46
+ nodes.each do |node|
47
+ component = add_component(node)
48
+ assigners[component.name.to_s] << component if component.assignable?
49
+ end
23
50
 
24
- assing_fields!(assigners)
51
+ assign_fields!(assigners)
25
52
  @selection.freeze
26
53
  end
27
54
 
28
55
  # Using +fields_source+, find the needed ones to be assigned to the
29
56
  # current requested fields. As shown by benchmark, since the index is
30
57
  # based on Symbols, the best way to find +gql_name+ based fields is
31
- # through interation and search. Complexity O(n)
32
- def assing_fields!(assigners)
58
+ # through iteration and search. Complexity O(n)
59
+ def assign_fields!(assigners)
33
60
  pending = assigners.map(&:size).reduce(:+) || 0
34
61
  return if pending.zero?
35
62
 
36
- fields_source.each_value do |field|
63
+ fields_source&.each_value do |field|
37
64
  next unless assigners.key?(field.gql_name)
38
65
 
39
66
  items = assigners[field.gql_name]
40
- items.each_with_object(field).each(&:assing_to)
67
+ items.each_with_object(field).each(&:assign_to)
41
68
  break if (pending -= items.size) === 0
42
69
  end
43
70
  end
@@ -45,20 +72,19 @@ module Rails # :nodoc:
45
72
  # Recursive operation that perform the organization step for the
46
73
  # selection
47
74
  def organize_fields
48
- selection.each_value(&:organize!) if selection.any?
75
+ selection.each_value(&:organize!) if selection.present?
49
76
  end
50
77
 
51
78
  # Find all the fields that have a prepare step and execute them
52
79
  def prepare_fields
53
- return unless selection.any?
54
- (strategy.listeners[:prepare] & selection.values).each(&:prepare!)
80
+ selection.each_value(&:prepare!) if selection.present?
55
81
  end
56
82
 
57
83
  # Trigger the process of resolving the value of all the fields. Since
58
84
  # complex object may or may not be inside an array, this helps to
59
85
  # decide if a new stack should be started or not
60
86
  def resolve_fields(object = nil)
61
- return unless selection.any?
87
+ return unless selection.present?
62
88
 
63
89
  items = selection.each_value
64
90
  items = items.each_with_object(object) unless object.nil?
@@ -70,15 +96,15 @@ module Rails # :nodoc:
70
96
 
71
97
  private
72
98
 
73
- def add_component(kind, node, data)
74
- item_name = data.try(:alias).presence || data[:name]
99
+ def add_component(node)
100
+ item_name = node[1] || node[0]
75
101
 
76
- if kind === :spread
77
- selection[selection.size] = request.build(Component::Spread, self, node, data)
78
- elsif data[:name] === '__typename'
79
- selection[item_name] ||= request.build(Component::Typename, self, node, data)
102
+ if node.of_type?(:spread)
103
+ selection[selection.size] = request.build(Component::Spread, self, node)
104
+ elsif node[0] === '__typename'
105
+ selection[item_name] ||= request.build(Component::Typename, self, node)
80
106
  else
81
- selection[item_name] ||= request.build(Component::Field, self, node, data)
107
+ selection[item_name] ||= request.build(Component::Field, self, node)
82
108
  end
83
109
  end
84
110
  end