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,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # = GraphQL Base Generator
6
6
  #
7
7
  # A module to help generators to operate
@@ -22,13 +22,7 @@ module Rails # :nodoc:
22
22
 
23
23
  def app_module_name
24
24
  require File.expand_path('config/application', destination_root)
25
-
26
- app_class = Rails.application.class
27
- source_name = app_class.respond_to?(:module_parent_name) \
28
- ? :module_parent_name \
29
- : :parent_name
30
-
31
- app_class.send(source_name)
25
+ Rails.application.class.name.chomp('::Application')
32
26
  end
33
27
  end
34
28
  end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module GraphQL
5
+ # = GraphQL Channel
6
+ #
7
+ # The channel helper methods that allow GraphQL to be performed on an
8
+ # Action Cable channel. It also provides structure fro working with
9
+ # subscriptions
10
+ module Channel
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ # Each channel is assigned to a GraphQL schema on which the requests
15
+ # will be performed from. It can be a string or the class
16
+ class_attribute :gql_schema, instance_accessor: false
17
+
18
+ # Set it up a callback after unsubscribed so that all the subscriptions
19
+ # can be properly unsubscribed
20
+ after_unsubscribe :gql_clear_subscriptions
21
+ end
22
+
23
+ # The default action of the helper to perform GraphQL requests. Any other
24
+ # action cab be added and use the granular methods here provided
25
+ def execute(data)
26
+ transmit(gql_request_response(data))
27
+ end
28
+
29
+ protected
30
+
31
+ # Identifies if the received request within +data+ should be threated as
32
+ # a compiled request
33
+ def gql_compiled_request?(*)
34
+ false
35
+ end
36
+
37
+ # Get the response of a request withing the given +data+
38
+ def gql_request_response(data)
39
+ xargs = gql_params(data)
40
+ schema, context, query = xargs.extract!(:schema, :context, :query).values
41
+
42
+ request = gql_request(schema)
43
+ request.context = context
44
+ request.execute(query, **xargs)
45
+
46
+ gql_merge_subscriptions(request)
47
+ gql_response(request)
48
+ end
49
+
50
+ # Merge the subscriptions in this channel and the ones that were added
51
+ # by the last request
52
+ def gql_merge_subscriptions(request)
53
+ gql_subscriptions.merge!(request.subscriptions)
54
+ end
55
+
56
+ # Properly format the response of the provided +request+ so it can be
57
+ # transmitted
58
+ def gql_response(request)
59
+ { result: request.response.as_json, more: request.subscriptions? }
60
+ end
61
+
62
+ # Build the necessary params from the provided data
63
+ def gql_params(data)
64
+ cache_key = gql_query_cache_key(
65
+ data['query_cache_key'],
66
+ data['query_cache_version'],
67
+ )
68
+
69
+ {
70
+ query: data['query'],
71
+ origin: self,
72
+ variables: gql_variables(data),
73
+ operation_name: data['operation_name'] || data['operationName'],
74
+ compiled: gql_compiled_request?(data),
75
+ context: gql_context(data),
76
+ schema: gql_schema(data),
77
+ hash: cache_key,
78
+ as: :hash,
79
+ }
80
+ end
81
+
82
+ # The list of ids of subscription and to which field they are
83
+ # associated with
84
+ def gql_subscriptions
85
+ @gql_subscriptions ||= {}
86
+ end
87
+
88
+ # The instance of a GraphQL request. It can't simply perform using
89
+ # +execute+, because it is important to check if any subscription was
90
+ # generated
91
+ def gql_request(schema = gql_schema)
92
+ @gql_request ||= ::Rails::GraphQL::Request.new(schema)
93
+ end
94
+
95
+ # Get the cache key of the query for persisted queries
96
+ def gql_query_cache_key(key = nil, version = nil)
97
+ CacheKey.new(key, version) if key.present?
98
+ end
99
+
100
+ # The schema on which the requests will be performed from
101
+ def gql_schema(*)
102
+ schema = self.class.gql_schema
103
+ schema = schema.constantize if schema.is_a?(String)
104
+ schema ||= gql_application_default_schema
105
+ return schema if schema.is_a?(Module) && schema < ::Rails::GraphQL::Schema
106
+
107
+ raise ExecutionError, (+<<~MSG).squish
108
+ Unable to find a valid schema for #{self.class.name},
109
+ defined value: #{schema.inspect}.
110
+ MSG
111
+ end
112
+
113
+ # Get the GraphQL context for a requests
114
+ # +ActionCable::Channel::Base#extract_action+
115
+ def gql_context(data)
116
+ { action: (data['action'] || :receive).to_sym }
117
+ end
118
+
119
+ # Get the GraphQL variables for a request
120
+ def gql_variables(data)
121
+ variables = data['variables']
122
+
123
+ case variables
124
+ when ::ActionController::Parameters then variables.permit!.to_h
125
+ when String then variables.present? ? JSON.parse(variables) : {}
126
+ when Hash then variables
127
+ else {}
128
+ end
129
+ end
130
+
131
+ # Remove all subscriptions
132
+ def gql_clear_subscriptions
133
+ gql_remove_subscription(*gql_subscriptions.keys) unless gql_subscriptions.empty?
134
+ end
135
+
136
+ # Remove one or more subscriptions
137
+ def gql_remove_subscriptions(*sids)
138
+ gql_schema.remove_subscriptions(*sids)
139
+ end
140
+
141
+ alias gql_remove_subscription gql_remove_subscriptions
142
+
143
+ private
144
+
145
+ # Find the default application schema
146
+ def gql_application_default_schema
147
+ app_class = Rails.application.class
148
+ source_name = app_class.respond_to?(:module_parent_name) \
149
+ ? :module_parent_name \
150
+ : :parent_name
151
+
152
+ klass = "::GraphQL::#{app_class.public_send(source_name)}Schema".constantize
153
+ self.class.gql_schema = klass
154
+ end
155
+ end
156
+ end
157
+ end
@@ -1,15 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ module Rails
4
+ module GraphQL
5
5
  # = GraphQL Controller
