rails-graphql 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +31 -0
- data/ext/depend +3 -0
- data/ext/extconf.rb +57 -0
- data/ext/graphqlparser/Ast.cpp +346 -0
- data/ext/graphqlparser/Ast.h +1214 -0
- data/ext/graphqlparser/AstNode.h +36 -0
- data/ext/graphqlparser/AstVisitor.h +137 -0
- data/ext/graphqlparser/GraphQLParser.cpp +76 -0
- data/ext/graphqlparser/GraphQLParser.h +55 -0
- data/ext/graphqlparser/JsonVisitor.cpp +161 -0
- data/ext/graphqlparser/JsonVisitor.cpp.inc +456 -0
- data/ext/graphqlparser/JsonVisitor.h +121 -0
- data/ext/graphqlparser/JsonVisitor.h.inc +110 -0
- data/ext/graphqlparser/VERSION +1 -0
- data/ext/graphqlparser/c/GraphQLAst.cpp +324 -0
- data/ext/graphqlparser/c/GraphQLAst.h +180 -0
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +44 -0
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/graphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/graphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/graphqlparser/c/GraphQLParser.h +54 -0
- data/ext/graphqlparser/dump_json_ast.cpp +48 -0
- data/ext/graphqlparser/lexer.lpp +324 -0
- data/ext/graphqlparser/parser.ypp +693 -0
- data/ext/graphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/graphqlparser/parsergen/lexer.h +528 -0
- data/ext/graphqlparser/parsergen/location.hh +189 -0
- data/ext/graphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/graphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/graphqlparser/parsergen/position.hh +179 -0
- data/ext/graphqlparser/parsergen/stack.hh +156 -0
- data/ext/graphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/AstNode.h +36 -0
- data/ext/libgraphqlparser/CMakeLists.txt +148 -0
- data/ext/libgraphqlparser/CONTRIBUTING.md +23 -0
- data/ext/libgraphqlparser/GraphQLParser.cpp +76 -0
- data/ext/libgraphqlparser/GraphQLParser.h +55 -0
- data/ext/libgraphqlparser/JsonVisitor.cpp +161 -0
- data/ext/libgraphqlparser/JsonVisitor.h +121 -0
- data/ext/libgraphqlparser/LICENSE +22 -0
- data/ext/libgraphqlparser/README.clang-tidy +7 -0
- data/ext/libgraphqlparser/README.md +84 -0
- data/ext/libgraphqlparser/ast/ast.ast +203 -0
- data/ext/libgraphqlparser/ast/ast.py +61 -0
- data/ext/libgraphqlparser/ast/c.py +100 -0
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +61 -0
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +39 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +26 -0
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +197 -0
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +61 -0
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +42 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +80 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +64 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +65 -0
- data/ext/libgraphqlparser/ast/license.py +10 -0
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +25 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +33 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +21 -0
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +24 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +55 -0
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +53 -0
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +35 -0
- data/ext/libgraphqlparser/c/GraphQLParser.h +54 -0
- data/ext/libgraphqlparser/clang-tidy-all.sh +3 -0
- data/ext/libgraphqlparser/cmake/version.cmake +16 -0
- data/ext/libgraphqlparser/dump_json_ast.cpp +48 -0
- data/ext/libgraphqlparser/go/README.md +20 -0
- data/ext/libgraphqlparser/go/callbacks.go +18 -0
- data/ext/libgraphqlparser/go/gotest.go +64 -0
- data/ext/libgraphqlparser/lexer.lpp +324 -0
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +11 -0
- data/ext/libgraphqlparser/parser.ypp +693 -0
- data/ext/libgraphqlparser/parsergen/lexer.cpp +2633 -0
- data/ext/libgraphqlparser/parsergen/lexer.h +528 -0
- data/ext/libgraphqlparser/parsergen/location.hh +189 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +3300 -0
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +646 -0
- data/ext/libgraphqlparser/parsergen/position.hh +179 -0
- data/ext/libgraphqlparser/parsergen/stack.hh +156 -0
- data/ext/libgraphqlparser/python/CMakeLists.txt +14 -0
- data/ext/libgraphqlparser/python/README.md +5 -0
- data/ext/libgraphqlparser/python/example.py +31 -0
- data/ext/libgraphqlparser/syntaxdefs.h +19 -0
- data/ext/libgraphqlparser/test/BuildCAPI.c +5 -0
- data/ext/libgraphqlparser/test/CMakeLists.txt +25 -0
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +28 -0
- data/ext/libgraphqlparser/test/ParserTests.cpp +352 -0
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +59 -0
- data/ext/libgraphqlparser/test/kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +78 -0
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +1 -0
- data/ext/libgraphqlparser/test/valgrind.supp +33 -0
- data/ext/version.cpp +21 -0
- data/lib/generators/graphql/controller_generator.rb +22 -0
- data/lib/generators/graphql/schema_generator.rb +22 -0
- data/lib/generators/graphql/templates/controller.erb +5 -0
- data/lib/generators/graphql/templates/schema.erb +6 -0
- data/lib/graphqlparser.so +0 -0
- data/lib/rails-graphql.rb +2 -0
- data/lib/rails/graphql.rake +1 -0
- data/lib/rails/graphql.rb +185 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +0 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +50 -0
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +39 -0
- data/lib/rails/graphql/argument.rb +220 -0
- data/lib/rails/graphql/callback.rb +124 -0
- data/lib/rails/graphql/collectors.rb +14 -0
- data/lib/rails/graphql/collectors/hash_collector.rb +83 -0
- data/lib/rails/graphql/collectors/idented_collector.rb +73 -0
- data/lib/rails/graphql/collectors/json_collector.rb +114 -0
- data/lib/rails/graphql/config.rb +61 -0
- data/lib/rails/graphql/directive.rb +203 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +59 -0
- data/lib/rails/graphql/directive/include_directive.rb +24 -0
- data/lib/rails/graphql/directive/skip_directive.rb +24 -0
- data/lib/rails/graphql/errors.rb +42 -0
- data/lib/rails/graphql/event.rb +141 -0
- data/lib/rails/graphql/field.rb +318 -0
- data/lib/rails/graphql/field/input_field.rb +92 -0
- data/lib/rails/graphql/field/mutation_field.rb +52 -0
- data/lib/rails/graphql/field/output_field.rb +96 -0
- data/lib/rails/graphql/field/proxied_field.rb +131 -0
- data/lib/rails/graphql/field/resolved_field.rb +96 -0
- data/lib/rails/graphql/field/scoped_config.rb +22 -0
- data/lib/rails/graphql/field/typed_field.rb +104 -0
- data/lib/rails/graphql/helpers.rb +40 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +39 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +152 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +141 -0
- data/lib/rails/graphql/helpers/registerable.rb +103 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +125 -0
- data/lib/rails/graphql/helpers/with_assignment.rb +113 -0
- data/lib/rails/graphql/helpers/with_callbacks.rb +55 -0
- data/lib/rails/graphql/helpers/with_directives.rb +126 -0
- data/lib/rails/graphql/helpers/with_events.rb +81 -0
- data/lib/rails/graphql/helpers/with_fields.rb +141 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +40 -0
- data/lib/rails/graphql/helpers/with_owner.rb +35 -0
- data/lib/rails/graphql/helpers/with_schema_fields.rb +230 -0
- data/lib/rails/graphql/helpers/with_validator.rb +52 -0
- data/lib/rails/graphql/introspection.rb +53 -0
- data/lib/rails/graphql/native.rb +56 -0
- data/lib/rails/graphql/native/functions.rb +38 -0
- data/lib/rails/graphql/native/location.rb +41 -0
- data/lib/rails/graphql/native/pointers.rb +23 -0
- data/lib/rails/graphql/native/visitor.rb +349 -0
- data/lib/rails/graphql/railtie.rb +85 -0
- data/lib/rails/graphql/railties/base_generator.rb +35 -0
- data/lib/rails/graphql/railties/controller.rb +101 -0
- data/lib/rails/graphql/railties/controller_runtime.rb +40 -0
- data/lib/rails/graphql/railties/log_subscriber.rb +62 -0
- data/lib/rails/graphql/request.rb +343 -0
- data/lib/rails/graphql/request/arguments.rb +93 -0
- data/lib/rails/graphql/request/component.rb +100 -0
- data/lib/rails/graphql/request/component/field.rb +225 -0
- data/lib/rails/graphql/request/component/fragment.rb +118 -0
- data/lib/rails/graphql/request/component/operation.rb +178 -0
- data/lib/rails/graphql/request/component/operation/subscription.rb +16 -0
- data/lib/rails/graphql/request/component/spread.rb +119 -0
- data/lib/rails/graphql/request/component/typename.rb +82 -0
- data/lib/rails/graphql/request/context.rb +51 -0
- data/lib/rails/graphql/request/errors.rb +54 -0
- data/lib/rails/graphql/request/event.rb +112 -0
- data/lib/rails/graphql/request/helpers/directives.rb +64 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +87 -0
- data/lib/rails/graphql/request/helpers/value_writers.rb +115 -0
- data/lib/rails/graphql/request/steps/organizable.rb +146 -0
- data/lib/rails/graphql/request/steps/prepareable.rb +33 -0
- data/lib/rails/graphql/request/steps/resolveable.rb +32 -0
- data/lib/rails/graphql/request/strategy.rb +249 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +41 -0
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +36 -0
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +28 -0
- data/lib/rails/graphql/schema.rb +272 -0
- data/lib/rails/graphql/shortcuts.rb +77 -0
- data/lib/rails/graphql/source.rb +371 -0
- data/lib/rails/graphql/source/active_record/builders.rb +154 -0
- data/lib/rails/graphql/source/active_record_source.rb +231 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +87 -0
- data/lib/rails/graphql/to_gql.rb +368 -0
- data/lib/rails/graphql/type.rb +138 -0
- data/lib/rails/graphql/type/enum.rb +206 -0
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +30 -0
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +57 -0
- data/lib/rails/graphql/type/input.rb +134 -0
- data/lib/rails/graphql/type/interface.rb +82 -0
- data/lib/rails/graphql/type/object.rb +111 -0
- data/lib/rails/graphql/type/object/directive_object.rb +34 -0
- data/lib/rails/graphql/type/object/enum_value_object.rb +25 -0
- data/lib/rails/graphql/type/object/field_object.rb +54 -0
- data/lib/rails/graphql/type/object/input_value_object.rb +49 -0
- data/lib/rails/graphql/type/object/schema_object.rb +40 -0
- data/lib/rails/graphql/type/object/type_object.rb +136 -0
- data/lib/rails/graphql/type/scalar.rb +71 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +37 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +34 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +35 -0
- data/lib/rails/graphql/type/scalar/float_scalar.rb +32 -0
- data/lib/rails/graphql/type/scalar/id_scalar.rb +39 -0
- data/lib/rails/graphql/type/scalar/int_scalar.rb +36 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +28 -0
- data/lib/rails/graphql/type/scalar/time_scalar.rb +40 -0
- data/lib/rails/graphql/type/union.rb +87 -0
- data/lib/rails/graphql/type_map.rb +347 -0
- data/lib/rails/graphql/version.rb +7 -0
- data/test/assets/introspection-db.json +0 -0
- data/test/assets/introspection-mem.txt +1 -0
- data/test/assets/introspection.gql +91 -0
- data/test/assets/luke.jpg +0 -0
- data/test/assets/mem.gql +428 -0
- data/test/assets/sqlite.gql +423 -0
- data/test/config.rb +80 -0
- data/test/graphql/request/context_test.rb +70 -0
- data/test/graphql/schema_test.rb +190 -0
- data/test/graphql/source_test.rb +237 -0
- data/test/graphql/type/enum_test.rb +203 -0
- data/test/graphql/type/input_test.rb +138 -0
- data/test/graphql/type/interface_test.rb +72 -0
- data/test/graphql/type/object_test.rb +104 -0
- data/test/graphql/type/scalar/bigint_scalar_test.rb +42 -0
- data/test/graphql/type/scalar/binary_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +40 -0
- data/test/graphql/type/scalar/date_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/date_time_scalar_test.rb +29 -0
- data/test/graphql/type/scalar/decimal_scalar_test.rb +28 -0
- data/test/graphql/type/scalar/float_scalar_test.rb +22 -0
- data/test/graphql/type/scalar/id_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/int_scalar_test.rb +26 -0
- data/test/graphql/type/scalar/string_scalar_test.rb +17 -0
- data/test/graphql/type/scalar/time_scalar_test.rb +36 -0
- data/test/graphql/type/scalar_test.rb +45 -0
- data/test/graphql/type/union_test.rb +82 -0
- data/test/graphql/type_map_test.rb +362 -0
- data/test/graphql/type_test.rb +68 -0
- data/test/graphql_test.rb +55 -0
- data/test/integration/config.rb +56 -0
- data/test/integration/memory/star_wars_introspection_test.rb +144 -0
- data/test/integration/memory/star_wars_query_test.rb +184 -0
- data/test/integration/memory/star_wars_validation_test.rb +99 -0
- data/test/integration/schemas/memory.rb +232 -0
- data/test/integration/schemas/sqlite.rb +82 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +15 -0
- data/test/integration/sqlite/star_wars_mutation_test.rb +82 -0
- data/test/integration/sqlite/star_wars_query_test.rb +71 -0
- data/test/test_ext.rb +48 -0
- 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
|