graphql 0.17.2 → 0.18.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 +1 -0
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/base_type.rb +25 -1
- data/lib/graphql/define.rb +2 -0
- data/lib/graphql/define/assign_connection.rb +11 -0
- data/lib/graphql/define/assign_global_id_field.rb +11 -0
- data/lib/graphql/define/assign_object_field.rb +21 -20
- data/lib/graphql/define/defined_object_proxy.rb +2 -2
- data/lib/graphql/define/instance_definable.rb +13 -3
- data/lib/graphql/field.rb +1 -1
- data/lib/graphql/language/generation.rb +57 -6
- data/lib/graphql/language/lexer.rb +434 -212
- data/lib/graphql/language/lexer.rl +18 -0
- data/lib/graphql/language/nodes.rb +75 -0
- data/lib/graphql/language/parser.rb +853 -341
- data/lib/graphql/language/parser.y +114 -17
- data/lib/graphql/query.rb +15 -1
- data/lib/graphql/relay.rb +13 -0
- data/lib/graphql/relay/array_connection.rb +80 -0
- data/lib/graphql/relay/base_connection.rb +138 -0
- data/lib/graphql/relay/connection_field.rb +54 -0
- data/lib/graphql/relay/connection_type.rb +25 -0
- data/lib/graphql/relay/edge.rb +22 -0
- data/lib/graphql/relay/edge_type.rb +14 -0
- data/lib/graphql/relay/global_id_resolve.rb +15 -0
- data/lib/graphql/relay/global_node_identification.rb +124 -0
- data/lib/graphql/relay/mutation.rb +146 -0
- data/lib/graphql/relay/page_info.rb +13 -0
- data/lib/graphql/relay/relation_connection.rb +98 -0
- data/lib/graphql/schema.rb +3 -0
- data/lib/graphql/schema/printer.rb +12 -2
- data/lib/graphql/static_validation/message.rb +9 -5
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +7 -7
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +6 -6
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +17 -9
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +17 -6
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -2
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -5
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +12 -11
- data/lib/graphql/static_validation/type_stack.rb +33 -2
- data/lib/graphql/static_validation/validation_context.rb +5 -0
- data/lib/graphql/version.rb +1 -1
- data/readme.md +16 -4
- data/spec/graphql/analysis/analyze_query_spec.rb +31 -2
- data/spec/graphql/analysis/query_complexity_spec.rb +24 -0
- data/spec/graphql/argument_spec.rb +1 -1
- data/spec/graphql/define/instance_definable_spec.rb +9 -0
- data/spec/graphql/field_spec.rb +1 -1
- data/spec/graphql/internal_representation/rewrite_spec.rb +3 -3
- data/spec/graphql/language/generation_spec.rb +25 -4
- data/spec/graphql/language/parser_spec.rb +116 -1
- data/spec/graphql/query_spec.rb +10 -0
- data/spec/graphql/relay/array_connection_spec.rb +164 -0
- data/spec/graphql/relay/connection_type_spec.rb +37 -0
- data/spec/graphql/relay/global_node_identification_spec.rb +149 -0
- data/spec/graphql/relay/mutation_spec.rb +55 -0
- data/spec/graphql/relay/page_info_spec.rb +106 -0
- data/spec/graphql/relay/relation_connection_spec.rb +348 -0
- data/spec/graphql/schema/printer_spec.rb +8 -0
- data/spec/graphql/schema/reduce_types_spec.rb +1 -1
- data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +12 -6
- data/spec/graphql/static_validation/rules/arguments_are_defined_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/directives_are_defined_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/directives_are_in_valid_locations_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fields_are_defined_on_type_spec.rb +7 -2
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragment_spreads_are_possible_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/fragment_types_exist_spec.rb +5 -3
- data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +4 -2
- data/spec/graphql/static_validation/rules/fragments_are_on_composite_types_spec.rb +5 -2
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +10 -2
- data/spec/graphql/static_validation/rules/required_arguments_are_present_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variable_usages_are_allowed_spec.rb +8 -4
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +6 -3
- data/spec/graphql/static_validation/rules/variables_are_used_and_defined_spec.rb +6 -3
- data/spec/spec_helper.rb +7 -0
- data/spec/support/dairy_app.rb +11 -10
- data/spec/support/star_wars_data.rb +65 -58
- data/spec/support/star_wars_schema.rb +192 -54
- metadata +84 -2
@@ -22,10 +22,12 @@ describe GraphQL::StaticValidation::DirectivesAreDefined do
|
|
22
22
|
expected = [
|
23
23
|
{
|
24
24
|
"message"=>"Directive @nonsense is not defined",
|
25
|
-
"locations"=>[{"line"=>5, "column"=>16}]
|
25
|
+
"locations"=>[{"line"=>5, "column"=>16}],
|
26
|
+
"path"=>["query getCheese", "okCheese", "source"],
|
26
27
|
}, {
|
27
28
|
"message"=>"Directive @moreNonsense is not defined",
|
28
|
-
"locations"=>[{"line"=>7, "column"=>18}]
|
29
|
+
"locations"=>[{"line"=>7, "column"=>18}],
|
30
|
+
"path"=>["query getCheese", "okCheese", "... on Cheese", "flavor"],
|
29
31
|
}
|
30
32
|
]
|
31
33
|
assert_equal(expected, errors)
|
@@ -26,11 +26,13 @@ describe GraphQL::StaticValidation::DirectivesAreInValidLocations do
|
|
26
26
|
expected = [
|
27
27
|
{
|
28
28
|
"message"=> "'@skip' can't be applied to queries (allowed: fields, fragment spreads, inline fragments)",
|
29
|
-
"locations"=>[{"line"=>2, "column"=>21}]
|
29
|
+
"locations"=>[{"line"=>2, "column"=>21}],
|
30
|
+
"path"=>["query getCheese"],
|
30
31
|
},
|
31
32
|
{
|
32
33
|
"message"=>"'@skip' can't be applied to fragment definitions (allowed: fields, fragment spreads, inline fragments)",
|
33
|
-
"locations"=>[{"line"=>12, "column"=>33}]
|
34
|
+
"locations"=>[{"line"=>12, "column"=>33}],
|
35
|
+
"path"=>["fragment whatever"],
|
34
36
|
},
|
35
37
|
]
|
36
38
|
assert_equal(expected, errors)
|
@@ -31,7 +31,11 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
|
|
31
31
|
|
32
32
|
it "finds invalid fields" do
|
33
33
|
expected_errors = [
|
34
|
-
{
|
34
|
+
{
|
35
|
+
"message"=>"Field 'amountThatILikeIt' doesn't exist on type 'Edible'",
|
36
|
+
"locations"=>[{"line"=>1, "column"=>18}],
|
37
|
+
"path"=>["query getStuff", "favoriteEdible", "amountThatILikeIt"],
|
38
|
+
}
|
35
39
|
]
|
36
40
|
assert_equal(expected_errors, errors)
|
37
41
|
end
|
@@ -51,7 +55,8 @@ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
|
|
51
55
|
"message"=>"Selections can't be made directly on unions (see selections on DairyProduct)",
|
52
56
|
"locations"=>[
|
53
57
|
{"line"=>3, "column"=>7}
|
54
|
-
]
|
58
|
+
],
|
59
|
+
"path"=>["fragment dbFields", "source"],
|
55
60
|
}
|
56
61
|
]
|
57
62
|
assert_equal(expected_errors, errors)
|
@@ -18,13 +18,15 @@ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
|
|
18
18
|
|
19
19
|
illegal_selection_error = {
|
20
20
|
"message"=>"Selections can't be made on scalars (field 'id' returns Int but has selections [something, someFields])",
|
21
|
-
"locations"=>[{"line"=>5, "column"=>47}]
|
21
|
+
"locations"=>[{"line"=>5, "column"=>47}],
|
22
|
+
"path"=>["query getCheese", "illegalSelectionCheese", "id"],
|
22
23
|
}
|
23
24
|
assert_includes(errors, illegal_selection_error, "finds illegal selections on scalarss")
|
24
25
|
|
25
26
|
selection_required_error = {
|
26
27
|
"message"=>"Objects must have selections (field 'cheese' returns Cheese but has no selections)",
|
27
|
-
"locations"=>[{"line"=>4, "column"=>7}]
|
28
|
+
"locations"=>[{"line"=>4, "column"=>7}],
|
29
|
+
"path"=>["query getCheese", "missingFieldsCheese"],
|
28
30
|
}
|
29
31
|
assert_includes(errors, selection_required_error, "finds objects without selections")
|
30
32
|
end
|
@@ -31,15 +31,18 @@ describe GraphQL::StaticValidation::FragmentSpreadsArePossible do
|
|
31
31
|
expected = [
|
32
32
|
{
|
33
33
|
"message"=>"Fragment on Milk can't be spread inside Cheese",
|
34
|
-
"locations"=>[{"line"=>6, "column"=>9}]
|
34
|
+
"locations"=>[{"line"=>6, "column"=>9}],
|
35
|
+
"path"=>["query getCheese", "cheese", "... on Milk"],
|
35
36
|
},
|
36
37
|
{
|
37
38
|
"message"=>"Fragment milkFields on Milk can't be spread inside Cheese",
|
38
|
-
"locations"=>[{"line"=>4, "column"=>9}]
|
39
|
+
"locations"=>[{"line"=>4, "column"=>9}],
|
40
|
+
"path"=>["query getCheese", "cheese", "... milkFields"],
|
39
41
|
},
|
40
42
|
{
|
41
43
|
"message"=>"Fragment milkFields on Milk can't be spread inside Cheese",
|
42
|
-
"locations"=>[{"line"=>18, "column"=>7}]
|
44
|
+
"locations"=>[{"line"=>18, "column"=>7}],
|
45
|
+
"path"=>["fragment cheeseFields", "... milkFields"],
|
43
46
|
}
|
44
47
|
]
|
45
48
|
assert_equal(expected, errors)
|
@@ -3,7 +3,7 @@ require "spec_helper"
|
|
3
3
|
describe GraphQL::StaticValidation::FragmentTypesExist do
|
4
4
|
let(:query_string) {"
|
5
5
|
query getCheese {
|
6
|
-
|
6
|
+
cheese(id: 1) {
|
7
7
|
... on Cheese { source }
|
8
8
|
... on Nothing { whatever }
|
9
9
|
... somethingFields
|
@@ -27,12 +27,14 @@ describe GraphQL::StaticValidation::FragmentTypesExist do
|
|
27
27
|
assert_equal(2, errors.length)
|
28
28
|
inline_fragment_error = {
|
29
29
|
"message"=>"No such type Something, so it can't be a fragment condition",
|
30
|
-
"locations"=>[{"line"=>11, "column"=>5}]
|
30
|
+
"locations"=>[{"line"=>11, "column"=>5}],
|
31
|
+
"path"=>["fragment somethingFields"],
|
31
32
|
}
|
32
33
|
assert_includes(errors, inline_fragment_error, "on inline fragments")
|
33
34
|
fragment_def_error = {
|
34
35
|
"message"=>"No such type Nothing, so it can't be a fragment condition",
|
35
|
-
"locations"=>[{"line"=>5, "column"=>9}]
|
36
|
+
"locations"=>[{"line"=>5, "column"=>9}],
|
37
|
+
"path"=>["query getCheese", "cheese", "... on Nothing"],
|
36
38
|
}
|
37
39
|
assert_includes(errors, fragment_def_error, "on fragment definitions")
|
38
40
|
end
|
@@ -32,11 +32,13 @@ describe GraphQL::StaticValidation::FragmentsAreFinite do
|
|
32
32
|
expected = [
|
33
33
|
{
|
34
34
|
"message"=>"Fragment sourceField contains an infinite loop",
|
35
|
-
"locations"=>[{"line"=>10, "column"=>5}]
|
35
|
+
"locations"=>[{"line"=>10, "column"=>5}],
|
36
|
+
"path"=>["fragment sourceField"],
|
36
37
|
},
|
37
38
|
{
|
38
39
|
"message"=>"Fragment flavorField contains an infinite loop",
|
39
|
-
"locations"=>[{"line"=>15, "column"=>5}]
|
40
|
+
"locations"=>[{"line"=>15, "column"=>5}],
|
41
|
+
"path"=>["fragment flavorField"],
|
40
42
|
}
|
41
43
|
]
|
42
44
|
assert_equal(expected, errors)
|
@@ -33,15 +33,18 @@ describe GraphQL::StaticValidation::FragmentsAreOnCompositeTypes do
|
|
33
33
|
expected = [
|
34
34
|
{
|
35
35
|
"message"=>"Invalid fragment on type Boolean (must be Union, Interface or Object)",
|
36
|
-
"locations"=>[{"line"=>6, "column"=>11}]
|
36
|
+
"locations"=>[{"line"=>6, "column"=>11}],
|
37
|
+
"path"=>["query getCheese", "cheese", "... on Cheese", "... on Boolean"],
|
37
38
|
},
|
38
39
|
{
|
39
40
|
"message"=>"Invalid fragment on type DairyProductInput (must be Union, Interface or Object)",
|
40
41
|
"locations"=>[{"line"=>14, "column"=>9}],
|
42
|
+
"path"=>["query getCheese", "cheese", "... on DairyProductInput"],
|
41
43
|
},
|
42
44
|
{
|
43
45
|
"message"=>"Invalid fragment on type Int (must be Union, Interface or Object)",
|
44
|
-
"locations"=>[{"line"=>20, "column"=>5}]
|
46
|
+
"locations"=>[{"line"=>20, "column"=>5}],
|
47
|
+
"path"=>["fragment intFields"],
|
45
48
|
},
|
46
49
|
]
|
47
50
|
assert_equal(expected, errors)
|
@@ -16,10 +16,18 @@ describe GraphQL::StaticValidation::FragmentsAreUsed do
|
|
16
16
|
let(:errors) { validator.validate(query)[:errors] }
|
17
17
|
|
18
18
|
it "adds errors for unused fragment definitions" do
|
19
|
-
assert_includes(errors, {
|
19
|
+
assert_includes(errors, {
|
20
|
+
"message"=>"Fragment unusedFields was defined, but not used",
|
21
|
+
"locations"=>[{"line"=>8, "column"=>5}],
|
22
|
+
"path"=>[],
|
23
|
+
})
|
20
24
|
end
|
21
25
|
|
22
26
|
it "adds errors for undefined fragment spreads" do
|
23
|
-
assert_includes(errors, {
|
27
|
+
assert_includes(errors, {
|
28
|
+
"message"=>"Fragment undefinedFields was used, but not defined",
|
29
|
+
"locations"=>[{"line"=>5, "column"=>7}],
|
30
|
+
"path"=>["query getCheese", "... undefinedFields"]
|
31
|
+
})
|
24
32
|
end
|
25
33
|
end
|
@@ -23,19 +23,22 @@ describe GraphQL::StaticValidation::RequiredArgumentsArePresent do
|
|
23
23
|
|
24
24
|
query_root_error = {
|
25
25
|
"message"=>"Field 'cheese' is missing required arguments: id",
|
26
|
-
"locations"=>[{"line"=>4, "column"=>7}]
|
26
|
+
"locations"=>[{"line"=>4, "column"=>7}],
|
27
|
+
"path"=>["query getCheese", "cheese"],
|
27
28
|
}
|
28
29
|
assert_includes(errors, query_root_error)
|
29
30
|
|
30
31
|
fragment_error = {
|
31
32
|
"message"=>"Field 'similarCheese' is missing required arguments: source",
|
32
|
-
"locations"=>[{"line"=>8, "column"=>7}]
|
33
|
+
"locations"=>[{"line"=>8, "column"=>7}],
|
34
|
+
"path"=>["fragment cheeseFields", "similarCheese"],
|
33
35
|
}
|
34
36
|
assert_includes(errors, fragment_error)
|
35
37
|
|
36
38
|
directive_error = {
|
37
39
|
"message"=>"Directive 'skip' is missing required arguments: if",
|
38
|
-
"locations"=>[{"line"=>10, "column"=>10}]
|
40
|
+
"locations"=>[{"line"=>10, "column"=>10}],
|
41
|
+
"path"=>["fragment cheeseFields", "id"],
|
39
42
|
}
|
40
43
|
assert_includes(errors, directive_error)
|
41
44
|
end
|
data/spec/graphql/static_validation/rules/variable_default_values_are_correctly_typed_spec.rb
CHANGED
@@ -24,19 +24,23 @@ describe GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTyped do
|
|
24
24
|
expected = [
|
25
25
|
{
|
26
26
|
"message"=>"Default value for $badFloat doesn't match type Float",
|
27
|
-
"locations"=>[{"line"=>6, "column"=>7}]
|
27
|
+
"locations"=>[{"line"=>6, "column"=>7}],
|
28
|
+
"path"=>["query getCheese"],
|
28
29
|
},
|
29
30
|
{
|
30
31
|
"message"=>"Default value for $badInt doesn't match type Int",
|
31
|
-
"locations"=>[{"line"=>7, "column"=>7}]
|
32
|
+
"locations"=>[{"line"=>7, "column"=>7}],
|
33
|
+
"path"=>["query getCheese"],
|
32
34
|
},
|
33
35
|
{
|
34
36
|
"message"=>"Default value for $badInput doesn't match type DairyProductInput",
|
35
|
-
"locations"=>[{"line"=>9, "column"=>7}]
|
37
|
+
"locations"=>[{"line"=>9, "column"=>7}],
|
38
|
+
"path"=>["query getCheese"],
|
36
39
|
},
|
37
40
|
{
|
38
41
|
"message"=>"Non-null variable $nonNull can't have a default value",
|
39
|
-
"locations"=>[{"line"=>10, "column"=>7}]
|
42
|
+
"locations"=>[{"line"=>10, "column"=>7}],
|
43
|
+
"path"=>["query getCheese"],
|
40
44
|
}
|
41
45
|
]
|
42
46
|
assert_equal(expected, errors)
|
@@ -43,19 +43,23 @@ describe GraphQL::StaticValidation::VariableUsagesAreAllowed do
|
|
43
43
|
expected = [
|
44
44
|
{
|
45
45
|
"message"=>"Nullability mismatch on variable $badInt and argument id (Int / Int!)",
|
46
|
-
"locations"=>[{"line"=>14, "column"=>28}]
|
46
|
+
"locations"=>[{"line"=>14, "column"=>28}],
|
47
|
+
"path"=>["query getCheese", "badCheese", "id"],
|
47
48
|
},
|
48
49
|
{
|
49
50
|
"message"=>"Type mismatch on variable $badStr and argument id (String! / Int!)",
|
50
|
-
"locations"=>[{"line"=>15, "column"=>28}]
|
51
|
+
"locations"=>[{"line"=>15, "column"=>28}],
|
52
|
+
"path"=>["query getCheese", "badStrCheese", "id"],
|
51
53
|
},
|
52
54
|
{
|
53
55
|
"message"=>"Nullability mismatch on variable $badAnimals and argument source ([DairyAnimal]! / [DairyAnimal!]!)",
|
54
|
-
"locations"=>[{"line"=>18, "column"=>30}]
|
56
|
+
"locations"=>[{"line"=>18, "column"=>30}],
|
57
|
+
"path"=>["query getCheese", "cheese", "other", "source"],
|
55
58
|
},
|
56
59
|
{
|
57
60
|
"message"=>"List dimension mismatch on variable $deepAnimals and argument source ([[DairyAnimal!]!]! / [DairyAnimal!]!)",
|
58
|
-
"locations"=>[{"line"=>19, "column"=>32}]
|
61
|
+
"locations"=>[{"line"=>19, "column"=>32}],
|
62
|
+
"path"=>["query getCheese", "cheese", "tooDeep", "source"],
|
59
63
|
}
|
60
64
|
]
|
61
65
|
assert_equal(expected, errors)
|
@@ -21,15 +21,18 @@ describe GraphQL::StaticValidation::VariablesAreInputTypes do
|
|
21
21
|
expected = [
|
22
22
|
{
|
23
23
|
"message"=>"AnimalProduct isn't a valid input type (on $interface)",
|
24
|
-
"locations"=>[{"line"=>5, "column"=>7}]
|
24
|
+
"locations"=>[{"line"=>5, "column"=>7}],
|
25
|
+
"path"=>["query getCheese"],
|
25
26
|
},
|
26
27
|
{
|
27
28
|
"message"=>"Milk isn't a valid input type (on $object)",
|
28
|
-
"locations"=>[{"line"=>6, "column"=>7}]
|
29
|
+
"locations"=>[{"line"=>6, "column"=>7}],
|
30
|
+
"path"=>["query getCheese"],
|
29
31
|
},
|
30
32
|
{
|
31
33
|
"message"=>"Cheese isn't a valid input type (on $objects)",
|
32
|
-
"locations"=>[{"line"=>7, "column"=>7}]
|
34
|
+
"locations"=>[{"line"=>7, "column"=>7}],
|
35
|
+
"path"=>["query getCheese"],
|
33
36
|
}
|
34
37
|
]
|
35
38
|
assert_equal(expected, errors)
|
@@ -37,15 +37,18 @@ describe GraphQL::StaticValidation::VariablesAreUsedAndDefined do
|
|
37
37
|
expected = [
|
38
38
|
{
|
39
39
|
"message"=>"Variable $notUsedVar is declared by getCheese but not used",
|
40
|
-
"locations"=>[{"line"=>2, "column"=>5}]
|
40
|
+
"locations"=>[{"line"=>2, "column"=>5}],
|
41
|
+
"path"=>["query getCheese"],
|
41
42
|
},
|
42
43
|
{
|
43
44
|
"message"=>"Variable $undefinedVar is used by getCheese but not declared",
|
44
|
-
"locations"=>[{"line"=>11, "column"=>29}]
|
45
|
+
"locations"=>[{"line"=>11, "column"=>29}],
|
46
|
+
"path"=>["query getCheese", "cheese", "whatever", "undefined"],
|
45
47
|
},
|
46
48
|
{
|
47
49
|
"message"=>"Variable $undefinedFragmentVar is used by innerCheeseFields but not declared",
|
48
|
-
"locations"=>[{"line"=>24, "column"=>26}]
|
50
|
+
"locations"=>[{"line"=>24, "column"=>26}],
|
51
|
+
"path"=>["fragment innerCheeseFields", "source", "notDefined"],
|
49
52
|
},
|
50
53
|
]
|
51
54
|
assert_equal(expected, errors)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "codeclimate-test-reporter"
|
2
2
|
CodeClimate::TestReporter.start
|
3
|
+
require "sqlite3"
|
4
|
+
require "active_record"
|
5
|
+
require "sequel"
|
3
6
|
require "graphql"
|
4
7
|
require "benchmark"
|
5
8
|
require "minitest/autorun"
|
@@ -16,3 +19,7 @@ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
|
|
16
19
|
|
17
20
|
# # Load support files
|
18
21
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
22
|
+
|
23
|
+
def query(string, variables={})
|
24
|
+
GraphQL::Query.new(StarWarsSchema, string, variables: variables).result
|
25
|
+
end
|
data/spec/support/dairy_app.rb
CHANGED
@@ -38,7 +38,7 @@ CheeseType = GraphQL::ObjectType.define do
|
|
38
38
|
"Animal which produced the milk for this cheese"
|
39
39
|
|
40
40
|
# Or can define by block, `resolve ->` should override `property:`
|
41
|
-
field :similarCheese, CheeseType, "Cheeses like this one", property: :
|
41
|
+
field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
|
42
42
|
argument :source, !types[!DairyAnimalEnum]
|
43
43
|
resolve -> (t, a, c) {
|
44
44
|
# get the strings out:
|
@@ -61,11 +61,12 @@ CheeseType = GraphQL::ObjectType.define do
|
|
61
61
|
resolve -> (t, a, c) { raise("NotImplemented") }
|
62
62
|
end
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
# Keywords can be used for definition methods
|
65
|
+
field :fatContent,
|
66
|
+
property: :fat_content,
|
67
|
+
type: !GraphQL::FLOAT_TYPE,
|
68
|
+
description: "Percentage which is milkfat",
|
69
|
+
deprecation_reason: "Diet fashion has changed"
|
69
70
|
end
|
70
71
|
|
71
72
|
MilkType = GraphQL::ObjectType.define do
|
@@ -216,7 +217,7 @@ FavoriteFieldDefn = GraphQL::Field.define do
|
|
216
217
|
resolve -> (t, a, c) { MILKS[1] }
|
217
218
|
end
|
218
219
|
|
219
|
-
|
220
|
+
DairyAppQueryType = GraphQL::ObjectType.define do
|
220
221
|
name "Query"
|
221
222
|
description "Query root of the system"
|
222
223
|
field :root, types.String do
|
@@ -275,7 +276,7 @@ ReplaceValuesInputType = GraphQL::InputObjectType.define do
|
|
275
276
|
input_field :values, !types[!types.Int]
|
276
277
|
end
|
277
278
|
|
278
|
-
|
279
|
+
DairyAppMutationType = GraphQL::ObjectType.define do
|
279
280
|
name "Mutation"
|
280
281
|
description "The root for mutations in this schema"
|
281
282
|
field :pushValue, !types[!types.Int] do
|
@@ -306,8 +307,8 @@ SubscriptionType = GraphQL::ObjectType.define do
|
|
306
307
|
end
|
307
308
|
|
308
309
|
DummySchema = GraphQL::Schema.new(
|
309
|
-
query:
|
310
|
-
mutation:
|
310
|
+
query: DairyAppQueryType,
|
311
|
+
mutation: DairyAppMutationType,
|
311
312
|
subscription: SubscriptionType,
|
312
313
|
max_depth: 5,
|
313
314
|
types: [HoneyType],
|
@@ -1,71 +1,78 @@
|
|
1
|
-
luke = OpenStruct.new({
|
2
|
-
id: "1000",
|
3
|
-
name: "Luke Skywalker",
|
4
|
-
friends: ["1002", "1003", "2000", "2001"],
|
5
|
-
appearsIn: [4, 5, 6],
|
6
|
-
homePlanet: "Tatooine",
|
7
|
-
})
|
8
1
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
2
|
+
names = [
|
3
|
+
'X-Wing',
|
4
|
+
'Y-Wing',
|
5
|
+
'A-Wing',
|
6
|
+
'Millenium Falcon',
|
7
|
+
'Home One',
|
8
|
+
'TIE Fighter',
|
9
|
+
'TIE Interceptor',
|
10
|
+
'Executor',
|
11
|
+
]
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
appearsIn: [4, 5, 6],
|
22
|
-
})
|
13
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
14
|
+
`rm -f ./_test_.db`
|
15
|
+
# Set up "Bases" in ActiveRecord
|
16
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "./_test_.db")
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
18
|
+
ActiveRecord::Schema.define do
|
19
|
+
self.verbose = false
|
20
|
+
create_table :bases do |t|
|
21
|
+
t.column :name, :string
|
22
|
+
t.column :planet, :string
|
23
|
+
t.column :faction_id, :integer
|
24
|
+
end
|
25
|
+
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
name: "Wilhuff Tarkin",
|
35
|
-
friends: ["1001"],
|
36
|
-
appearsIn: [4],
|
37
|
-
})
|
27
|
+
class Base < ActiveRecord::Base
|
28
|
+
end
|
38
29
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
}
|
30
|
+
Base.create!(name: "Yavin", planet: "Yavin 4", faction_id: 1)
|
31
|
+
Base.create!(name: "Echo Base", planet: "Hoth", faction_id: 1)
|
32
|
+
Base.create!(name: "Secret Hideout", planet: "Dantooine", faction_id: 1)
|
33
|
+
Base.create!(name: "Death Star", planet: nil, faction_id: 2)
|
34
|
+
Base.create!(name: "Shield Generator", planet: "Endor", faction_id: 2)
|
35
|
+
Base.create!(name: "Headquarters", planet: "Coruscant", faction_id: 2)
|
46
36
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
37
|
+
# Also, set up Bases with Sequel
|
38
|
+
DB = Sequel.sqlite("./_test_.db")
|
39
|
+
class SequelBase < Sequel::Model(:bases)
|
40
|
+
end
|
41
|
+
|
42
|
+
rebels = OpenStruct.new({
|
43
|
+
id: '1',
|
44
|
+
name: 'Alliance to Restore the Republic',
|
45
|
+
ships: ['1', '2', '3', '4', '5'],
|
46
|
+
bases: Base.where(faction_id: 1),
|
47
|
+
basesClone: Base.where(faction_id: 1),
|
53
48
|
})
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
50
|
+
|
51
|
+
empire = OpenStruct.new({
|
52
|
+
id: '2',
|
53
|
+
name: 'Galactic Empire',
|
54
|
+
ships: ['6', '7', '8'],
|
55
|
+
bases: Base.where(faction_id: 2),
|
56
|
+
basesClone: Base.where(faction_id: 2),
|
61
57
|
})
|
62
58
|
|
63
|
-
|
64
|
-
"
|
65
|
-
|
59
|
+
STAR_WARS_DATA = {
|
60
|
+
"Faction" => {
|
61
|
+
"1" => rebels,
|
62
|
+
"2" => empire,
|
63
|
+
},
|
64
|
+
"Ship" => names.each_with_index.reduce({}) do |memo, (name, idx)|
|
65
|
+
id = (idx + 1).to_s
|
66
|
+
memo[id] = OpenStruct.new(name: name, id: id)
|
67
|
+
memo
|
68
|
+
end,
|
69
|
+
"Base" => Hash.new { |h, k| h[k] = Base.find(k) }
|
66
70
|
}
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
72
|
+
def STAR_WARS_DATA.create_ship(name, faction_id)
|
73
|
+
new_id = (self["Ship"].keys.map(&:to_i).max + 1).to_s
|
74
|
+
new_ship = OpenStruct.new(id: new_id, name: name)
|
75
|
+
self["Ship"][new_id] = new_ship
|
76
|
+
self["Faction"][faction_id]["ships"] << new_id
|
77
|
+
new_ship
|
71
78
|
end
|