6
6
  #
7
7
  # The controller helper methods that allow GraphQL to be performed on a
8
- # Rails Controller class.
8
+ # Rails Controller class
9
9
  module Controller
10
10
  extend ActiveSupport::Concern
11
11
 
12
- REQUEST_XARGS = %i[operation_name variables context schema].freeze
12
+ REQUEST_XARGS = %i[operation_name variables context schema query_cache_key].freeze
13
+ DESCRIBE_HEADER = <<~TXT.freeze
14
+ """
15
+ Use the following HTTP params to modify this result:
16
+ without_descriptions => Hide all descriptions
17
+ without_spec => Hide all default spec types
18
+ """
19
+ TXT
13
20
 
14
21
  included do
15
22
  # Each controller is assigned to a GraphQL schema on which the requests
@@ -24,10 +31,7 @@ module Rails # :nodoc:
24
31
 
25
32
  # GET /describe
26
33
  def describe
27
- render plain: gql_schema.to_gql(
28
- with_descriptions: !params.key?(:without_descriptions),
29
- with_spec: !params.key?(:without_spec),
30
- )
34
+ render plain: gql_schema_header + gql_describe_schema + gql_schema_footer
31
35
  end
32
36
 
33
37
  protected
@@ -37,23 +41,46 @@ module Rails # :nodoc:
37
41
  render json: gql_request(*args, **xargs)
38
42
  end
39
43
 
44
+ # Shows a text representation of the schema
45
+ def gql_describe_schema(schema = gql_schema)
46
+ schema.to_gql(
47
+ with_descriptions: !params.key?(:without_descriptions),
48
+ with_spec: !params.key?(:without_spec),
49
+ )
50
+ end
51
+
40
52
  # Execute a GraphQL request
41
53
  def gql_request(query, **xargs)
42
- request_xargs = REQUEST_XARGS.inject({}) do |result, setting|
43
- result.merge(setting => (xargs[setting] || send("gql_#{setting}")))
54
+ request_xargs = REQUEST_XARGS.each_with_object({}) do |setting, result|
55
+ result[setting] ||= (xargs[setting] || send(:"gql_#{setting}"))
44
56
  end
45
57
 
