graphql 0.0.4 → 0.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/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
|