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,56 @@
1
+ require 'config'
2
+
3
+ require 'active_support/core_ext/class/subclasses'
4
+
5
+ module GraphQL
6
+ class IntegrationTestCase < TestCase
7
+ SCHEMAS = Pathname.new(__dir__).join('schemas')
8
+ ASSETS = Pathname.new(__dir__).join('../assets')
9
+
10
+ def setup
11
+ reset_type_map!
12
+ end
13
+
14
+ protected
15
+
16
+ def self.load_schema(name)
17
+ require(SCHEMAS.join("#{name}"))
18
+ end
19
+
20
+ def reset_type_map!
21
+ Rails::GraphQL.type_map.hard_reset!
22
+ end
23
+
24
+ def named_list(*list, **extra)
25
+ list.map { |x| extra.reverse_merge(name: x) }
26
+ end
27
+
28
+ def gql_file(name)
29
+ ASSETS.join("#{name}.gql").read
30
+ end
31
+
32
+ def text_file(name)
33
+ ASSETS.join("#{name}.txt").read
34
+ end
35
+
36
+ def json_file(name)
37
+ JSON.parse(ASSETS.join("#{name}.json").read)
38
+ end
39
+
40
+ def execute(*args, **xargs)
41
+ xargs[:as] ||= :object
42
+ xargs[:schema] ||= self.class.const_get(:SCHEMA)
43
+ ::GraphQL.execute(*args, **xargs)
44
+ end
45
+
46
+ def assert_result(obj, *args, dig: nil, **xargs)
47
+ result = execute(*args, **xargs)
48
+ result = result.try(:dig, *dig) if !!dig
49
+ yield result if block_given?
50
+
51
+ obj = obj.deep_stringify_keys if obj.is_a?(Hash)
52
+ obj = obj.map(&:deep_stringify_keys) if obj.is_a?(Array)
53
+ obj.nil? ? assert_nil(result) : assert_equal(obj, result)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,144 @@
1
+ require 'integration/config'
2
+
3
+ # See: https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsIntrospection-test.js
4
+ class Integration_Memory_StarWarsIntrospectionTest < GraphQL::IntegrationTestCase
5
+ load_schema 'memory'
6
+
7
+ SCHEMA = ::StartWarsMemSchema
8
+
9
+ def test_query_schema_types
10
+ types = named_list(*%w[Bigint Binary Boolean Character Date DateTime Decimal Droid Episode
11
+ Float Human ID Int String Time _Mutation _Query __Directive __DirectiveLocation
12
+ __EnumValue __Field __InputValue __Schema __Type __TypeKind])
13
+
14
+ sort_items = ->(result) do
15
+ result.dig('data', '__schema', 'types').sort_by! { |t| t['name'] }
16
+ end
17
+
18
+ assert_result({ data: { __schema: { types: types } } }, <<~GQL, &sort_items)
19
+ { __schema { types { name } } }
20
+ GQL
21
+ end
22
+
23
+ def test_query_schema_query_type
24
+ assert_result({ data: { __schema: { queryType: { name: '_Query' } } } }, <<~GQL)
25
+ { __schema { queryType { name } } }
26
+ GQL
27
+ end
28
+
29
+ def test_query_schema_mutation_type
30
+ assert_result({ data: { __schema: { mutationType: { name: '_Mutation' } } } }, <<~GQL)
31
+ { __schema { mutationType { name } } }
32
+ GQL
33
+ end
34
+
35
+ def test_query_specific_type
36
+ assert_result({ data: { __type: { name: 'Droid' } } }, <<~GQL)
37
+ { __type(name: "Droid") { name } }
38
+ GQL
39
+ end
40
+
41
+ def test_query_specific_type_with_kind
42
+ assert_result({ data: { __type: { name: 'Droid', kind: 'OBJECT' } } }, <<~GQL)
43
+ { __type(name: "Droid") { name kind } }
44
+ GQL
45
+ end
46
+
47
+ def test_query_specific_type_as_interface
48
+ assert_result({ data: { __type: { name: 'Character', kind: 'INTERFACE' } } }, <<~GQL)
49
+ { __type(name: "Character") { name kind } }
50
+ GQL
51
+ end
52
+
53
+ def test_query_object_fields
54
+ fields = [
55
+ { name: 'id', type: { name: nil, kind: 'NON_NULL' } },
56
+ { name: 'name', type: { name: 'String', kind: 'SCALAR' } },
57
+ { name: 'friends', type: { name: nil, kind: 'LIST' } },
58
+ { name: 'appearsIn', type: { name: nil, kind: 'LIST' } },
59
+ { name: 'secretBackstory', type: { name: 'String', kind: 'SCALAR' } },
60
+ { name: 'primaryFunction', type: { name: 'String', kind: 'SCALAR' } },
61
+ ]
62
+
63
+ assert_result({ data: { __type: { name: 'Droid', fields: fields } } }, <<~GQL)
64
+ { __type(name: "Droid") { name fields { name type { name kind } } } }
65
+ GQL
66
+ end
67
+
68
+ def test_query_object_fields_with_nested_type
69
+ fields = [
70
+ { name: 'id', type: { name: nil, kind: 'NON_NULL', ofType: { name: 'ID', kind: 'SCALAR' } } },
71
+ { name: 'name', type: { name: 'String', kind: 'SCALAR', ofType: nil } },
72
+ { name: 'friends', type: { name: nil, kind: 'LIST', ofType: { name: 'Character', kind: 'INTERFACE' } } },
73
+ { name: 'appearsIn', type: { name: nil, kind: 'LIST', ofType: { name: 'Episode', kind: 'ENUM' } } },
74
+ { name: 'secretBackstory', type: { name: 'String', kind: 'SCALAR', ofType: nil } },
75
+ { name: 'primaryFunction', type: { name: 'String', kind: 'SCALAR', ofType: nil } },
76
+ ]
77
+
78
+ assert_result({ data: { __type: { name: 'Droid', fields: fields } } }, <<~GQL)
79
+ {
80
+ __type(name: "Droid") {
81
+ name fields { name type { name kind ofType { name kind } } }
82
+ }
83
+ }
84
+ GQL
85
+ end
86
+
87
+ def test_query_object_with_arguments
88
+ name_arg = { name: 'name', description: nil, type: {
89
+ name: nil, kind: 'NON_NULL', ofType: { name: 'String', kind: 'SCALAR' } ,
90
+ }, defaultValue: nil }
91
+
92
+ epi_arg = { name: 'episode', description: 'Return for a specific episode', type: {
93
+ name: 'Episode', kind: 'ENUM', ofType: nil,
94
+ }, defaultValue: nil }
95
+
96
+ id_arg = { name: 'id', description: nil, type: {
97
+ name: nil, kind: 'NON_NULL', ofType: { name: 'ID', kind: 'SCALAR' } ,
98
+ }, defaultValue: nil }
99
+
100
+ fields = [
101
+ { name: '__schema', args: [] },
102
+ { name: '__type', args: [name_arg] },
103
+ { name: 'hero', args: [epi_arg] },
104
+ { name: 'human', args: [id_arg.merge(description: 'ID of the human')] },
105
+ { name: 'droid', args: [id_arg.merge(description: 'ID of the droid')] },
106
+ ]
107
+
108
+ assert_result({ data: { __schema: { queryType: { fields: fields } } } }, <<~GQL)
109
+ { __schema { queryType { fields {
110
+ name
111
+ args {
112
+ name
113
+ description
114
+ type { name kind ofType { name kind } }
115
+ defaultValue
116
+ }
117
+ } } } }
118
+ GQL
119
+ end
120
+
121
+ def test_query_schema_documentation
122
+ description = 'A mechanical creature in the Star Wars universe'
123
+ assert_result({ data: { __type: { name: 'Droid', description: description } } }, <<~GQL)
124
+ { __type(name: "Droid") { name description } }
125
+ GQL
126
+ end
127
+
128
+ # There are some issues with the end sorting, so compare the string result
129
+ # with sorted characters, which will produce the exact match
130
+ def test_query_full_introspection
131
+ query = gql_file('introspection')
132
+ result = text_file('introspection-mem').split('').sort.join
133
+ assert_result(result, query, as: :string) do |res|
134
+ # File.write('test/assets/introspection-mem.txt', res)
135
+ res.replace(res.split('').sort.join)
136
+ end
137
+ end
138
+
139
+ def test_gql_introspection
140
+ # File.write('test/assets/mem.gql', SCHEMA.to_gql)
141
+ result = gql_file('mem').split('').sort.join.squish
142
+ assert_equal(result, SCHEMA.to_gql.split('').sort.join.squish)
143
+ end
144
+ end
@@ -0,0 +1,184 @@
1
+ require 'integration/config'
2
+
3
+ # See: https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsQuery-test.js
4
+ class Integration_Memory_StarWarsQueryTest < GraphQL::IntegrationTestCase
5
+ load_schema 'memory'
6
+
7
+ SCHEMA = ::StartWarsMemSchema
8
+ ALL_EPISODES = %w[NEW_HOPE EMPIRE JEDI]
9
+
10
+ def test_r2d2_saga_hero
11
+ assert_result({ data: { hero: { name: 'R2-D2' } } }, <<~GQL)
12
+ query HeroNameQuery { hero { name } }
13
+ GQL
14
+ end
15
+
16
+ def test_r2d2_by_id_and_friends
17
+ friends = named_list('Luke Skywalker', 'Han Solo', 'Leia Organa')
18
+ hero = { id: '2001', name: 'R2-D2', friends: friends }
19
+ assert_result({ data: { hero: hero } }, <<~GQL)
20
+ query HeroNameAndFriendsQuery {
21
+ hero { id name friends { name } }
22
+ }
23
+ GQL
24
+ end
25
+
26
+ def test_r2d2_friends_of_friends
27
+ friends1 = named_list('Han Solo', 'Leia Organa', 'C-3PO', 'R2-D2')
28
+ friends2 = named_list('Luke Skywalker', 'Leia Organa', 'R2-D2')
29
+ friends3 = named_list('Luke Skywalker', 'Han Solo', 'C-3PO', 'R2-D2')
30
+ friends = [
31
+ { name: 'Luke Skywalker', appearsIn: ALL_EPISODES, friends: friends1 },
32
+ { name: 'Han Solo', appearsIn: ALL_EPISODES, friends: friends2 },
33
+ { name: 'Leia Organa', appearsIn: ALL_EPISODES, friends: friends3 },
34
+ ]
35
+
36
+ hero = { name: 'R2-D2', friends: friends }
37
+ assert_result({ data: { hero: hero } }, <<~GQL)
38
+ query NestedQuery {
39
+ hero { name friends { name appearsIn friends { name } } }
40
+ }
41
+ GQL
42
+ end
43
+
44
+ def test_using_ids_refetch
45
+ human = { name: 'Luke Skywalker' }
46
+ droid = { name: 'C-3PO' }
47
+
48
+ assert_result({ data: { human: human, droid: droid } }, <<~GQL)
49
+ query FetchLukeAndC3POQuery {
50
+ human(id: "1000") { name }
51
+ droid(id: "2000") { name }
52
+ }
53
+ GQL
54
+ end
55
+
56
+ def test_generic_query_fetch_by_id_luke
57
+ xargs = { args: { some_id: '1000' } }
58
+ assert_result({ data: { human: { name: 'Luke Skywalker' } } }, <<~GQL, **xargs)
59
+ query FetchSomeIDQuery($someId: ID!) {
60
+ human(id: $someId) { name }
61
+ }
62
+ GQL
63
+ end
64
+
65
+ def test_generic_query_fetch_by_id_han
66
+ xargs = { args: { some_id: '1002' } }
67
+ assert_result({ data: { human: { name: 'Han Solo' } } }, <<~GQL, **xargs)
68
+ query FetchSomeIDQuery($someId: ID!) {
69
+ human(id: $someId) { name }
70
+ }
71
+ GQL
72
+ end
73
+
74
+ def test_generic_query_fetch_by_id_invalid
75
+ xargs = { args: { id: 'not a valid id' } }
76
+ assert_result({ data: { human: nil } }, <<~GQL, **xargs)
77
+ query FetchSomeIDQuery($id: ID!) {
78
+ human(id: $id) { name }
79
+ }
80
+ GQL
81
+ end
82
+
83
+ def test_using_alias_to_change_key_name
84
+ assert_result({ data: { luke: { name: 'Luke Skywalker' } } }, <<~GQL)
85
+ query FetchLukeAliased { luke: human(id: "1000") { name } }
86
+ GQL
87
+ end
88
+
89
+ def test_using_alias_to_change_key_name_twice
90
+ luke = { name: 'Luke Skywalker' }
91
+ leia = { name: 'Leia Organa' }
92
+ assert_result({ data: { luke: luke, leia: leia } }, <<~GQL)
93
+ query FetchLukeAliased {
94
+ luke: human(id: "1000") { name }
95
+ leia: human(id: "1003") { name }
96
+ }
97
+ GQL
98
+ end
99
+
100
+ def test_query_with_duplicated_content
101
+ luke = { name: 'Luke Skywalker', homePlanet: 'Tatooine' }
102
+ leia = { name: 'Leia Organa', homePlanet: 'Alderaan' }
103
+ assert_result({ data: { luke: luke, leia: leia } }, <<~GQL)
104
+ query DuplicateFields {
105
+ luke: human(id: "1000") { name homePlanet }
106
+ leia: human(id: "1003") { name homePlanet }
107
+ }
108
+ GQL
109
+ end
110
+
111
+ def test_query_with_fragment
112
+ luke = { name: 'Luke Skywalker', homePlanet: 'Tatooine' }
113
+ leia = { name: 'Leia Organa', homePlanet: 'Alderaan' }
114
+ assert_result({ data: { luke: luke, leia: leia } }, <<~GQL)
115
+ query DuplicateFields {
116
+ luke: human(id: "1000") { ...HumanFragment }
117
+ leia: human(id: "1003") { ...HumanFragment }
118
+ }
119
+
120
+ fragment HumanFragment on Human {
121
+ name
122
+ homePlanet
123
+ }
124
+ GQL
125
+ end
126
+
127
+ def test_get_typename_field
128
+ assert_result({ data: { hero: { __typename: 'Droid', name: 'R2-D2' } } }, <<~GQL)
129
+ query CheckTypeOfR2 { hero { __typename name } }
130
+ GQL
131
+ end
132
+
133
+ def test_get_typename_from_luke
134
+ luke = { __typename: 'Human', name: 'Luke Skywalker' }
135
+ assert_result({ data: { hero: luke } }, <<~GQL)
136
+ query CheckTypeOfLuke { hero(episode: EMPIRE) { __typename name } }
137
+ GQL
138
+ end
139
+
140
+ def test_error_on_secret_backstory
141
+ hero = { name: 'R2-D2', secretBackstory: nil }
142
+ errors = [{
143
+ message: 'Secret backstory is secret',
144
+ locations: [{ line: 1, column: 35 }, { line: 1, column: 50 }],
145
+ path: %w[HeroNameQuery hero secretBackstory],
146
+ extensions: { exception: 'RuntimeError' },
147
+ }]
148
+
149
+ assert_result({ data: { hero: hero }, errors: errors }, <<~GQL)
150
+ query HeroNameQuery { hero { name secretBackstory } }
151
+ GQL
152
+ end
153
+
154
+ def test_nested_secret_backstory
155
+ friends = named_list('Luke Skywalker', 'Han Solo', 'Leia Organa', secretBackstory: nil)
156
+ hero = { name: 'R2-D2', friends: friends }
157
+ errors = 3.times.map do |n|
158
+ {
159
+ message: 'Secret backstory is secret',
160
+ locations: [{ line: 1, column: 50 }, { line: 1, column: 65 }],
161
+ path: ['HeroNameQuery', 'hero', 'friends', n, 'secretBackstory'],
162
+ extensions: { exception: 'RuntimeError' },
163
+ }
164
+ end
165
+
166
+ assert_result({ data: { hero: hero }, errors: errors }, <<~GQL)
167
+ query HeroNameQuery { hero { name friends { name secretBackstory } } }
168
+ GQL
169
+ end
170
+
171
+ def test_backstory_on_alias
172
+ hero = { name: 'R2-D2', story: nil }
173
+ errors = [{
174
+ message: 'Secret backstory is secret',
175
+ locations: [{ line: 1, column: 35 }, { line: 1, column: 57 }],
176
+ path: %w[HeroNameQuery hero story],
177
+ extensions: { exception: 'RuntimeError' },
178
+ }]
179
+
180
+ assert_result({ data: { hero: hero }, errors: errors }, <<~GQL)
181
+ query HeroNameQuery { hero { name story: secretBackstory } }
182
+ GQL
183
+ end
184
+ end
@@ -0,0 +1,99 @@
1
+ require 'integration/config'
2
+
3
+ # See: https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsValidation-test.js
4
+ class Integration_Memory_StarWarsValidationTest < GraphQL::IntegrationTestCase
5
+ load_schema 'memory'
6
+
7
+ SCHEMA = ::StartWarsMemSchema
8
+
9
+ def test_complex_but_valid_query
10
+ assert_result(nil, <<~GQL, dig: 'errors')
11
+ query NestedQueryWithFragment { hero {
12
+ ...NameAndAppearances friends { ...NameAndAppearances friends {
13
+ ...NameAndAppearances
14
+ } }
15
+ } }
16
+
17
+ fragment NameAndAppearances on Character {
18
+ name
19
+ appearsIn
20
+ }
21
+ GQL
22
+ end
23
+
24
+ def test_invalid_query
25
+ errors = [{
26
+ message: 'syntax error, unexpected EOF',
27
+ locations: [{ line: 2, column: 1 }],
28
+ }]
29
+
30
+ assert_result(errors, <<~GQL, dig: 'errors')
31
+ query DroidFieldInFragment { hero { name ... on Droid { primaryFunction
32
+ GQL
33
+ end
34
+
35
+ def test_nonexistent_fields
36
+ errors = [{
37
+ message: 'Unable to find a field named "favoriteSpaceship" on GraphQL::CharacterInterface.',
38
+ locations: [{ line: 1, column: 35 }, { line: 1, column: 52 }],
39
+ path: %w[HeroSpaceshipQuery hero favoriteSpaceship],
40
+ extensions: { stage: 'organize', exception: 'Rails::GraphQL::MissingFieldError' },
41
+ }]
42
+
43
+ assert_result(errors, <<~GQL, dig: 'errors')
44
+ query HeroSpaceshipQuery { hero { favoriteSpaceship } }
45
+ GQL
46
+ end
47
+
48
+ def test_requires_fields
49
+ errors = [{
50
+ message: 'The "hero" was assigned to the Character which is not a leaf type and requires a selection of fields.',
51
+ locations: [{ line: 1, column: 28 }, { line: 1, column: 32 }],
52
+ path: %w[HeroSpaceshipQuery hero],
53
+ extensions: { stage: 'organize', exception: 'Rails::GraphQL::FieldError' },
54
+ }]
55
+
56
+ assert_result(errors, <<~GQL, dig: 'errors')
57
+ query HeroSpaceshipQuery { hero }
58
+ GQL
59
+ end
60
+
61
+ def test_disallows_fields_on_scalars
62
+ errors = [{
63
+ message: 'The "name" was assigned to the String which is a leaf type and does not have nested fields.',
64
+ locations: [{ line: 1, column: 35 }, { line: 1, column: 64 }],
65
+ path: %w[HeroSpaceshipQuery hero name],
66
+ extensions: { stage: 'organize', exception: 'Rails::GraphQL::FieldError' },
67
+ }]
68
+
69
+ assert_result(errors, <<~GQL, dig: 'errors')
70
+ query HeroSpaceshipQuery { hero { name { firstCharacterOfName } } }
71
+ GQL
72
+ end
73
+
74
+ def test_specific_fields_on_interfaces
75
+ errors = [{
76
+ message: 'Unable to find a field named "primaryFunction" on GraphQL::CharacterInterface.',
77
+ locations: [{ line: 1, column: 40 }, { line: 1, column: 55 }],
78
+ path: %w[HeroSpaceshipQuery hero primaryFunction],
79
+ extensions: { stage: 'organize', exception: 'Rails::GraphQL::MissingFieldError' },
80
+ }]
81
+
82
+ assert_result(errors, <<~GQL, dig: 'errors')
83
+ query HeroSpaceshipQuery { hero { name primaryFunction } }
84
+ GQL
85
+ end
86
+
87
+ def test_allow_specific_fields_with_fragments
88
+ assert_result(nil, <<~GQL, dig: 'errors')
89
+ query DroidFieldInFragment { hero { name ...DroidFields } }
90
+ fragment DroidFields on Droid { primaryFunction }
91
+ GQL
92
+ end
93
+
94
+ def test_allow_specific_fields_with_spread
95
+ assert_result(nil, <<~GQL, dig: 'errors')
96
+ query DroidFieldInFragment { hero { name ... on Droid { primaryFunction } } }
97
+ GQL
98
+ end
99
+ end