58
+ request_xargs[:hash] ||= gql_query_cache_key
59
+ request_xargs[:origin] ||= self
60
+
61
+ request_xargs = request_xargs.except(*%i[query_cache_key query_cache_version])
46
62
  ::Rails::GraphQL::Request.execute(query, **request_xargs)
47
63
  end
48
64
 
65
+ # Print a header of the current schema for the description process
66
+ # TODO: Maybe add a way to detect from which file the schema is being loaded
67
+ def gql_schema_header
68
+ ns = +" [#{gql_schema.namespace}]" if gql_schema.namespace != :base
69
+ +"#{DESCRIBE_HEADER}# Schema #{gql_schema.name}#{ns}\n"
70
+ end
71
+
49
72
  # The schema on which the requests will be performed from
50
73
  def gql_schema
74
+ return @gql_schema if defined?(@gql_schema)
75
+
51
76
  schema = self.class.gql_schema
52
- schema = schema.safe_constantize if schema.is_a?(String)
53
- schema ||= application_default_schema
54
- return schema if schema.is_a?(Module) && schema < ::Rails::GraphQL::Schema
77
+ schema = schema.constantize if schema.is_a?(String)
78
+ schema ||= gql_application_default_schema
79
+
80
+ return @gql_schema = schema if schema.is_a?(Module) &&
81
+ schema < ::Rails::GraphQL::Schema
55
82
 
56
- raise ExecutionError, <<~MSG.squish
83
+ raise ExecutionError, (+<<~MSG).squish
57
84
  Unable to find a valid schema for #{self.class.name},
58
85
  defined value: #{schema.inspect}.
59
86
  MSG
@@ -64,9 +91,15 @@ module Rails # :nodoc:
64
91
  params[:query]
65
92
  end
66
93
 
94
+ # Get the cache key of the query for persisted queries
95
+ def gql_query_cache_key(key = nil, version = nil)
96
+ return unless (key ||= params[:query_cache_key]).present?
97
+ CacheKey.new(key, version || params[:query_cache_version])
98
+ end
99
+
67
100
  # Get the GraphQL operation name
68
101
  def gql_operation_name
69
- params[:operationName]
102
+ params[:operationName] || params[:operation_name]
70
103
  end
71
104
 
72
105
  # Get the GraphQL context for a requests
@@ -84,16 +117,28 @@ module Rails # :nodoc:
84
117
  end
85
118
  end
86
119
 
120
+ # Show the footer of the describe page
121
+ def gql_schema_footer
122
+ $/ + $/ + '# Version: ' + gql_version + $/ +
123
+ '# Rails GraphQL ' + ::Rails::GraphQL::VERSION::STRING +
124
+ ' (Spec ' + ::GQLParser::VERSION + ')'
125
+ end
126
+
127
+ # Get the version of the running instance of GraphQL
128
+ def gql_version
129
+ ::Rails::GraphQL.type_map.version
130
+ end
131
+
87
132
  private
88
133
 
89
134
  # Find the default application schema
90
- def application_default_schema
135
+ def gql_application_default_schema
91
136
  app_class = Rails.application.class
92
137
  source_name = app_class.respond_to?(:module_parent_name) \
93
138
  ? :module_parent_name \
94
139
  : :parent_name
95
140
 
96
- klass = "::GraphQL::#{app_class.send(source_name)}Schema".constantize
141
+ klass = "::GraphQL::#{app_class.public_send(source_name)}Schema".constantize
97
142
  self.class.gql_schema = klass
98
143
  end
99
144
  end
@@ -2,20 +2,20 @@
2
2
 
3
3
  require 'active_support/core_ext/module/attr_internal'
4
4
 
5
- module Rails # :nodoc:
6
- module GraphQL # :nodoc:
5
+ module Rails
6
+ module GraphQL
7
7
  # = GraphQL Controller Runtime
8
8
  #
9
9
  # Tool that calculates the runtime of a GraphQL operation. This works
10
10
  # similar to how Rails ActiveRecord calculate its execution time while
11
- # performing a request.
11
+ # performing a request
12
12
  module ControllerRuntime
13
13
  extend ActiveSupport::Concern
14
14
 
15
- module ClassMethods # :nodoc: all
15
+ module ClassMethods
16
16
  def log_process_action(payload)
17
17
  messages, gql_runtime = super, payload[:gql_runtime]
