graphql 1.0.0 → 1.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.
- 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
|