graphql 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +10 -0
- data/lib/graphql/base_type.rb +8 -5
- data/lib/graphql/compatibility.rb +3 -0
- data/lib/graphql/compatibility/execution_specification.rb +414 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +81 -0
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +78 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +239 -0
- data/lib/graphql/define/instance_definable.rb +53 -21
- data/lib/graphql/directive.rb +1 -1
- data/lib/graphql/enum_type.rb +31 -8
- data/lib/graphql/execution/directive_checks.rb +0 -6
- data/lib/graphql/input_object_type.rb +6 -4
- data/lib/graphql/introspection/arguments_field.rb +3 -1
- data/lib/graphql/introspection/enum_values_field.rb +10 -5
- data/lib/graphql/introspection/fields_field.rb +1 -1
- data/lib/graphql/introspection/input_fields_field.rb +2 -2
- data/lib/graphql/introspection/interfaces_field.rb +7 -1
- data/lib/graphql/introspection/possible_types_field.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +1 -1
- data/lib/graphql/introspection/type_by_name_field.rb +4 -2
- data/lib/graphql/introspection/type_type.rb +7 -6
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/language/parser.y +1 -1
- data/lib/graphql/list_type.rb +3 -4
- data/lib/graphql/non_null_type.rb +4 -8
- data/lib/graphql/object_type.rb +5 -3
- data/lib/graphql/query.rb +48 -12
- data/lib/graphql/query/context.rb +7 -1
- data/lib/graphql/query/serial_execution/execution_context.rb +8 -3
- data/lib/graphql/query/serial_execution/field_resolution.rb +8 -5
- data/lib/graphql/query/serial_execution/operation_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -21
- data/lib/graphql/query/serial_execution/value_resolution.rb +59 -99
- data/lib/graphql/query/variables.rb +7 -2
- data/lib/graphql/scalar_type.rb +1 -1
- data/lib/graphql/schema.rb +49 -18
- data/lib/graphql/schema/build_from_definition.rb +248 -0
- data/lib/graphql/schema/instrumented_field_map.rb +23 -0
- data/lib/graphql/schema/loader.rb +4 -11
- data/lib/graphql/schema/possible_types.rb +4 -2
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +4 -4
- data/lib/graphql/schema/type_map.rb +1 -1
- data/lib/graphql/schema/validation.rb +4 -0
- data/lib/graphql/schema/warden.rb +114 -0
- data/lib/graphql/static_validation/literal_validator.rb +10 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -3
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -14
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -4
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +2 -1
- data/lib/graphql/static_validation/validation_context.rb +7 -1
- data/lib/graphql/union_type.rb +6 -3
- data/lib/graphql/unresolved_type_error.rb +1 -2
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -5
- data/spec/graphql/compatibility/execution_specification_spec.rb +3 -0
- data/spec/graphql/compatibility/query_parser_specification_spec.rb +5 -0
- data/spec/graphql/compatibility/schema_parser_specification_spec.rb +5 -0
- data/spec/graphql/define/instance_definable_spec.rb +20 -0
- data/spec/graphql/directive_spec.rb +11 -0
- data/spec/graphql/enum_type_spec.rb +20 -1
- data/spec/graphql/input_object_type_spec.rb +9 -9
- data/spec/graphql/introspection/directive_type_spec.rb +4 -4
- data/spec/graphql/introspection/input_value_type_spec.rb +6 -6
- data/spec/graphql/introspection/type_type_spec.rb +28 -26
- data/spec/graphql/language/parser_spec.rb +27 -17
- data/spec/graphql/list_type_spec.rb +2 -2
- data/spec/graphql/query/variables_spec.rb +1 -0
- data/spec/graphql/scalar_type_spec.rb +3 -3
- data/spec/graphql/schema/build_from_definition_spec.rb +693 -0
- data/spec/graphql/schema/type_expression_spec.rb +3 -3
- data/spec/graphql/schema/validation_spec.rb +7 -3
- data/spec/graphql/schema/warden_spec.rb +510 -0
- data/spec/graphql/schema_spec.rb +129 -0
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -1
- data/spec/graphql/static_validation/type_stack_spec.rb +3 -3
- data/spec/spec_helper.rb +27 -1
- data/spec/support/dairy_app.rb +8 -5
- metadata +21 -3
- data/lib/graphql/language/parser_tests.rb +0 -809
@@ -0,0 +1,117 @@
|
|
1
|
+
require "graphql/compatibility/query_parser_specification/query_assertions"
|
2
|
+
require "graphql/compatibility/query_parser_specification/parse_error_specification"
|
3
|
+
|
4
|
+
module GraphQL
|
5
|
+
module Compatibility
|
6
|
+
# This asserts that a given parse function turns a string into
|
7
|
+
# the proper tree of {{GraphQL::Language::Nodes}}.
|
8
|
+
module QueryParserSpecification
|
9
|
+
# @yieldparam query_string [String] A query string to parse
|
10
|
+
# @yieldreturn [GraphQL::Language::Nodes::Document]
|
11
|
+
# @return [Class<Minitest::Test>] A test suite for this parse function
|
12
|
+
def self.build_suite(&block)
|
13
|
+
Class.new(Minitest::Test) do
|
14
|
+
include QueryAssertions
|
15
|
+
include ParseErrorSpecification
|
16
|
+
|
17
|
+
@@parse_fn = block
|
18
|
+
|
19
|
+
def parse(query_string)
|
20
|
+
@@parse_fn.call(query_string)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_it_parses_queries
|
24
|
+
document = parse(QUERY_STRING)
|
25
|
+
query = document.definitions.first
|
26
|
+
assert_valid_query(query)
|
27
|
+
assert_valid_fragment(document.definitions.last)
|
28
|
+
assert_valid_variable(query.variables.first)
|
29
|
+
field = query.selections.first
|
30
|
+
assert_valid_field(field)
|
31
|
+
assert_valid_variable_argument(field.arguments.first)
|
32
|
+
assert_valid_literal_argument(field.arguments.last)
|
33
|
+
assert_valid_directive(field.directives.first)
|
34
|
+
fragment_spread = query.selections[1].selections.last
|
35
|
+
assert_valid_fragment_spread(fragment_spread)
|
36
|
+
assert_valid_typed_inline_fragment(query.selections[2])
|
37
|
+
assert_valid_typeless_inline_fragment(query.selections[3])
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_it_parses_unnamed_queries
|
41
|
+
document = parse("{ name, age, height }")
|
42
|
+
operation = document.definitions.first
|
43
|
+
assert_equal 1, document.definitions.length
|
44
|
+
assert_equal "query", operation.operation_type
|
45
|
+
assert_equal nil, operation.name
|
46
|
+
assert_equal 3, operation.selections.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_it_parses_the_introspection_query
|
50
|
+
parse(GraphQL::Introspection::INTROSPECTION_QUERY)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_it_parses_inputs
|
54
|
+
query_string = %|
|
55
|
+
{
|
56
|
+
field(
|
57
|
+
int: 3,
|
58
|
+
float: 4.7e-24,
|
59
|
+
bool: false,
|
60
|
+
string: "☀︎🏆\\n \\" \u00b6 /",
|
61
|
+
enum: ENUM_NAME,
|
62
|
+
array: [7, 8, 9]
|
63
|
+
object: {a: [1,2,3], b: {c: "4"}}
|
64
|
+
unicode_bom: "\xef\xbb\xbfquery"
|
65
|
+
keywordEnum: on
|
66
|
+
)
|
67
|
+
}
|
68
|
+
|
|
69
|
+
document = parse(query_string)
|
70
|
+
inputs = document.definitions.first.selections.first.arguments
|
71
|
+
assert_equal 3, inputs[0].value, "Integers"
|
72
|
+
assert_equal 0.47e-23, inputs[1].value, "Floats"
|
73
|
+
assert_equal false, inputs[2].value, "Booleans"
|
74
|
+
assert_equal %|☀︎🏆\n " ¶ /|, inputs[3].value, "Strings"
|
75
|
+
assert_instance_of GraphQL::Language::Nodes::Enum, inputs[4].value
|
76
|
+
assert_equal "ENUM_NAME", inputs[4].value.name, "Enums"
|
77
|
+
assert_equal [7,8,9], inputs[5].value, "Lists"
|
78
|
+
|
79
|
+
obj = inputs[6].value
|
80
|
+
assert_equal "a", obj.arguments[0].name
|
81
|
+
assert_equal [1,2,3], obj.arguments[0].value
|
82
|
+
assert_equal "b", obj.arguments[1].name
|
83
|
+
assert_equal "c", obj.arguments[1].value.arguments[0].name
|
84
|
+
assert_equal "4", obj.arguments[1].value.arguments[0].value
|
85
|
+
|
86
|
+
assert_equal %|\xef\xbb\xbfquery|, inputs[7].value, "Unicode BOM"
|
87
|
+
assert_equal "on", inputs[8].value.name, "Enum value 'on'"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
QUERY_STRING = %|
|
93
|
+
query getStuff($someVar: Int = 1, $anotherVar: [String!] ) @skip(if: false) {
|
94
|
+
myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
|
95
|
+
|
96
|
+
anotherField(someArg: [1,2,3]) {
|
97
|
+
nestedField
|
98
|
+
... moreNestedFields @skip(if: true)
|
99
|
+
}
|
100
|
+
|
101
|
+
... on OtherType @include(unless: false){
|
102
|
+
field(arg: [{key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER}])
|
103
|
+
anotherField
|
104
|
+
}
|
105
|
+
|
106
|
+
... {
|
107
|
+
id
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
fragment moreNestedFields on NestedType @or(something: "ok") {
|
112
|
+
anotherNestedField @enum(directive: true)
|
113
|
+
}
|
114
|
+
|
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Compatibility
|
3
|
+
module QueryParserSpecification
|
4
|
+
module ParseErrorSpecification
|
5
|
+
def assert_raises_parse_error(query_string)
|
6
|
+
assert_raises(GraphQL::ParseError) {
|
7
|
+
parse(query_string)
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_it_includes_line_and_column
|
12
|
+
err = assert_raises_parse_error("
|
13
|
+
query getCoupons {
|
14
|
+
allCoupons: {data{id}}
|
15
|
+
}
|
16
|
+
")
|
17
|
+
|
18
|
+
assert_includes(err.message, '{')
|
19
|
+
assert_equal(3, err.line)
|
20
|
+
assert_equal(27, err.col)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_it_rejects_unterminated_strings
|
24
|
+
assert_raises_parse_error('{ " }')
|
25
|
+
assert_raises_parse_error(%|{ "\n" }|)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_it_rejects_unexpected_ends
|
29
|
+
assert_raises_parse_error("query { stuff { thing }")
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_rejects_character(char)
|
33
|
+
err = assert_raises_parse_error("{ field#{char} }")
|
34
|
+
expected_char = char.inspect.gsub('"', '').downcase
|
35
|
+
msg_downcase = err.message.downcase
|
36
|
+
# Case-insensitive for UTF-8 printing
|
37
|
+
assert_includes(msg_downcase, expected_char, "The message includes the invalid character")
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_it_rejects_invalid_characters
|
41
|
+
assert_rejects_character(";")
|
42
|
+
assert_rejects_character("\a")
|
43
|
+
assert_rejects_character("\xef")
|
44
|
+
assert_rejects_character("\v")
|
45
|
+
assert_rejects_character("\f")
|
46
|
+
assert_rejects_character("\xa0")
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_it_rejects_bad_unicode
|
50
|
+
assert_raises_parse_error(%|{ field(arg:"\\x") }|)
|
51
|
+
assert_raises_parse_error(%|{ field(arg:"\\u1") }|)
|
52
|
+
assert_raises_parse_error(%|{ field(arg:"\\u0XX1") }|)
|
53
|
+
assert_raises_parse_error(%|{ field(arg:"\\uXXXX") }|)
|
54
|
+
assert_raises_parse_error(%|{ field(arg:"\\uFXXX") }|)
|
55
|
+
assert_raises_parse_error(%|{ field(arg:"\\uXXXF") }|)
|
56
|
+
end
|
57
|
+
|
58
|
+
def assert_empty_document(query_string)
|
59
|
+
doc = parse(query_string)
|
60
|
+
assert_equal 0, doc.definitions.length
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_it_parses_blank_queries
|
64
|
+
assert_empty_document("")
|
65
|
+
assert_empty_document(" ")
|
66
|
+
assert_empty_document("\t \t")
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_it_restricts_on
|
70
|
+
assert_raises_parse_error("{ ...on }")
|
71
|
+
assert_raises_parse_error("fragment on on Type { field }")
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_it_rejects_null
|
75
|
+
err = assert_raises_parse_error("{ field(input: null) }")
|
76
|
+
assert_includes(err.message, "null")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Compatibility
|
3
|
+
module QueryParserSpecification
|
4
|
+
module QueryAssertions
|
5
|
+
def assert_valid_query(query)
|
6
|
+
assert query.is_a?(GraphQL::Language::Nodes::OperationDefinition)
|
7
|
+
assert_equal "getStuff", query.name
|
8
|
+
assert_equal "query", query.operation_type
|
9
|
+
assert_equal 2, query.variables.length
|
10
|
+
assert_equal 4, query.selections.length
|
11
|
+
assert_equal 1, query.directives.length
|
12
|
+
assert_equal [2, 13], [query.line, query.col]
|
13
|
+
end
|
14
|
+
|
15
|
+
def assert_valid_fragment(fragment_def)
|
16
|
+
assert fragment_def.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
|
17
|
+
assert_equal "moreNestedFields", fragment_def.name
|
18
|
+
assert_equal 1, fragment_def.selections.length
|
19
|
+
assert_equal "NestedType", fragment_def.type.name
|
20
|
+
assert_equal 1, fragment_def.directives.length
|
21
|
+
assert_equal [20, 13], fragment_def.position
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_valid_variable(variable)
|
25
|
+
assert_equal "someVar", variable.name
|
26
|
+
assert_equal "Int", variable.type.name
|
27
|
+
assert_equal 1, variable.default_value
|
28
|
+
assert_equal [2, 28], variable.position
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_valid_field(field)
|
32
|
+
assert_equal "someField", field.name
|
33
|
+
assert_equal "myField", field.alias
|
34
|
+
assert_equal 2, field.directives.length
|
35
|
+
assert_equal 2, field.arguments.length
|
36
|
+
assert_equal 0, field.selections.length
|
37
|
+
assert_equal [3, 15], field.position
|
38
|
+
end
|
39
|
+
|
40
|
+
def assert_valid_literal_argument(argument)
|
41
|
+
assert_equal "ok", argument.name
|
42
|
+
assert_equal 1.4, argument.value
|
43
|
+
end
|
44
|
+
|
45
|
+
def assert_valid_variable_argument(argument)
|
46
|
+
assert_equal "someArg", argument.name
|
47
|
+
assert_equal "someVar", argument.value.name
|
48
|
+
end
|
49
|
+
|
50
|
+
def assert_valid_fragment_spread(fragment_spread)
|
51
|
+
assert_equal "moreNestedFields", fragment_spread.name
|
52
|
+
assert_equal 1, fragment_spread.directives.length
|
53
|
+
assert_equal [7, 17], fragment_spread.position
|
54
|
+
end
|
55
|
+
|
56
|
+
def assert_valid_directive(directive)
|
57
|
+
assert_equal "skip", directive.name
|
58
|
+
assert_equal "if", directive.arguments.first.name
|
59
|
+
assert_equal 1, directive.arguments.length
|
60
|
+
assert_equal [3, 62], directive.position
|
61
|
+
end
|
62
|
+
|
63
|
+
def assert_valid_typed_inline_fragment(inline_fragment)
|
64
|
+
assert_equal "OtherType", inline_fragment.type.name
|
65
|
+
assert_equal 2, inline_fragment.selections.length
|
66
|
+
assert_equal 1, inline_fragment.directives.length
|
67
|
+
assert_equal [10, 15], inline_fragment.position
|
68
|
+
end
|
69
|
+
|
70
|
+
def assert_valid_typeless_inline_fragment(inline_fragment)
|
71
|
+
assert_equal nil, inline_fragment.type
|
72
|
+
assert_equal 1, inline_fragment.selections.length
|
73
|
+
assert_equal 0, inline_fragment.directives.length
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Compatibility
|
3
|
+
# This asserts that a given parse function turns a string into
|
4
|
+
# the proper tree of {{GraphQL::Language::Nodes}}.
|
5
|
+
module SchemaParserSpecification
|
6
|
+
# @yieldparam query_string [String] A query string to parse
|
7
|
+
# @yieldreturn [GraphQL::Language::Nodes::Document]
|
8
|
+
# @return [Class<Minitest::Test>] A test suite for this parse function
|
9
|
+
def self.build_suite(&block)
|
10
|
+
Class.new(Minitest::Test) do
|
11
|
+
@@parse_fn = block
|
12
|
+
|
13
|
+
def parse(query_string)
|
14
|
+
@@parse_fn.call(query_string)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_it_parses_object_types
|
18
|
+
document = parse('
|
19
|
+
# This is what
|
20
|
+
# somebody said about something
|
21
|
+
type Comment implements Node @deprecated(reason: "No longer supported") {
|
22
|
+
id: ID!
|
23
|
+
}
|
24
|
+
')
|
25
|
+
|
26
|
+
type = document.definitions.first
|
27
|
+
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
|
28
|
+
assert_equal 'Comment', type.name
|
29
|
+
assert_equal "This is what\nsomebody said about something", type.description
|
30
|
+
assert_equal ['Node'], type.interfaces.map(&:name)
|
31
|
+
assert_equal ['id'], type.fields.map(&:name)
|
32
|
+
assert_equal [], type.fields[0].arguments
|
33
|
+
assert_equal 'ID', type.fields[0].type.of_type.name
|
34
|
+
assert_equal 1, type.directives.length
|
35
|
+
|
36
|
+
deprecated_directive = type.directives[0]
|
37
|
+
assert_equal 'deprecated', deprecated_directive.name
|
38
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
39
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_it_parses_scalars
|
43
|
+
document = parse('scalar DateTime')
|
44
|
+
|
45
|
+
type = document.definitions.first
|
46
|
+
assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
|
47
|
+
assert_equal 'DateTime', type.name
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_it_parses_enum_types
|
51
|
+
document = parse('
|
52
|
+
enum DogCommand {
|
53
|
+
# Good dog
|
54
|
+
SIT
|
55
|
+
DOWN @deprecated(reason: "No longer supported")
|
56
|
+
HEEL
|
57
|
+
}
|
58
|
+
')
|
59
|
+
|
60
|
+
type = document.definitions.first
|
61
|
+
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
|
62
|
+
assert_equal 'DogCommand', type.name
|
63
|
+
assert_equal 3, type.values.length
|
64
|
+
|
65
|
+
assert_equal 'SIT', type.values[0].name
|
66
|
+
assert_equal [], type.values[0].directives
|
67
|
+
assert_equal "Good dog", type.values[0].description
|
68
|
+
|
69
|
+
assert_equal 'DOWN', type.values[1].name
|
70
|
+
assert_equal 1, type.values[1].directives.length
|
71
|
+
deprecated_directive = type.values[1].directives[0]
|
72
|
+
assert_equal 'deprecated', deprecated_directive.name
|
73
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
74
|
+
assert_equal 'No longer supported', deprecated_directive.arguments[0].value
|
75
|
+
|
76
|
+
assert_equal 'HEEL', type.values[2].name
|
77
|
+
assert_equal [], type.values[2].directives
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_it_parses_input_types
|
81
|
+
document = parse('
|
82
|
+
input EmptyMutationInput {
|
83
|
+
clientMutationId: String
|
84
|
+
}
|
85
|
+
')
|
86
|
+
|
87
|
+
type = document.definitions.first
|
88
|
+
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
|
89
|
+
assert_equal 'EmptyMutationInput', type.name
|
90
|
+
assert_equal ['clientMutationId'], type.fields.map(&:name)
|
91
|
+
assert_equal 'String', type.fields[0].type.name
|
92
|
+
assert_equal nil, type.fields[0].default_value
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_it_parses_directives
|
96
|
+
document = parse('
|
97
|
+
directive @include(if: Boolean!)
|
98
|
+
on FIELD
|
99
|
+
| FRAGMENT_SPREAD
|
100
|
+
| INLINE_FRAGMENT
|
101
|
+
')
|
102
|
+
|
103
|
+
type = document.definitions.first
|
104
|
+
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, type.class
|
105
|
+
assert_equal 'include', type.name
|
106
|
+
|
107
|
+
assert_equal 1, type.arguments.length
|
108
|
+
assert_equal 'if', type.arguments[0].name
|
109
|
+
assert_equal 'Boolean', type.arguments[0].type.of_type.name
|
110
|
+
|
111
|
+
assert_equal ['FIELD', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT'], type.locations
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_it_parses_field_arguments
|
115
|
+
document = parse('
|
116
|
+
type Mutation {
|
117
|
+
post(
|
118
|
+
id: ID! @deprecated(reason: "Not used"),
|
119
|
+
# This is what goes in the post
|
120
|
+
data: String
|
121
|
+
): Post
|
122
|
+
}
|
123
|
+
')
|
124
|
+
|
125
|
+
field = document.definitions.first.fields.first
|
126
|
+
assert_equal ['id', 'data'], field.arguments.map(&:name)
|
127
|
+
id_arg = field.arguments[0]
|
128
|
+
|
129
|
+
deprecated_directive = id_arg.directives[0]
|
130
|
+
assert_equal 'deprecated', deprecated_directive.name
|
131
|
+
assert_equal 'reason', deprecated_directive.arguments[0].name
|
132
|
+
assert_equal 'Not used', deprecated_directive.arguments[0].value
|
133
|
+
|
134
|
+
data_arg = field.arguments[1]
|
135
|
+
assert_equal "data", data_arg.name
|
136
|
+
assert_equal "This is what goes in the post", data_arg.description
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_it_parses_schema_definition
|
140
|
+
document = parse('
|
141
|
+
schema {
|
142
|
+
query: QueryRoot
|
143
|
+
mutation: MutationRoot
|
144
|
+
subscription: SubscriptionRoot
|
145
|
+
}
|
146
|
+
')
|
147
|
+
|
148
|
+
schema = document.definitions.first
|
149
|
+
assert_equal 'QueryRoot', schema.query
|
150
|
+
assert_equal 'MutationRoot', schema.mutation
|
151
|
+
assert_equal 'SubscriptionRoot', schema.subscription
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_it_parses_whole_definition_with_descriptions
|
155
|
+
document = parse(SCHEMA_DEFINITION_STRING)
|
156
|
+
|
157
|
+
assert_equal 6, document.definitions.size
|
158
|
+
|
159
|
+
schema_definition = document.definitions.shift
|
160
|
+
assert_equal GraphQL::Language::Nodes::SchemaDefinition, schema_definition.class
|
161
|
+
|
162
|
+
directive_definition = document.definitions.shift
|
163
|
+
assert_equal GraphQL::Language::Nodes::DirectiveDefinition, directive_definition.class
|
164
|
+
assert_equal 'This is a directive', directive_definition.description
|
165
|
+
|
166
|
+
enum_type_definition = document.definitions.shift
|
167
|
+
assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, enum_type_definition.class
|
168
|
+
assert_equal "Multiline comment\n\nWith an enum", enum_type_definition.description
|
169
|
+
|
170
|
+
assert_nil enum_type_definition.values[0].description
|
171
|
+
assert_equal 'Not a creative color', enum_type_definition.values[1].description
|
172
|
+
|
173
|
+
object_type_definition = document.definitions.shift
|
174
|
+
assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, object_type_definition.class
|
175
|
+
assert_equal 'Comment without preceding space', object_type_definition.description
|
176
|
+
assert_equal 'And a field to boot', object_type_definition.fields[0].description
|
177
|
+
|
178
|
+
input_object_type_definition = document.definitions.shift
|
179
|
+
assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, input_object_type_definition.class
|
180
|
+
assert_equal 'Comment for input object types', input_object_type_definition.description
|
181
|
+
assert_equal 'Color of the car', input_object_type_definition.fields[0].description
|
182
|
+
|
183
|
+
interface_type_definition = document.definitions.shift
|
184
|
+
assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, interface_type_definition.class
|
185
|
+
assert_equal 'Comment for interface definitions', interface_type_definition.description
|
186
|
+
assert_equal 'Amount of wheels', interface_type_definition.fields[0].description
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
SCHEMA_DEFINITION_STRING = %|
|
192
|
+
# Schema at beginning of file
|
193
|
+
|
194
|
+
schema {
|
195
|
+
query: Hello
|
196
|
+
}
|
197
|
+
|
198
|
+
# Comment between two definitions are omitted
|
199
|
+
|
200
|
+
# This is a directive
|
201
|
+
directive @foo(
|
202
|
+
# It has an argument
|
203
|
+
arg: Int
|
204
|
+
) on FIELD
|
205
|
+
|
206
|
+
# Multiline comment
|
207
|
+
#
|
208
|
+
# With an enum
|
209
|
+
enum Color {
|
210
|
+
RED
|
211
|
+
|
212
|
+
# Not a creative color
|
213
|
+
GREEN
|
214
|
+
BLUE
|
215
|
+
}
|
216
|
+
|
217
|
+
#Comment without preceding space
|
218
|
+
type Hello {
|
219
|
+
# And a field to boot
|
220
|
+
str: String
|
221
|
+
}
|
222
|
+
|
223
|
+
# Comment for input object types
|
224
|
+
input Car {
|
225
|
+
# Color of the car
|
226
|
+
color: String!
|
227
|
+
}
|
228
|
+
|
229
|
+
# Comment for interface definitions
|
230
|
+
interface Vehicle {
|
231
|
+
# Amount of wheels
|
232
|
+
wheels: Int!
|
233
|
+
}
|
234
|
+
|
235
|
+
# Comment at the end of schema
|
236
|
+
|
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|