rails-graphql 0.2.0 → 1.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
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 +10 -8
  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 +50 -36
  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 +257 -165
  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