rails-graphql 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +19 -0
  4. data/Rakefile +31 -0
  5. data/ext/depend +3 -0
  6. data/ext/extconf.rb +57 -0
  7. data/ext/graphqlparser/Ast.cpp +346 -0
  8. data/ext/graphqlparser/Ast.h +1214 -0
  9. data/ext/graphqlparser/AstNode.h +36 -0
  10. data/ext/graphqlparser/AstVisitor.h +137 -0
  11. data/ext/graphqlparser/GraphQLParser.cpp +76 -0
  12. data/ext/graphqlparser/GraphQLParser.h +55 -0
  13. data/ext/graphqlparser/JsonVisitor.cpp +161 -0
  14. data/ext/graphqlparser/JsonVisitor.cpp.inc +456 -0
  15. data/ext/graphqlparser/JsonVisitor.h +121 -0
  16. data/ext/graphqlparser/JsonVisitor.h.inc +110 -0
  17. data/ext/graphqlparser/VERSION +1 -0
  18. data/ext/graphqlparser/c/GraphQLAst.cpp +324 -0
  19. data/ext/graphqlparser/c/GraphQLAst.h +180 -0
  20. data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +44 -0
  21. data/ext/graphqlparser/c/GraphQLAstNode.cpp +25 -0
  22. data/ext/graphqlparser/c/GraphQLAstNode.h +33 -0
  23. data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +21 -0
  24. data/ext/graphqlparser/c/GraphQLAstToJSON.h +24 -0
  25. data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +55 -0
  26. data/ext/graphqlparser/c/GraphQLAstVisitor.h +53 -0
  27. data/ext/graphqlparser/c/GraphQLParser.cpp +35 -0
  28. data/ext/graphqlparser/c/GraphQLParser.h +54 -0
  29. data/ext/graphqlparser/dump_json_ast.cpp +48 -0
  30. data/ext/graphqlparser/lexer.lpp +324 -0
  31. data/ext/graphqlparser/parser.ypp +693 -0
  32. data/ext/graphqlparser/parsergen/lexer.cpp +2633 -0
  33. data/ext/graphqlparser/parsergen/lexer.h +528 -0
  34. data/ext/graphqlparser/parsergen/location.hh +189 -0
  35. data/ext/graphqlparser/parsergen/parser.tab.cpp +3300 -0
  36. data/ext/graphqlparser/parsergen/parser.tab.hpp +646 -0
  37. data/ext/graphqlparser/parsergen/position.hh +179 -0
  38. data/ext/graphqlparser/parsergen/stack.hh +156 -0
  39. data/ext/graphqlparser/syntaxdefs.h +19 -0
  40. data/ext/libgraphqlparser/AstNode.h +36 -0
  41. data/ext/libgraphqlparser/CMakeLists.txt +148 -0
  42. data/ext/libgraphqlparser/CONTRIBUTING.md +23 -0
  43. data/ext/libgraphqlparser/GraphQLParser.cpp +76 -0
  44. data/ext/libgraphqlparser/GraphQLParser.h +55 -0
  45. data/ext/libgraphqlparser/JsonVisitor.cpp +161 -0
  46. data/ext/libgraphqlparser/JsonVisitor.h +121 -0
  47. data/ext/libgraphqlparser/LICENSE +22 -0
  48. data/ext/libgraphqlparser/README.clang-tidy +7 -0
  49. data/ext/libgraphqlparser/README.md +84 -0
  50. data/ext/libgraphqlparser/ast/ast.ast +203 -0
  51. data/ext/libgraphqlparser/ast/ast.py +61 -0
  52. data/ext/libgraphqlparser/ast/c.py +100 -0
  53. data/ext/libgraphqlparser/ast/c.pyc +0 -0
  54. data/ext/libgraphqlparser/ast/c_impl.py +61 -0
  55. data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
  56. data/ext/libgraphqlparser/ast/c_visitor_impl.py +39 -0
  57. data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
  58. data/ext/libgraphqlparser/ast/casing.py +26 -0
  59. data/ext/libgraphqlparser/ast/casing.pyc +0 -0
  60. data/ext/libgraphqlparser/ast/cxx.py +197 -0
  61. data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
  62. data/ext/libgraphqlparser/ast/cxx_impl.py +61 -0
  63. data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
  64. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +42 -0
  65. data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
  66. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +80 -0
  67. data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
  68. data/ext/libgraphqlparser/ast/cxx_visitor.py +64 -0
  69. data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
  70. data/ext/libgraphqlparser/ast/js.py +65 -0
  71. data/ext/libgraphqlparser/ast/license.py +10 -0
  72. data/ext/libgraphqlparser/ast/license.pyc +0 -0
  73. data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +25 -0
  74. data/ext/libgraphqlparser/c/GraphQLAstNode.h +33 -0
  75. data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +21 -0
  76. data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +24 -0
  77. data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +55 -0
  78. data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +53 -0
  79. data/ext/libgraphqlparser/c/GraphQLParser.cpp +35 -0
  80. data/ext/libgraphqlparser/c/GraphQLParser.h +54 -0
  81. data/ext/libgraphqlparser/clang-tidy-all.sh +3 -0
  82. data/ext/libgraphqlparser/cmake/version.cmake +16 -0
  83. data/ext/libgraphqlparser/dump_json_ast.cpp +48 -0
  84. data/ext/libgraphqlparser/go/README.md +20 -0
  85. data/ext/libgraphqlparser/go/callbacks.go +18 -0
  86. data/ext/libgraphqlparser/go/gotest.go +64 -0
  87. data/ext/libgraphqlparser/lexer.lpp +324 -0
  88. data/ext/libgraphqlparser/libgraphqlparser.pc.in +11 -0
  89. data/ext/libgraphqlparser/parser.ypp +693 -0
  90. data/ext/libgraphqlparser/parsergen/lexer.cpp +2633 -0
  91. data/ext/libgraphqlparser/parsergen/lexer.h +528 -0
  92. data/ext/libgraphqlparser/parsergen/location.hh +189 -0
  93. data/ext/libgraphqlparser/parsergen/parser.tab.cpp +3300 -0
  94. data/ext/libgraphqlparser/parsergen/parser.tab.hpp +646 -0
  95. data/ext/libgraphqlparser/parsergen/position.hh +179 -0
  96. data/ext/libgraphqlparser/parsergen/stack.hh +156 -0
  97. data/ext/libgraphqlparser/python/CMakeLists.txt +14 -0
  98. data/ext/libgraphqlparser/python/README.md +5 -0
  99. data/ext/libgraphqlparser/python/example.py +31 -0
  100. data/ext/libgraphqlparser/syntaxdefs.h +19 -0
  101. data/ext/libgraphqlparser/test/BuildCAPI.c +5 -0
  102. data/ext/libgraphqlparser/test/CMakeLists.txt +25 -0
  103. data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +28 -0
  104. data/ext/libgraphqlparser/test/ParserTests.cpp +352 -0
  105. data/ext/libgraphqlparser/test/kitchen-sink.graphql +59 -0
  106. data/ext/libgraphqlparser/test/kitchen-sink.json +1 -0
  107. data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +78 -0
  108. data/ext/libgraphqlparser/test/schema-kitchen-sink.json +1 -0
  109. data/ext/libgraphqlparser/test/valgrind.supp +33 -0
  110. data/ext/version.cpp +21 -0
  111. data/lib/generators/graphql/controller_generator.rb +22 -0
  112. data/lib/generators/graphql/schema_generator.rb +22 -0
  113. data/lib/generators/graphql/templates/controller.erb +5 -0
  114. data/lib/generators/graphql/templates/schema.erb +6 -0
  115. data/lib/graphqlparser.so +0 -0
  116. data/lib/rails-graphql.rb +2 -0
  117. data/lib/rails/graphql.rake +1 -0
  118. data/lib/rails/graphql.rb +185 -0
  119. data/lib/rails/graphql/adapters/mysql_adapter.rb +0 -0
  120. data/lib/rails/graphql/adapters/pg_adapter.rb +50 -0
  121. data/lib/rails/graphql/adapters/sqlite_adapter.rb +39 -0
  122. data/lib/rails/graphql/argument.rb +220 -0
  123. data/lib/rails/graphql/callback.rb +124 -0
  124. data/lib/rails/graphql/collectors.rb +14 -0
  125. data/lib/rails/graphql/collectors/hash_collector.rb +83 -0
  126. data/lib/rails/graphql/collectors/idented_collector.rb +73 -0
  127. data/lib/rails/graphql/collectors/json_collector.rb +114 -0
  128. data/lib/rails/graphql/config.rb +61 -0
  129. data/lib/rails/graphql/directive.rb +203 -0
  130. data/lib/rails/graphql/directive/deprecated_directive.rb +59 -0
  131. data/lib/rails/graphql/directive/include_directive.rb +24 -0
  132. data/lib/rails/graphql/directive/skip_directive.rb +24 -0
  133. data/lib/rails/graphql/errors.rb +42 -0
  134. data/lib/rails/graphql/event.rb +141 -0
  135. data/lib/rails/graphql/field.rb +318 -0
  136. data/lib/rails/graphql/field/input_field.rb +92 -0
  137. data/lib/rails/graphql/field/mutation_field.rb +52 -0
  138. data/lib/rails/graphql/field/output_field.rb +96 -0
  139. data/lib/rails/graphql/field/proxied_field.rb +131 -0
  140. data/lib/rails/graphql/field/resolved_field.rb +96 -0
  141. data/lib/rails/graphql/field/scoped_config.rb +22 -0
  142. data/lib/rails/graphql/field/typed_field.rb +104 -0
  143. data/lib/rails/graphql/helpers.rb +40 -0
  144. data/lib/rails/graphql/helpers/attribute_delegator.rb +39 -0
  145. data/lib/rails/graphql/helpers/inherited_collection.rb +152 -0
  146. data/lib/rails/graphql/helpers/leaf_from_ar.rb +141 -0
  147. data/lib/rails/graphql/helpers/registerable.rb +103 -0
  148. data/lib/rails/graphql/helpers/with_arguments.rb +125 -0
  149. data/lib/rails/graphql/helpers/with_assignment.rb +113 -0
  150. data/lib/rails/graphql/helpers/with_callbacks.rb +55 -0
  151. data/lib/rails/graphql/helpers/with_directives.rb +126 -0
  152. data/lib/rails/graphql/helpers/with_events.rb +81 -0
  153. data/lib/rails/graphql/helpers/with_fields.rb +141 -0
  154. data/lib/rails/graphql/helpers/with_namespace.rb +40 -0
  155. data/lib/rails/graphql/helpers/with_owner.rb +35 -0
  156. data/lib/rails/graphql/helpers/with_schema_fields.rb +230 -0
  157. data/lib/rails/graphql/helpers/with_validator.rb +52 -0
  158. data/lib/rails/graphql/introspection.rb +53 -0
  159. data/lib/rails/graphql/native.rb +56 -0
  160. data/lib/rails/graphql/native/functions.rb +38 -0
  161. data/lib/rails/graphql/native/location.rb +41 -0
  162. data/lib/rails/graphql/native/pointers.rb +23 -0
  163. data/lib/rails/graphql/native/visitor.rb +349 -0
  164. data/lib/rails/graphql/railtie.rb +85 -0
  165. data/lib/rails/graphql/railties/base_generator.rb +35 -0
  166. data/lib/rails/graphql/railties/controller.rb +101 -0
  167. data/lib/rails/graphql/railties/controller_runtime.rb +40 -0
  168. data/lib/rails/graphql/railties/log_subscriber.rb +62 -0
  169. data/lib/rails/graphql/request.rb +343 -0
  170. data/lib/rails/graphql/request/arguments.rb +93 -0
  171. data/lib/rails/graphql/request/component.rb +100 -0
  172. data/lib/rails/graphql/request/component/field.rb +225 -0
  173. data/lib/rails/graphql/request/component/fragment.rb +118 -0
  174. data/lib/rails/graphql/request/component/operation.rb +178 -0
  175. data/lib/rails/graphql/request/component/operation/subscription.rb +16 -0
  176. data/lib/rails/graphql/request/component/spread.rb +119 -0
  177. data/lib/rails/graphql/request/component/typename.rb +82 -0
  178. data/lib/rails/graphql/request/context.rb +51 -0
  179. data/lib/rails/graphql/request/errors.rb +54 -0
  180. data/lib/rails/graphql/request/event.rb +112 -0
  181. data/lib/rails/graphql/request/helpers/directives.rb +64 -0
  182. data/lib/rails/graphql/request/helpers/selection_set.rb +87 -0
  183. data/lib/rails/graphql/request/helpers/value_writers.rb +115 -0
  184. data/lib/rails/graphql/request/steps/organizable.rb +146 -0
  185. data/lib/rails/graphql/request/steps/prepareable.rb +33 -0
  186. data/lib/rails/graphql/request/steps/resolveable.rb +32 -0
  187. data/lib/rails/graphql/request/strategy.rb +249 -0
  188. data/lib/rails/graphql/request/strategy/dynamic_instance.rb +41 -0
  189. data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +36 -0
  190. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +28 -0
  191. data/lib/rails/graphql/schema.rb +272 -0
  192. data/lib/rails/graphql/shortcuts.rb +77 -0
  193. data/lib/rails/graphql/source.rb +371 -0
  194. data/lib/rails/graphql/source/active_record/builders.rb +154 -0
  195. data/lib/rails/graphql/source/active_record_source.rb +231 -0
  196. data/lib/rails/graphql/source/scoped_arguments.rb +87 -0
  197. data/lib/rails/graphql/to_gql.rb +368 -0
  198. data/lib/rails/graphql/type.rb +138 -0
  199. data/lib/rails/graphql/type/enum.rb +206 -0
  200. data/lib/rails/graphql/type/enum/directive_location_enum.rb +30 -0
  201. data/lib/rails/graphql/type/enum/type_kind_enum.rb +57 -0
  202. data/lib/rails/graphql/type/input.rb +134 -0
  203. data/lib/rails/graphql/type/interface.rb +82 -0
  204. data/lib/rails/graphql/type/object.rb +111 -0
  205. data/lib/rails/graphql/type/object/directive_object.rb +34 -0
  206. data/lib/rails/graphql/type/object/enum_value_object.rb +25 -0
  207. data/lib/rails/graphql/type/object/field_object.rb +54 -0
  208. data/lib/rails/graphql/type/object/input_value_object.rb +49 -0
  209. data/lib/rails/graphql/type/object/schema_object.rb +40 -0
  210. data/lib/rails/graphql/type/object/type_object.rb +136 -0
  211. data/lib/rails/graphql/type/scalar.rb +71 -0
  212. data/lib/rails/graphql/type/scalar/bigint_scalar.rb +34 -0
  213. data/lib/rails/graphql/type/scalar/binary_scalar.rb +30 -0
  214. data/lib/rails/graphql/type/scalar/boolean_scalar.rb +37 -0
  215. data/lib/rails/graphql/type/scalar/date_scalar.rb +34 -0
  216. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +32 -0
  217. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +35 -0
  218. data/lib/rails/graphql/type/scalar/float_scalar.rb +32 -0
  219. data/lib/rails/graphql/type/scalar/id_scalar.rb +39 -0
  220. data/lib/rails/graphql/type/scalar/int_scalar.rb +36 -0
  221. data/lib/rails/graphql/type/scalar/string_scalar.rb +28 -0
  222. data/lib/rails/graphql/type/scalar/time_scalar.rb +40 -0
  223. data/lib/rails/graphql/type/union.rb +87 -0
  224. data/lib/rails/graphql/type_map.rb +347 -0
  225. data/lib/rails/graphql/version.rb +7 -0
  226. data/test/assets/introspection-db.json +0 -0
  227. data/test/assets/introspection-mem.txt +1 -0
  228. data/test/assets/introspection.gql +91 -0
  229. data/test/assets/luke.jpg +0 -0
  230. data/test/assets/mem.gql +428 -0
  231. data/test/assets/sqlite.gql +423 -0
  232. data/test/config.rb +80 -0
  233. data/test/graphql/request/context_test.rb +70 -0
  234. data/test/graphql/schema_test.rb +190 -0
  235. data/test/graphql/source_test.rb +237 -0
  236. data/test/graphql/type/enum_test.rb +203 -0
  237. data/test/graphql/type/input_test.rb +138 -0
  238. data/test/graphql/type/interface_test.rb +72 -0
  239. data/test/graphql/type/object_test.rb +104 -0
  240. data/test/graphql/type/scalar/bigint_scalar_test.rb +42 -0
  241. data/test/graphql/type/scalar/binary_scalar_test.rb +17 -0
  242. data/test/graphql/type/scalar/boolean_scalar_test.rb +40 -0
  243. data/test/graphql/type/scalar/date_scalar_test.rb +29 -0
  244. data/test/graphql/type/scalar/date_time_scalar_test.rb +29 -0
  245. data/test/graphql/type/scalar/decimal_scalar_test.rb +28 -0
  246. data/test/graphql/type/scalar/float_scalar_test.rb +22 -0
  247. data/test/graphql/type/scalar/id_scalar_test.rb +26 -0
  248. data/test/graphql/type/scalar/int_scalar_test.rb +26 -0
  249. data/test/graphql/type/scalar/string_scalar_test.rb +17 -0
  250. data/test/graphql/type/scalar/time_scalar_test.rb +36 -0
  251. data/test/graphql/type/scalar_test.rb +45 -0
  252. data/test/graphql/type/union_test.rb +82 -0
  253. data/test/graphql/type_map_test.rb +362 -0
  254. data/test/graphql/type_test.rb +68 -0
  255. data/test/graphql_test.rb +55 -0
  256. data/test/integration/config.rb +56 -0
  257. data/test/integration/memory/star_wars_introspection_test.rb +144 -0
  258. data/test/integration/memory/star_wars_query_test.rb +184 -0
  259. data/test/integration/memory/star_wars_validation_test.rb +99 -0
  260. data/test/integration/schemas/memory.rb +232 -0
  261. data/test/integration/schemas/sqlite.rb +82 -0
  262. data/test/integration/sqlite/star_wars_introspection_test.rb +15 -0
  263. data/test/integration/sqlite/star_wars_mutation_test.rb +82 -0
  264. data/test/integration/sqlite/star_wars_query_test.rb +71 -0
  265. data/test/test_ext.rb +48 -0
  266. metadata +509 -0
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ class Request # :nodoc:
6
+ # = GraphQL Request Component Operation
7
+ #
8
+ # This class holds information about a given operation. This will guide
9
+ # the validation and execution of it.
10
+ class Component::Operation < Component
11
+ extend ActiveSupport::Autoload
12
+
13
+ include SelectionSet
14
+ include Directives
15
+
16
+ class << self
17
+ alias type kind
18
+
19
+ # Helper method to initialize an operation given the data
20
+ def build(request, node, data)
21
+ request.build(const_get(data[:kind].classify), request, node, data)
22
+ end
23
+
24
+ # Rewrite the kind to always return +:operation+
25
+ def kind
26
+ :operation
27
+ end
28
+
29
+ # Defines if the current operation is a query type
30
+ def query?
31
+ false
32
+ end
33
+
34
+ # Defines if the current operation is a mutation type
35
+ def mutation?
36
+ false
37
+ end
38
+
39
+ # Defines if the current operation is a subscription type
40
+ def subscription?
41
+ false
42
+ end
43
+ end
44
+
45
+ # Query is default behavior, so it doesn't need a whole class
46
+ Query = Class.new(self) { redefine_singleton_method(:query?) { true } }
47
+ Mutation = Class.new(self) { redefine_singleton_method(:mutation?) { true } }
48
+
49
+ autoload :Subscription
50
+
51
+ delegate :type, :query?, :mutation?, :subscription?, to: :class
52
+
53
+ attr_reader :name, :variables, :arguments, :request
54
+
55
+ alias gql_name name
56
+ alias operation itself
57
+ alias vars variables
58
+ alias all_arguments arguments
59
+
60
+ def initialize(request, node, data)
61
+ @name = data[:name]
62
+ @request = request
63
+
64
+ super(node, data)
65
+
66
+ check_invalid_operation!
67
+ end
68
+
69
+ # The list of fields comes from the +fields_for+ of the same type as
70
+ # the +type+ of the operation
71
+ def fields_source
72
+ schema.fields_for(type)
73
+ end
74
+
75
+ # The typename is always based on the fake name used for the set of
76
+ # schema fields
77
+ def typename
78
+ schema.type_name_for(type)
79
+ end
80
+
81
+ # Support memory object to save information across the iteration
82
+ def memo
83
+ @memo ||= OpenStruct.new
84
+ end
85
+
86
+ # Add a empty entry if the operation has a name
87
+ def resolve_invalid
88
+ response.safe_add(name, nil) if stacked_selection?
89
+ end
90
+
91
+ # Stores all the used arguments to report not used ones
92
+ def used_variables
93
+ @used_variables ||= Set.new
94
+ end
95
+
96
+ # A fast way to access the correct display name for log or errors
97
+ def log_source
98
+ @log_source ||= name.blank? ? type : "#{name} #{type}"
99
+ end
100
+
101
+ protected
102
+
103
+ # Trigger an specific event with the +type+ of the operation
104
+ def organize
105
+ organize_then do
106
+ trigger_event(type)
107
+ yield if block_given?
108
+ organize_fields
109
+ report_unused_variables
110
+ end
111
+ end
112
+
113
+ # Perform the organization step
114
+ def organize_then(&block)
115
+ super(block) do
116
+ parse_variables
117
+ parse_directives(type)
118
+ parse_selection
119
+ end
120
+ end
121
+
122
+ # Resolve all the fields
123
+ def resolve
124
+ resolve_then { resolve_fields }
125
+ end
126
+
127
+ # Don't stack over response when the operation doesn't have a name
128
+ # TODO: As per spec, when an operation has variables, it should not
129
+ # be stacked
130
+ def stacked_selection?
131
+ name.present? && request.operations.size > 1
132
+ end
133
+
134
+ # Name used for debug purposes
135
+ def display_name
136
+ @display_name ||= "#{type.to_s.titlecase} #{name.presence || '__default__'}"
137
+ end
138
+
139
+ # Add an error for each not used variable and then clean up some data
140
+ def report_unused_variables
141
+ (arguments.keys - used_variables.to_a).each do |key|
142
+ argument = arguments[key]
143
+ request.report_node_error(<<~MSG.squish, argument.node || @node)
144
+ Unused variable $#{argument.gql_name} on #{log_source}.
145
+ MSG
146
+ end
147
+
148
+ @arguments = nil
149
+ @used_variables = nil
150
+ end
151
+
152
+ # If there is another operation with the same name already defined,
153
+ # raise an error. If an anonymous was started, then any other
154
+ # operatios is invalid.
155
+ def check_invalid_operation!
156
+ if request.operations.key?(nil)
157
+ invalidate!
158
+
159
+ request.report_node_error(<<~MSG.squish, @node)
160
+ Unable to process the operation #{display_name} when the document
161
+ contain multiple anonymous operations.
162
+ MSG
163
+ elsif request.operations.key?(name)
164
+ invalidate!
165
+
166
+ other_node = request.operations[name].instance_variable_get(:@node)
167
+ location = GraphQL::Native.get_location(other_node)
168
+
169
+ request.report_node_error(<<~MSG.squish, @node)
170
+ Duplicated operation named "#{name}" defined on
171
+ line #{location.begin_line}:#{location.begin_column}.
172
+ MSG
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ class Request # :nodoc:
6
+ class Component # :nodoc:
7
+ # = GraphQL Request Component Subscription Operation
8
+ #
9
+ # Handles a subscription operation inside a request.
10
+ class Operation::Subscription < Operation
11
+ redefine_singleton_method(:subscription?) { true }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ class Request # :nodoc:
6
+ # = GraphQL Request Component Spread
7
+ #
8
+ # This class holds information about a given spread that should be
9
+ # iterated, which connect to either a fragment or an inline selection
10
+ class Component::Spread < Component
11
+ include SelectionSet
12
+ include Directives
13
+
14
+ delegate :operation, :typename, to: :parent
15
+ delegate :variables, to: :operation
16
+
17
+ parent_memoize :request
18
+
19
+ attr_reader :name, :parent, :fragment, :type_klass
20
+
21
+ def initialize(parent, node, data)
22
+ @parent = parent
23
+
24
+ @name = data[:name]
25
+ @inline = data[:inline]
26
+
27
+ super(node, data)
28
+ end
29
+
30
+ # Check if the object is an inline spread
31
+ def inline?
32
+ @inline.present?
33
+ end
34
+
35
+ # Redirect to the fragment or check the inline type before resolving
36
+ def resolve_with!(object)
37
+ return if invalid?
38
+
39
+ @current_object = object
40
+ resolve!
41
+ ensure
42
+ @current_object = nil
43
+ end
44
+
45
+ protected
46
+
47
+ # Spread always resolve inline selection unstacked on response,
48
+ # meaning that its fields will be set in the same level as the parent
49
+ def stacked_selection?
50
+ false
51
+ end
52
+
53
+ # Just provide the correct location for directives
54
+ def parse_directives
55
+ super(inline? ? :inline_fragment : :fragment_spread)
56
+ end
57
+
58
+ # Scope the arguments whenever stacked within a spread
59
+ def stacked(*)
60
+ Request::Arguments.scoped(operation) { super }
61
+ end
62
+
63
+ # Normal mode of the organize step
64
+ # TODO: Once the fragment is organized, double checks the needed
65
+ # variables to ensure that the operation has everything necessary
66
+ def organize
67
+ inline? ? organize_then { organize_fields } : organize_then do
68
+ run_on_fragment(:organize!)
69
+ # Ensure necessary variables
70
+ end
71
+ end
72
+
73
+ # Perform the organization step
74
+ def organize_then(&block)
75
+ super(block) do
76
+ if inline?
77
+ @type_klass = find_type!(data[:type])
78
+ parse_selection
79
+ else
80
+ @fragment = request.fragments[name]
81
+ raise ArgumentError, <<~MSG.squish if @fragment.nil?
82
+ The "#{name}" fragment is not defined in this request.
83
+ MSG
84
+ end
85
+
86
+ parse_directives
87
+ end
88
+ end
89
+
90
+ # Spread has a special behavior when using a fragment
91
+ def prepare
92
+ return super if inline?
93
+ raise 'Prepare with fragment not implemented yet'
94
+ end
95
+
96
+ # Resolve the spread operation
97
+ def resolve
98
+ return if invalid?
99
+
100
+ object = (defined?(@current_object) && @current_object) || parent.type_klass
101
+ return run_on_fragment(:resolve_with!, object) unless inline?
102
+
103
+ super if type_klass =~ object
104
+ end
105
+
106
+ # This will just trigger the selection resolver
107
+ def resolve_then(&block)
108
+ super(block) { resolve_fields(@current_object) }
109
+ end
110
+
111
+ # Most of the things that are redirected to the fragment needs to run
112
+ # inside a arguments scoped
113
+ def run_on_fragment(method_name, *args)
114
+ Request::Arguments.scoped(operation) { fragment.public_send(method_name, *args) }
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ class Request # :nodoc:
6
+ # = GraphQL Request Component Typename
7
+ #
8
+ # Extra component which simulates a field that its only purpose is to
9
+ # return the name of the parent GraphQL object
10
+ class Component::Typename < Component
11
+ include ValueWriters
12
+ include Directives
13
+
14
+ delegate :operation, to: :parent
15
+ delegate :variables, to: :operation
16
+
17
+ parent_memoize :request
18
+
19
+ attr_reader :name, :alias_name, :parent
20
+
21
+ # Rewrite the kind to always return +:field+
22
+ def self.kind
23
+ :field
24
+ end
25
+
26
+ def initialize(parent, node, data)
27
+ @parent = parent
28
+
29
+ @name = data[:name]
30
+ @alias_name = data[:alias]
31
+
32
+ super(node, data)
33
+ end
34
+
35
+ # Set the value that the field will be resolved as
36
+ def resolve_with!(object)
37
+ @typename = object.gql_name
38
+ resolve!
39
+ ensure
40
+ @typename = nil
41
+ end
42
+
43
+ # Return the name of the field to be used on the response
44
+ def gql_name
45
+ alias_name || name
46
+ end
47
+
48
+ # Write the typename information
49
+ def write_value(value)
50
+ response.serialize(Type::Scalar::StringScalar, gql_name, value)
51
+ end
52
+
53
+ protected
54
+
55
+ # Normal mode of the organize step
56
+ def organize
57
+ organize_then
58
+ end
59
+
60
+ # Perform the organization step
61
+ def organize_then(&block)
62
+ super(block) { parse_directives(:field) }
63
+ end
64
+
65
+ # Go through the right flow to write the value
66
+ def resolve_then
67
+ super do
68
+ typename = @typename || parent.typename
69
+ raise InvalidValueError, <<~MSG.squish if typename.blank?
70
+ The #{gql_name} field value cannot be null.
71
+ MSG
72
+
73
+ strategy.resolve(self, typename) do |value|
74
+ yield value if block_given?
75
+ trigger_event(:finalize)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails # :nodoc:
4
+ module GraphQL # :nodoc:
5
+ class Request # :nodoc:
6
+ # = GraphQL Request Context
7
+ #
8
+ # This class is used as context for the response while processing fields,
9
+ # objects, or any data that it's going to be placed on the response
10
+ class Context
11
+ attr_reader :current
12
+
13
+ def initialize
14
+ @stack = []
15
+ @current = Helpers::AttributeDelegator.new(self, :current_value, cache: false)
16
+ end
17
+
18
+ # Add, exec, and then remove the value from the stack
19
+ def stacked(value)
20
+ @stack.unshift(value) unless value.eql?(@stack[0])
21
+ yield(@current)
22
+ ensure
23
+ @stack.shift
24
+ end
25
+
26
+ # Find the parent object
27
+ def parent
28
+ @stack[1]
29
+ end
30
+
31
+ # Get all ancestors objects
32
+ def ancestors
33
+ @stack[1..-1]
34
+ end
35
+
36
+ # Get the current value, which basically means basically the first item
37
+ # on the current stafck
38
+ def current_value
39
+ @stack[0]
40
+ end
41
+
42
+ # Change the current value, either form hits or the actual value
43
+ def override_value(value)
44
+ @stack[0] = value
45
+ end
46
+
47
+ alias current_value= override_value
48
+ end
49
+ end
50
+ end
51
+ end