18
- messages << format('GraphQL: %.1fms', gql_runtime.to_f) if gql_runtime
18
+ messages << format(+'GraphQL: %.1fms', gql_runtime.to_f) if gql_runtime
19
19
  messages
20
20
  end
21
21
  end
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Rails # :nodoc:
4
- module GraphQL # :nodoc:
3
+ require "active_support/parameter_filter"
4
+
5
+ module Rails
6
+ module GraphQL
5
7
  # = GraphQL Log Subscriber
6
8
  #
7
- # This is the log tracker that workds the same way as ActiveRecord when it
8
- # has to report on logs that a query was performed.
9
- class LogSubscriber < ::ActiveSupport::LogSubscriber # :nodoc: all
9
+ # This is the log tracker that works the same way as ActiveRecord when it
10
+ # has to report on logs that a query was performed
11
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
10
12
  class_attribute :backtrace_cleaner, default: ActiveSupport::BacktraceCleaner.new
11
13
 
14
+ REMOVE_COMMENTS = /#(?=(?:[^"]*"[^"]*")*[^"]*$).*/
15
+
12
16
  def self.runtime
13
17
  RuntimeRegistry.gql_runtime ||= 0
14
18
  end
@@ -22,23 +26,65 @@ module Rails # :nodoc:
22
26
  return unless logger.debug?
23
27
 
24
28
  payload = event.payload
29
+ cached = '[CACHE]' if payload[:cached]
30
+ doc = payload[:document]&.gsub(REMOVE_COMMENTS, '')&.squish
31
+
32
+ desc = +"#{header(event, cached)} #{doc}"
33
+ desc << debug_variables(payload[:variables]) unless payload[:variables].blank?
34
+
35
+ debug(desc)
36
+ end
37
+
38
+ def compile(event)
39
+ return unless logger.debug?
40
+
41
+ payload = event.payload
42
+ doc = payload[:document].gsub(REMOVE_COMMENTS, '').squish
43
+
44
+ helper = ActiveSupport::NumberHelper::NumberToHumanSizeConverter
45
+ total = helper.convert(payload[:total], EMPTY_HASH)
46
+
47
+ debug(+"#{header(event, 'Compile')} #{total} #{doc}")
48
+ end
49
+
50
+ def validate(event)
51
+ return unless logger.debug?
52
+
53
+ payload = event.payload
54
+ doc = payload[:document].gsub(REMOVE_COMMENTS, '').squish
55
+ valid = payload[:result] ? color('YES', GREEN) : color('NO', RED)
56
+
57
+ debug(+"#{header(event, 'Valid?')} #{valid} #{doc}")
58
+ end
59
+
60
+ def subscription(event)
61
+ return unless logger.info?
25
62
 
26
- name = ['GraphQL', payload[:name].presence]
27
- name.unshift('CACHE') if payload[:cached]
28
- name = "#{name.compact.join(' ')} (#{event.duration.round(1)}ms)"
63
+ item, type, provider = event.payload.values_at(:item, :type, :provider)
64
+ provider = provider.class.name.sub(/\ARails::GraphQL::Subscription::/, '')
65
+ duration = event.duration.round(1)
29
66
 
30
- document = payload[:document].squish
31
- variables = payload[:variables].blank? ? nil : begin
32
- " (#{JSON.pretty_generate(payload[:variables]).squish})"
67
+ desc = +"#{header(event, provider)} Subscription #{type}"
68
+
69
+ unless item.nil?
70
+ hex = { added: GREEN, removed: RED, updated: BLUE }[type]
71
+
72
+ if type == :updated
73
+ desc << +": #{color(item.sid, hex)}"
74
+ else
75
+ desc << +": [#{color(item.sid, hex)}] #{item.schema}.#{item.field.gql_name}"
76
+ desc << +" [#{(item.scope === null_subscription_scope ? nil : item.scope).inspect}"
77
+ desc << +", #{item.args.as_json.inspect}]"
78
+ end
33
79
  end
34
80
 
35
- debug " #{color(name, MAGENTA, true)} #{document}#{variables}"
81
+ info(desc)
36
82
  end
37
83
 
38
84
  private
39
85
 
40
86
  def logger
