graphql 0.0.4 → 0.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/graph_ql/directive.rb +36 -0
- data/lib/graph_ql/directives/directive_chain.rb +33 -0
- data/lib/graph_ql/directives/include_directive.rb +15 -0
- data/lib/graph_ql/directives/skip_directive.rb +15 -0
- data/lib/graph_ql/enum.rb +34 -0
- data/lib/graph_ql/fields/abstract_field.rb +37 -0
- data/lib/graph_ql/fields/access_field.rb +24 -0
- data/lib/graph_ql/fields/field.rb +34 -0
- data/lib/graph_ql/interface.rb +14 -0
- data/lib/graph_ql/introspection/arguments_field.rb +5 -0
- data/lib/graph_ql/introspection/directive_type.rb +12 -0
- data/lib/graph_ql/introspection/enum_value_type.rb +10 -0
- data/lib/graph_ql/introspection/enum_values_field.rb +15 -0
- data/lib/graph_ql/introspection/field_type.rb +11 -0
- data/lib/graph_ql/introspection/fields_field.rb +14 -0
- data/lib/graph_ql/introspection/input_fields_field.rb +12 -0
- data/lib/graph_ql/introspection/input_value_type.rb +10 -0
- data/lib/graph_ql/introspection/of_type_field.rb +12 -0
- data/lib/graph_ql/introspection/possible_types_field.rb +12 -0
- data/lib/graph_ql/introspection/schema_type.rb +32 -0
- data/lib/graph_ql/introspection/type_kind_enum.rb +7 -0
- data/lib/graph_ql/introspection/type_type.rb +22 -0
- data/lib/graph_ql/parser/nodes.rb +72 -0
- data/lib/graph_ql/parser/parser.rb +108 -0
- data/lib/graph_ql/parser/transform.rb +86 -0
- data/lib/graph_ql/parser/visitor.rb +47 -0
- data/lib/graph_ql/query.rb +50 -0
- data/lib/graph_ql/query/arguments.rb +25 -0
- data/lib/graph_ql/query/field_resolution_strategy.rb +83 -0
- data/lib/graph_ql/query/fragment_spread_resolution_strategy.rb +16 -0
- data/lib/graph_ql/query/inline_fragment_resolution_strategy.rb +14 -0
- data/lib/graph_ql/query/operation_resolver.rb +28 -0
- data/lib/graph_ql/query/selection_resolver.rb +20 -0
- data/lib/graph_ql/query/type_resolver.rb +19 -0
- data/lib/graph_ql/repl.rb +27 -0
- data/lib/graph_ql/schema.rb +30 -0
- data/lib/graph_ql/schema/type_reducer.rb +44 -0
- data/lib/graph_ql/type_kinds.rb +15 -0
- data/lib/graph_ql/types/abstract_type.rb +14 -0
- data/lib/graph_ql/types/boolean_type.rb +6 -0
- data/lib/graph_ql/types/float_type.rb +6 -0
- data/lib/graph_ql/types/input_object_type.rb +17 -0
- data/lib/graph_ql/types/input_value.rb +10 -0
- data/lib/graph_ql/types/int_type.rb +6 -0
- data/lib/graph_ql/types/list_type.rb +10 -0
- data/lib/graph_ql/types/non_null_type.rb +18 -0
- data/lib/graph_ql/types/non_null_with_bang.rb +5 -0
- data/lib/graph_ql/types/object_type.rb +62 -0
- data/lib/graph_ql/types/scalar_type.rb +5 -0
- data/lib/graph_ql/types/string_type.rb +6 -0
- data/lib/graph_ql/types/type_definer.rb +16 -0
- data/lib/graph_ql/union.rb +35 -0
- data/lib/graph_ql/validations/fields_are_defined_on_type.rb +44 -0
- data/lib/graph_ql/validations/fields_will_merge.rb +80 -0
- data/lib/graph_ql/validations/fragments_are_used.rb +24 -0
- data/lib/graph_ql/validator.rb +29 -0
- data/lib/graph_ql/version.rb +3 -0
- data/lib/graphql.rb +92 -99
- data/readme.md +17 -177
- data/spec/graph_ql/directive_spec.rb +81 -0
- data/spec/graph_ql/enum_spec.rb +5 -0
- data/spec/graph_ql/fields/field_spec.rb +10 -0
- data/spec/graph_ql/interface_spec.rb +13 -0
- data/spec/graph_ql/introspection/directive_type_spec.rb +40 -0
- data/spec/graph_ql/introspection/schema_type_spec.rb +39 -0
- data/spec/graph_ql/introspection/type_type_spec.rb +104 -0
- data/spec/graph_ql/parser/parser_spec.rb +120 -0
- data/spec/graph_ql/parser/transform_spec.rb +109 -0
- data/spec/graph_ql/parser/visitor_spec.rb +31 -0
- data/spec/graph_ql/query/operation_resolver_spec.rb +14 -0
- data/spec/graph_ql/query_spec.rb +82 -0
- data/spec/graph_ql/schema/type_reducer_spec.rb +24 -0
- data/spec/graph_ql/types/input_object_type_spec.rb +12 -0
- data/spec/graph_ql/types/object_type_spec.rb +35 -0
- data/spec/graph_ql/union_spec.rb +27 -0
- data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +28 -0
- data/spec/graph_ql/validations/fields_will_merge_spec.rb +40 -0
- data/spec/graph_ql/validations/fragments_are_used_spec.rb +28 -0
- data/spec/graph_ql/validator_spec.rb +24 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/support/dummy_app.rb +123 -63
- data/spec/support/dummy_data.rb +11 -0
- metadata +107 -59
- data/lib/graphql/call.rb +0 -8
- data/lib/graphql/connection.rb +0 -65
- data/lib/graphql/field.rb +0 -12
- data/lib/graphql/field_definer.rb +0 -25
- data/lib/graphql/introspection/call_type.rb +0 -13
- data/lib/graphql/introspection/connection.rb +0 -9
- data/lib/graphql/introspection/field_type.rb +0 -10
- data/lib/graphql/introspection/root_call_argument_node.rb +0 -5
- data/lib/graphql/introspection/root_call_type.rb +0 -20
- data/lib/graphql/introspection/schema_call.rb +0 -8
- data/lib/graphql/introspection/schema_type.rb +0 -17
- data/lib/graphql/introspection/type_call.rb +0 -8
- data/lib/graphql/introspection/type_type.rb +0 -18
- data/lib/graphql/node.rb +0 -244
- data/lib/graphql/parser/parser.rb +0 -39
- data/lib/graphql/parser/transform.rb +0 -22
- data/lib/graphql/query.rb +0 -109
- data/lib/graphql/root_call.rb +0 -202
- data/lib/graphql/root_call_argument.rb +0 -11
- data/lib/graphql/root_call_argument_definer.rb +0 -17
- data/lib/graphql/schema/all.rb +0 -46
- data/lib/graphql/schema/schema.rb +0 -87
- data/lib/graphql/schema/schema_validation.rb +0 -32
- data/lib/graphql/syntax/call.rb +0 -8
- data/lib/graphql/syntax/field.rb +0 -9
- data/lib/graphql/syntax/fragment.rb +0 -7
- data/lib/graphql/syntax/node.rb +0 -8
- data/lib/graphql/syntax/query.rb +0 -8
- data/lib/graphql/syntax/variable.rb +0 -7
- data/lib/graphql/types/boolean_type.rb +0 -3
- data/lib/graphql/types/number_type.rb +0 -3
- data/lib/graphql/types/object_type.rb +0 -6
- data/lib/graphql/types/string_type.rb +0 -3
- data/lib/graphql/version.rb +0 -3
- data/spec/graphql/node_spec.rb +0 -69
- data/spec/graphql/parser/parser_spec.rb +0 -168
- data/spec/graphql/parser/transform_spec.rb +0 -157
- data/spec/graphql/query_spec.rb +0 -274
- data/spec/graphql/root_call_spec.rb +0 -69
- data/spec/graphql/schema/schema_spec.rb +0 -93
- data/spec/graphql/schema/schema_validation_spec.rb +0 -48
- data/spec/support/nodes.rb +0 -175
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Directive do
|
4
|
+
let(:result) { GraphQL::Query.new(DummySchema, query_string, params: {"t" => true, "f" => false}).result }
|
5
|
+
describe 'on fields' do
|
6
|
+
let(:query_string) { %|query directives($t: Boolean!, $f: Boolean!) {
|
7
|
+
cheese(id: 1) {
|
8
|
+
# plain fields:
|
9
|
+
skipFlavor: flavor @skip(if: true)
|
10
|
+
dontSkipFlavor: flavor @skip(if: false)
|
11
|
+
includeFlavor: flavor @include(if: $t)
|
12
|
+
dontIncludeFlavor: flavor @include(if: $f)
|
13
|
+
# fields in fragments
|
14
|
+
... includeIdField
|
15
|
+
... dontIncludeIdField
|
16
|
+
... skipIdField
|
17
|
+
... dontSkipIdField
|
18
|
+
}
|
19
|
+
}
|
20
|
+
fragment includeIdField on Cheese { includeId: id @include(if: true) }
|
21
|
+
fragment dontIncludeIdField on Cheese { dontIncludeId: id @include(if: false) }
|
22
|
+
fragment skipIdField on Cheese { skipId: id @skip(if: true) }
|
23
|
+
fragment dontSkipIdField on Cheese { dontSkipId: id @skip(if: false) }
|
24
|
+
|}
|
25
|
+
it 'intercepts fields' do
|
26
|
+
expected = { "data" => {"directives" => {
|
27
|
+
"cheese" => {
|
28
|
+
"dontSkipFlavor" => "Brie",
|
29
|
+
"includeFlavor" => "Brie",
|
30
|
+
"includeId" => 1,
|
31
|
+
"dontSkipId" => 1,
|
32
|
+
},
|
33
|
+
}}}
|
34
|
+
assert_equal(expected, result)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
describe 'on fragments' do
|
38
|
+
let(:query_string) { %|query directives {
|
39
|
+
cheese(id: 1) {
|
40
|
+
... skipFlavorField @skip(if: true)
|
41
|
+
... dontSkipFlavorField @skip(if: false)
|
42
|
+
... includeFlavorField @include(if: true)
|
43
|
+
... dontIncludeFlavorField @include(if: false)
|
44
|
+
|
45
|
+
... includeIdField
|
46
|
+
... dontIncludeIdField
|
47
|
+
... skipIdField
|
48
|
+
... dontSkipIdField
|
49
|
+
|
50
|
+
... on Cheese @skip(if: true) { skipInlineId: id }
|
51
|
+
... on Cheese @skip(if: false) { dontSkipInlineId: id }
|
52
|
+
... on Cheese @include(if: true) { includeInlineId: id }
|
53
|
+
... on Cheese @include(if: false) { dontIncludeInlineId: id }
|
54
|
+
}
|
55
|
+
}
|
56
|
+
fragment includeFlavorField on Cheese { includeFlavor: flavor }
|
57
|
+
fragment dontIncludeFlavorField on Cheese { dontIncludeFlavor: flavor }
|
58
|
+
fragment skipFlavorField on Cheese { skipFlavor: flavor }
|
59
|
+
fragment dontSkipFlavorField on Cheese { dontSkipFlavor: flavor }
|
60
|
+
|
61
|
+
fragment includeIdField on Cheese @include(if: true) { includeId: id }
|
62
|
+
fragment dontIncludeIdField on Cheese @include(if: false) { dontIncludeId: id }
|
63
|
+
fragment skipIdField on Cheese @skip(if: true) { skipId: id }
|
64
|
+
fragment dontSkipIdField on Cheese @skip(if: false) { dontSkipId: id }
|
65
|
+
|}
|
66
|
+
|
67
|
+
it 'intercepts fragment spreads' do
|
68
|
+
expected = { "data" => {"directives" => {
|
69
|
+
"cheese" => {
|
70
|
+
"dontSkipFlavor" => "Brie",
|
71
|
+
"includeFlavor" => "Brie",
|
72
|
+
"includeId" => 1,
|
73
|
+
"dontSkipId" => 1,
|
74
|
+
"dontSkipInlineId" => 1,
|
75
|
+
"includeInlineId" => 1,
|
76
|
+
},
|
77
|
+
}}}
|
78
|
+
assert_equal(expected, result)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Interface do
|
4
|
+
let(:interface) { Edible }
|
5
|
+
it 'has possible types' do
|
6
|
+
assert_equal([CheeseType, MilkType], interface.possible_types)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'resolves types for objects' do
|
10
|
+
assert_equal(CheeseType, interface.resolve_type(CHEESES.values.first))
|
11
|
+
assert_equal(MilkType, interface.resolve_type(MILKS.values.first))
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::DirectiveType do
|
4
|
+
let(:query_string) {%|
|
5
|
+
query getDirectives {
|
6
|
+
__schema {
|
7
|
+
directives { name, args { name, type { name, ofType { name }} }, onField, onFragment, onOperation }
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|}
|
11
|
+
let(:result) { GraphQL::Query.new(DummySchema, query_string).result }
|
12
|
+
|
13
|
+
it 'shows directive info ' do
|
14
|
+
expected = { "data" => {"getDirectives" => {
|
15
|
+
"__schema" => {
|
16
|
+
"directives" => [
|
17
|
+
{
|
18
|
+
"name" => "skip",
|
19
|
+
"args" => [
|
20
|
+
{"name"=>"if", "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Boolean"}}}
|
21
|
+
],
|
22
|
+
"onField" => true,
|
23
|
+
"onFragment" => true,
|
24
|
+
"onOperation" => false,
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"name" => "include",
|
28
|
+
"args" => [
|
29
|
+
{"name"=>"if", "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Boolean"}}}
|
30
|
+
],
|
31
|
+
"onField" => true,
|
32
|
+
"onFragment" => true,
|
33
|
+
"onOperation" => false,
|
34
|
+
},
|
35
|
+
]
|
36
|
+
}
|
37
|
+
}}}
|
38
|
+
assert_equal(expected, result)
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::SchemaType do
|
4
|
+
let(:query_string) {%|
|
5
|
+
query getSchema {
|
6
|
+
__schema {
|
7
|
+
types { name }
|
8
|
+
queryType { fields { name }}
|
9
|
+
mutationType { fields { name }}
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|}
|
13
|
+
let(:result) { GraphQL::Query.new(DummySchema, query_string).result }
|
14
|
+
it 'exposes the schema' do
|
15
|
+
expected = { "data" => { "getSchema" => {
|
16
|
+
"__schema" => {
|
17
|
+
"types" => DummySchema.types.values.map { |t| t.name.nil? ? (p t; raise("no name for #{t}")) : {"name" => t.name} },
|
18
|
+
"queryType"=>{
|
19
|
+
"fields"=>[
|
20
|
+
{"name"=>"cheese"},
|
21
|
+
{"name"=>"fromSource"},
|
22
|
+
{"name"=>"favoriteEdible"},
|
23
|
+
{"name"=>"searchDairy"},
|
24
|
+
{"name"=>"__typename"},
|
25
|
+
{"name"=>"__type"},
|
26
|
+
{"name"=>"__schema"},
|
27
|
+
]
|
28
|
+
},
|
29
|
+
"mutationType"=> {
|
30
|
+
"fields"=>[
|
31
|
+
{"name"=>"pushValue"},
|
32
|
+
{"name"=>"__typename"}
|
33
|
+
]
|
34
|
+
},
|
35
|
+
}
|
36
|
+
}}}
|
37
|
+
assert_equal(expected, result)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::TypeType do
|
4
|
+
let(:query_string) {%|
|
5
|
+
query introspectionQuery {
|
6
|
+
cheeseType: __type(name: "Cheese") { name, kind, fields { name, isDeprecated, type { name, ofType { name } } } }
|
7
|
+
dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: false) { name, isDeprecated } }
|
8
|
+
dairyProduct: __type(name: "DairyProduct") { name, kind, possibleTypes { name } }
|
9
|
+
animalProduct: __type(name: "AnimalProduct") { name, kind, possibleTypes { name }, fields { name } }
|
10
|
+
}
|
11
|
+
|}
|
12
|
+
let(:query) { GraphQL::Query.new(DummySchema, query_string, context: {}, params: {"cheeseId" => 2})}
|
13
|
+
let(:cheese_fields) {[
|
14
|
+
{"name"=>"id", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "Int"}}},
|
15
|
+
{"name"=>"flavor", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
|
16
|
+
{"name"=>"source", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "DairyAnimal"}}},
|
17
|
+
{"name"=>"__typename", "isDeprecated"=>false, "type"=> { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
|
18
|
+
]}
|
19
|
+
|
20
|
+
let(:dairy_animals) {[
|
21
|
+
{"name"=>"COW", "isDeprecated"=> false },
|
22
|
+
{"name"=>"GOAT", "isDeprecated"=> false },
|
23
|
+
{"name"=>"SHEEP", "isDeprecated"=> false },
|
24
|
+
]}
|
25
|
+
it 'exposes metadata about types' do
|
26
|
+
expected = {"data"=> { "introspectionQuery" => {
|
27
|
+
"cheeseType" => {
|
28
|
+
"name"=> "Cheese",
|
29
|
+
"kind" => "OBJECT",
|
30
|
+
"fields"=> cheese_fields
|
31
|
+
},
|
32
|
+
"dairyAnimal"=>{
|
33
|
+
"name"=>"DairyAnimal",
|
34
|
+
"kind"=>"ENUM",
|
35
|
+
"enumValues"=> dairy_animals,
|
36
|
+
},
|
37
|
+
"dairyProduct"=>{
|
38
|
+
"name"=>"DairyProduct",
|
39
|
+
"kind"=>"UNION",
|
40
|
+
"possibleTypes"=>[{"name"=>"Milk"}, {"name"=>"Cheese"}],
|
41
|
+
},
|
42
|
+
"animalProduct" => {
|
43
|
+
"name"=>"AnimalProduct",
|
44
|
+
"kind"=>"INTERFACE",
|
45
|
+
"possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Milk"}],
|
46
|
+
"fields"=>[
|
47
|
+
{"name"=>"source"},
|
48
|
+
{"name"=>"__typename"},
|
49
|
+
]
|
50
|
+
}
|
51
|
+
}}}
|
52
|
+
assert_equal(expected, query.result)
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'deprecated fields' do
|
56
|
+
let(:query_string) {%|
|
57
|
+
query introspectionQuery {
|
58
|
+
cheeseType: __type(name: "Cheese") { name, kind, fields(includeDeprecated: true) { name, isDeprecated, type { name, ofType { name } } } }
|
59
|
+
dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: true) { name, isDeprecated } }
|
60
|
+
}
|
61
|
+
|}
|
62
|
+
let(:deprecated_fields) { {"name"=>"fatContent", "isDeprecated"=>true, "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}} }
|
63
|
+
it 'can expose deprecated fields' do
|
64
|
+
typename = cheese_fields.pop
|
65
|
+
new_cheese_fields = cheese_fields + [deprecated_fields, typename]
|
66
|
+
expected = { "data" => { "introspectionQuery" => {
|
67
|
+
"cheeseType" => {
|
68
|
+
"name"=> "Cheese",
|
69
|
+
"kind" => "OBJECT",
|
70
|
+
"fields"=> new_cheese_fields
|
71
|
+
},
|
72
|
+
"dairyAnimal"=>{
|
73
|
+
"name"=>"DairyAnimal",
|
74
|
+
"kind"=>"ENUM",
|
75
|
+
"enumValues"=> dairy_animals + [{"name" => "YAK", "isDeprecated" => true}],
|
76
|
+
},
|
77
|
+
}}}
|
78
|
+
assert_equal(expected, query.result)
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'input objects' do
|
82
|
+
let(:query_string) {%|
|
83
|
+
query introspectionQuery {
|
84
|
+
__type(name: "DairyProductInput") { name, description, kind, inputFields { name, type { name }, defaultValue } }
|
85
|
+
}
|
86
|
+
|}
|
87
|
+
|
88
|
+
it 'exposes metadata about input objects' do
|
89
|
+
expected = { "data" => { "introspectionQuery" => {
|
90
|
+
"__type" => {
|
91
|
+
"name"=>"DairyProductInput",
|
92
|
+
"description"=>"Properties for finding a dairy product",
|
93
|
+
"kind"=>"INPUT_OBJECT",
|
94
|
+
"inputFields"=>[
|
95
|
+
{"name"=>"source", "type"=>{ "name" => "DairyAnimal"}, "defaultValue"=>nil},
|
96
|
+
{"name"=>"fatContent", "type"=>{ "name" => "Float"}, "defaultValue"=>nil}
|
97
|
+
]
|
98
|
+
}
|
99
|
+
}}}
|
100
|
+
assert_equal(expected, query.result)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Parser do
|
4
|
+
let(:parser) { GraphQL::PARSER }
|
5
|
+
|
6
|
+
it 'parses documents' do
|
7
|
+
assert(parser.parse_with_debug(%|
|
8
|
+
# let's make a big query:
|
9
|
+
# a read-only:
|
10
|
+
query getStuff {id, name @skip(if: true)}
|
11
|
+
# a mutation:
|
12
|
+
mutation changeStuff($override: Boolean!, $cucumbers: [Vegetable]!) @veggie, @healthy(vitamins: true) {
|
13
|
+
# change the cucumber
|
14
|
+
changeStuff(thing: $cucumbers) {
|
15
|
+
id,
|
16
|
+
name,
|
17
|
+
... on Species { color },
|
18
|
+
... family # background info, of course
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
# a fragment:
|
23
|
+
fragment family on Species {
|
24
|
+
family {
|
25
|
+
name, # name of the family
|
26
|
+
members(first: 3, query: {isPlant: true}) # some of the other examples
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
fragment nonsense on NonsenseType @skip(if: true) { bogus }
|
31
|
+
|), 'gets a document with lots of comments')
|
32
|
+
|
33
|
+
assert(parser.parse_with_debug("{fields, only, inThisOne}"), 'fetch-only query')
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
it 'parses operation definitions' do
|
38
|
+
assert(parser.operation_definition.parse_with_debug(%|{id, name, ...people}|), "just a selection")
|
39
|
+
assert(parser.operation_definition.parse_with_debug(%|query personStuff {id, name, ...people, ... stuff}|), "named fetch")
|
40
|
+
assert(parser.operation_definition.parse_with_debug(%|query personStuff @flagDirective {id, name, ...people}|), "with a directive")
|
41
|
+
assert(parser.operation_definition.parse_with_debug(%|mutation changeStuff($stuff: Int = 1, $things: [Boolean]!) {id, name, ...people}|), "mutation with arguments")
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'parses fragment definitions' do
|
45
|
+
assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food { fat, sodium, carbohydrates, vitamins { a, b } }|))
|
46
|
+
assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food @directive(key: 1) { fat, sodium, carbohydrates, vitamins { a, b } }|), 'gets directives')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'parses selections' do
|
50
|
+
assert(parser.selections.parse_with_debug(%|{id, name, people { count }}|), 'gets nested fields')
|
51
|
+
assert(parser.selections.parse_with_debug(%|{id, ... myFragment }|), 'gets fragment spreads')
|
52
|
+
assert(parser.selections.parse_with_debug(%|{id, ... on User @myFlag { name, photo } }|), 'gets inline fragments')
|
53
|
+
assert(parser.selections.parse_with_debug(%|{id @skip(if: true), ... myFragment @include(if: $something)}|), 'gets directives')
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'parses directives' do
|
57
|
+
assert(parser.directives.parse_with_debug("@doSomething"), 'gets without argument')
|
58
|
+
assert(parser.directives.parse_with_debug('@doSomething(why: "forSomeReason")'), 'gets with argument')
|
59
|
+
assert(parser.directives.parse_with_debug('@myFlag, @doSomething(why: "forSomeReason")'), 'gets multiple')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'parses fields' do
|
63
|
+
assert(parser.field.parse_with_debug(%|myField { name, id }|), 'gets subselections')
|
64
|
+
assert(parser.field.parse_with_debug(%{myAlias: myField}), 'gets an alias')
|
65
|
+
assert(parser.field.parse_with_debug(%{myField(intKey: 1, floatKey: 1.1e5)}), 'gets arguments')
|
66
|
+
assert(parser.field.parse_with_debug(%{myAlias: myField(stringKey: "my_string", boolKey: false, objKey: {key : true})}), 'gets alias and arguments')
|
67
|
+
assert(parser.field.parse_with_debug(%|myField @withFlag, @skip(if: true) { name, id }|), 'gets with directive')
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'parses variable definitions' do
|
71
|
+
assert(parser.operation_variable_definition.parse_with_debug("$myVar: Boolean = true"), "it gets variables with defaults")
|
72
|
+
assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Int]"), "it gets list variables")
|
73
|
+
assert(parser.operation_variable_definition.parse_with_debug("$myVar: Elephant!"), "it gets non-null variables")
|
74
|
+
assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Food]!"), "it gets non-null list variables")
|
75
|
+
assert(parser.operation_variable_definitions.parse_with_debug(%|($myVar: Elephant!, $myList: [Float], $myString: String="Cheese")|), "it gets a list of defns")
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'value' do
|
79
|
+
it 'gets ints' do
|
80
|
+
assert(parser.value.parse_with_debug("100"), 'positive')
|
81
|
+
assert(parser.value.parse_with_debug("-9"), 'negative')
|
82
|
+
assert(parser.value.parse_with_debug("0"), 'zero')
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'gets floats' do
|
86
|
+
assert(parser.value.parse_with_debug("1.14"), 'no exponent')
|
87
|
+
assert(parser.value.parse_with_debug("6.7e-9"), 'negative exponent')
|
88
|
+
assert(parser.value.parse_with_debug("0.4e12"), 'exponent')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'gets booleans' do
|
92
|
+
assert(parser.value.parse_with_debug("true"))
|
93
|
+
assert(parser.value.parse_with_debug("false"))
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'gets strings' do
|
97
|
+
assert(parser.value.parse_with_debug('"my string"'))
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'gets arrays' do
|
101
|
+
assert(parser.value.parse_with_debug('[true, 1, "my string", -5.123e56]'), 'array of values')
|
102
|
+
assert(parser.value.parse_with_debug('[]'), 'empty array')
|
103
|
+
assert(parser.value.parse_with_debug('[[true, 1], ["my string", -5.123e56]]'), 'array of arrays')
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'gets variables' do
|
107
|
+
assert(parser.value.parse_with_debug('$myVariable'), 'gets named variables')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'gets objects' do
|
111
|
+
assert(parser.value.parse_with_debug('{name: "tomato", calories: 50}'), 'gets scalar values')
|
112
|
+
assert(parser.value.parse_with_debug('{listOfValues: [1, 2, [3]], nestedObject: {nestedKey: "nested{Value}"}}'), 'gets complex values')
|
113
|
+
assert(parser.value.parse_with_debug('{variableKey: $variableValue}'), 'gets variables')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'gets enums' do
|
117
|
+
assert(parser.value.parse_with_debug("MY_ENUM"), 'gets enums')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Transform do
|
4
|
+
def get_result(query_string, parse: nil, debug: false)
|
5
|
+
# send parse: :value to do something less than a document
|
6
|
+
parser = parse ? GraphQL::PARSER.send(parse) : GraphQL::PARSER
|
7
|
+
raw_tree = parser.parse_with_debug(query_string)
|
8
|
+
transformed_result = GraphQL::TRANSFORM.apply(raw_tree)
|
9
|
+
# send debug: true to see parsing & transforming output
|
10
|
+
if debug
|
11
|
+
p raw_tree.inspect
|
12
|
+
p transformed_result.inspect
|
13
|
+
end
|
14
|
+
transformed_result
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'transforms documents' do
|
18
|
+
query = %|
|
19
|
+
# you can retrieve data:
|
20
|
+
query someInfo {
|
21
|
+
me {
|
22
|
+
name, favorite_food,
|
23
|
+
...personInfo
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
# assign fragments:
|
28
|
+
fragment personInfo on Person {
|
29
|
+
birthdate, name # with fields
|
30
|
+
}
|
31
|
+
|
32
|
+
fragment petInfo on Pet { isHousebroken, species } # all on one line
|
33
|
+
|
34
|
+
# and also mutations
|
35
|
+
mutation changePetInfo($id: Int = 5, $info: [Dog]) {
|
36
|
+
changePetName(id: $id, info: $info) {
|
37
|
+
name,
|
38
|
+
... petInfo,
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
|
42
|
+
res = get_result(query, debug: false)
|
43
|
+
assert_equal(4, res.parts.length)
|
44
|
+
|
45
|
+
res = get_result("{ me {id, birthdate} } # query shorthand")
|
46
|
+
assert_equal(1, res.parts.length)
|
47
|
+
assert_equal("me", res.parts.first.selections.first.name)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'transforms operation definitions' do
|
51
|
+
res = get_result("query someInfo { a, b, c }", parse: :operation_definition)
|
52
|
+
assert_equal("query", res.operation_type)
|
53
|
+
assert_equal("someInfo", res.name)
|
54
|
+
assert_equal(3, res.selections.length)
|
55
|
+
|
56
|
+
res = get_result("mutation changeThings($var: Float = 4.5, $arr: [Int]!) @flag, @skip(if: 1) { changeThings(var: $var) { a,b,c }}", parse: :operation_definition)
|
57
|
+
assert_equal("mutation", res.operation_type)
|
58
|
+
assert_equal("var", res.variables.first.name)
|
59
|
+
assert_equal("Float", res.variables.first.type.name)
|
60
|
+
assert_equal(4.5, res.variables.first.default_value)
|
61
|
+
assert_equal("arr", res.variables.last.name)
|
62
|
+
assert_equal("Int", res.variables.last.type.of_type.of_type.name)
|
63
|
+
assert_equal(2, res.directives.length)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'transforms fragment definitions' do
|
67
|
+
res = get_result("fragment someFields on SomeType @flag1, @flag2 { id, name }", parse: :fragment_definition)
|
68
|
+
assert_equal("someFields", res.name)
|
69
|
+
assert_equal("SomeType", res.type)
|
70
|
+
assert_equal(2, res.directives.length)
|
71
|
+
assert_equal(2, res.selections.length)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'transforms selections' do
|
75
|
+
res = get_result("{ id, ...petStuff @flag, ... on Pet { isHousebroken }, name }", parse: :selections)
|
76
|
+
expected_classes = [GraphQL::Nodes::Field, GraphQL::Nodes::FragmentSpread, GraphQL::Nodes::InlineFragment, GraphQL::Nodes::Field]
|
77
|
+
assert_equal(expected_classes, res.map(&:class))
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'transforms fields' do
|
81
|
+
res = get_result(%|best_pals: friends(first: 3, coolnessLevel: SO_COOL, query: {nice: {very: true}})|, parse: :field)
|
82
|
+
assert_equal(GraphQL::Nodes::Field, res.class)
|
83
|
+
assert_equal("friends", res.name)
|
84
|
+
assert_equal("best_pals", res.alias)
|
85
|
+
assert_equal("first", res.arguments[0].name)
|
86
|
+
assert_equal(3, res.arguments[0].value)
|
87
|
+
assert_equal("SO_COOL", res.arguments[1].value.name)
|
88
|
+
assert_equal({"nice" => {"very" => true}}, res.arguments[2].value.to_h)
|
89
|
+
|
90
|
+
res = get_result(%|me @flag, @include(if: "something") {name, id}|, parse: :field)
|
91
|
+
assert_equal("me", res.name)
|
92
|
+
assert_equal(nil, res.alias)
|
93
|
+
assert_equal(2, res.directives.length)
|
94
|
+
assert_equal("flag", res.directives.first.name)
|
95
|
+
assert_equal("something", res.directives.last.arguments.first.value)
|
96
|
+
assert_equal(2, res.selections.length)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'transforms directives' do
|
100
|
+
res = get_result("@doSomething(vigorously: true)", parse: :directive)
|
101
|
+
assert_equal("doSomething", res.name, 'gets the name without @')
|
102
|
+
assert_equal("vigorously", res.arguments.first.name)
|
103
|
+
assert_equal(true, res.arguments.first.value)
|
104
|
+
|
105
|
+
res = get_result("@someFlag", parse: :directive)
|
106
|
+
assert_equal("someFlag", res.name)
|
107
|
+
assert_equal([], res.arguments, 'gets [] if no args')
|
108
|
+
end
|
109
|
+
end
|