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,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