41
- GraphQL.config.logger
87
+ GraphQL.logger
42
88
  end
43
89
 
44
90
  def debug(*)
@@ -47,14 +93,35 @@ module Rails # :nodoc:
47
93
  log_query_source if GraphQL.config.verbose_logs
48
94
  end
49
95
 
96
+ def header(event, suffix = '')
97
+ duration = event.duration.round(1)
98
+ parts = [' GraphQL', suffix.presence, event.payload[:name]]
99
+ parts << "(#{duration}ms)" unless duration.zero?
100
+
101
+ color(parts.compact.join(' '), MAGENTA, true)
102
+ end
103
+
104
+ def debug_variables(vars)
105
+ vars = JSON.pretty_generate(parameter_filter.filter(vars))
106
+ +' ' << '(' << vars.squish << ')'
107
+ end
108
+
50
109
  def log_query_source
51
110
  source = extract_query_source_location(caller)
52
- logger.debug(" ↳ #{source}") if source
111
+ logger.debug(+" ↳ #{source}") if source
53
112
  end
54
113
 
55
114
  def extract_query_source_location(locations)
56
115
  backtrace_cleaner.clean(locations.lazy).first
57
116
  end
117
+
118
+ def parameter_filter
119
+ ActiveSupport::ParameterFilter.new(GraphQL.config.filter_parameters)
120
+ end
121
+
122
+ def null_subscription_scope
123
+ Request::Subscription::NULL_SCOPE
124
+ end
58
125
  end
59
126
  end
60
127
 
@@ -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 Arguments
7
7
  #
8
8
  # This is an extension of an +OpenStruct+ since argument values can be
@@ -14,14 +14,33 @@ module Rails # :nodoc:
14
14
  # the spread and operation.
15
15
  class Arguments < OpenStruct
16
16
  THREAD_KEY = :_rails_graphql_operation
17
- LAZY_LOADER = ->(key, object) { object.variables[key] }.curry
17
+
18
+ class Lazy < Delegator
19
+ attr_reader :var_name
20
+
21
+ def self.[](key)
22
+ new(key)
23
+ end
24
+
25
+ def initialize(var_name)
26
+ @var_name = var_name
27
+ end
28
+
29
+ def __getobj__
30
+ Arguments.operation&.variables&.dig(var_name)
31
+ end
32
+
33
+ def __setobj__(*)
34
+ raise FrozenError
35
+ end
36
+ end
18
37
 
19
38
  delegate :key?, to: :@table
20
39
 
21
40
  class << self
22
41
  # Easy access to the easy loader method
23
42
  def lazy
24
- LAZY_LOADER
43
+ Lazy
25
44
  end
26
45
 
27
46
  # Get the current operation thread safely
@@ -42,50 +61,6 @@ module Rails # :nodoc:
42
61
  def scoped?
43
62
  operation.present?
44
63
  end
45
-
46
- # If it's running under a scope, transform proc based values
47
- def transform(value)
48
- return if value.nil?
49
- scoped? && value.is_a?(Proc) ? value.call(operation) : value
50
- end
51
- end
52
-
53
- # Transform any proc by its actual value before returning the hash
54
- def to_h(*)
55
- super.transform_values(&self.class.method(:transform))
56
- end
57
-
58
- # Before iterating, transform any needed value
59
- def each_pair
60
- enum = to_h.to_enum
61
- return enum unless block_given?
62
- enum.each { |v| yield v }
63
- self
64
- end
65
-
66
- # rubocop:disable Style/MissingRespondToMissing
67
- # Transform the value before returning
68
- def method_missing(*)
69
- self.class.transform(super)
70
- end
71
- # rubocop:enable Style/MissingRespondToMissing
72
-
73
- # Transform the value before returning
74
- def [](*)
75
- self.class.transform(super)
76
- end
77
-
78
- # Transform the value before returning
79
- def dig(name, *names)
80
- result = self.class.transform(super(name))
81
- names.empty? ? result : result&.dig(*names)
82
- end
83
-
84
- # Override the freeze method to just freeze the table and do not create
85
- # the getters and setter methods
86
- def freeze
87
- @table.freeze
88
- super
89
64
  end
90
65
  end
91
66
  end