graphql 0.0.1 → 0.0.2
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 +78 -9
- data/lib/graphql/call.rb +7 -0
- data/lib/graphql/connection.rb +44 -0
- data/lib/graphql/field.rb +117 -0
- data/lib/graphql/field_definer.rb +29 -0
- data/lib/graphql/introspection/call_node.rb +13 -0
- data/lib/graphql/introspection/connection.rb +9 -0
- data/lib/graphql/introspection/field_node.rb +19 -0
- data/lib/graphql/introspection/root_call_argument_node.rb +5 -0
- data/lib/graphql/introspection/root_call_node.rb +16 -0
- data/lib/graphql/introspection/schema_call.rb +8 -0
- data/lib/graphql/introspection/schema_node.rb +17 -0
- data/lib/graphql/introspection/type_call.rb +8 -0
- data/lib/graphql/introspection/type_node.rb +16 -0
- data/lib/graphql/node.rb +141 -34
- data/lib/graphql/parser.rb +19 -8
- data/lib/graphql/query.rb +64 -21
- data/lib/graphql/root_call.rb +176 -0
- data/lib/graphql/root_call_argument.rb +8 -0
- data/lib/graphql/root_call_argument_definer.rb +20 -0
- data/lib/graphql/schema.rb +99 -0
- data/lib/graphql/syntax/call.rb +3 -12
- data/lib/graphql/syntax/field.rb +4 -2
- data/lib/graphql/syntax/node.rb +3 -10
- data/lib/graphql/syntax/query.rb +7 -0
- data/lib/graphql/syntax/variable.rb +7 -0
- data/lib/graphql/transform.rb +14 -5
- data/lib/graphql/types/boolean_field.rb +3 -0
- data/lib/graphql/types/connection_field.rb +30 -0
- data/lib/graphql/types/cursor_field.rb +9 -0
- data/lib/graphql/types/number_field.rb +3 -0
- data/lib/graphql/types/object_field.rb +8 -0
- data/lib/graphql/types/string_field.rb +3 -0
- data/lib/graphql/types/type_field.rb +6 -0
- data/lib/graphql/version.rb +3 -0
- data/readme.md +142 -10
- data/spec/graphql/field_spec.rb +66 -0
- data/spec/graphql/node_spec.rb +68 -0
- data/spec/graphql/parser_spec.rb +75 -25
- data/spec/graphql/query_spec.rb +185 -83
- data/spec/graphql/root_call_spec.rb +55 -0
- data/spec/graphql/schema_spec.rb +128 -0
- data/spec/graphql/transform_spec.rb +124 -39
- data/spec/spec_helper.rb +2 -1
- data/spec/support/dummy_app.rb +43 -23
- data/spec/support/nodes.rb +145 -32
- metadata +78 -16
- data/lib/graphql/collection_edge.rb +0 -62
- data/lib/graphql/syntax/edge.rb +0 -12
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe GraphQL::Field do
|
5
|
+
let(:owner) { OpenStruct.new(name: "TestOwner")}
|
6
|
+
let(:field) { GraphQL::Field.create_class(name: "high_fives", type: :number, owner_class: owner).new(query: {}) }
|
7
|
+
|
8
|
+
describe '#name' do
|
9
|
+
it 'is present' do
|
10
|
+
assert_equal field.name, "high_fives"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#method' do
|
15
|
+
it 'defaults to name' do
|
16
|
+
assert_equal "high_fives", field.method
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '.call' do
|
21
|
+
let(:content_field) { Nodes::PostNode.all_fields["content"] }
|
22
|
+
it 'doesnt register a call twice' do
|
23
|
+
assert_equal 3, content_field.calls.size
|
24
|
+
call = content_field.calls.first[1]
|
25
|
+
content_field.call(call.name, call.lambda)
|
26
|
+
content_field.call(call.name, call.lambda)
|
27
|
+
assert_equal 3, content_field.calls.size
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '.to_s' do
|
32
|
+
it 'includes name' do
|
33
|
+
assert_match(/high_fives/, field.class.to_s)
|
34
|
+
end
|
35
|
+
it 'includes owner name' do
|
36
|
+
assert_match(/TestOwner/, field.class.to_s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '__type__' do
|
41
|
+
let(:query_string) { "type(post) { fields { edges { node { name, type, calls { edges { node { name } }} } } } } "}
|
42
|
+
let(:query) { GraphQL::Query.new(query_string, context: {}) }
|
43
|
+
let(:result) { query.as_result }
|
44
|
+
let(:id_field) { result["post"]["fields"]["edges"][1]["node"] }
|
45
|
+
let(:title_field) { result["post"]["fields"]["edges"][2]["node"] }
|
46
|
+
let(:comments_field) { result["post"]["fields"]["edges"][5]["node"] }
|
47
|
+
let(:content_field) { result["post"]["fields"]["edges"][3]["node"] }
|
48
|
+
|
49
|
+
it 'has name' do
|
50
|
+
assert_equal "id", id_field["name"]
|
51
|
+
assert_equal "title", title_field["name"]
|
52
|
+
assert_equal "comments", comments_field["name"]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'has type' do
|
56
|
+
assert_equal "number", id_field["type"]
|
57
|
+
assert_equal "string", title_field["type"]
|
58
|
+
assert_equal "connection", comments_field["type"]
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'has calls' do
|
62
|
+
assert_equal 3, content_field["calls"]["edges"].length
|
63
|
+
assert_equal ["from", "for", "select"], content_field["calls"]["edges"].map {|c| c["node"]["name"] }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Node do
|
4
|
+
let(:query_string) { "type(post) { name, description, fields { count, edges { node { name, description }}} }"}
|
5
|
+
let(:result) { GraphQL::Query.new(query_string).as_result}
|
6
|
+
|
7
|
+
describe '__type__' do
|
8
|
+
let(:title_field) { result["post"]["fields"]["edges"].find {|e| e["node"]["name"] == "title"}["node"] }
|
9
|
+
it 'has name' do
|
10
|
+
assert_equal "post", result["post"]["name"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has description' do
|
14
|
+
assert_equal "A blog post entry", result["post"]["description"]
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'has fields' do
|
18
|
+
assert_equal 8, result["post"]["fields"]["count"]
|
19
|
+
assert_equal({ "name" => "title", "description" => nil}, title_field)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'getting the __type__ field' do
|
23
|
+
before do
|
24
|
+
@post = Post.create(id: 155, content: "Hello world")
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
@post.destroy
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:query_string) { "post(155) { __type__ { name, fields { count } } }"}
|
32
|
+
|
33
|
+
it 'exposes the type' do
|
34
|
+
assert_equal "post", result["155"]["__type__"]["name"]
|
35
|
+
assert_equal 8, result["155"]["__type__"]["fields"]["count"]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.node_name' do
|
41
|
+
let(:query_string) { "type(upvote) { name }"}
|
42
|
+
|
43
|
+
it 'overrides __type__.name' do
|
44
|
+
assert_equal "upvote", result["upvote"]["name"]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '.field' do
|
49
|
+
it 'doesnt add the field twice if you call it twice' do
|
50
|
+
assert_equal 5, Nodes::CommentNode.all_fields.size
|
51
|
+
Nodes::CommentNode.field.number(:id)
|
52
|
+
Nodes::CommentNode.field.number(:id)
|
53
|
+
assert_equal 5, Nodes::CommentNode.all_fields.size
|
54
|
+
Nodes::CommentNode.remove_field(:id)
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'type:' do
|
58
|
+
it 'uses symbols to find built-ins' do
|
59
|
+
id_field = Nodes::CommentNode.all_fields["id"]
|
60
|
+
assert id_field.superclass == GraphQL::Types::NumberField
|
61
|
+
end
|
62
|
+
it 'uses the provided class as a superclass' do
|
63
|
+
letters_field = Nodes::CommentNode.all_fields["letters"]
|
64
|
+
assert letters_field.superclass == Nodes::LetterSelectionField
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/graphql/parser_spec.rb
CHANGED
@@ -1,33 +1,59 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe GraphQL::Parser do
|
4
|
-
let(:node_name) { "" }
|
5
|
-
let(:fields) { "id, name"}
|
6
|
-
let(:query) { "#{node_name} { #{fields} }"}
|
7
4
|
let(:parser) { GraphQL::PARSER }
|
8
5
|
|
6
|
+
describe 'query' do
|
7
|
+
let(:query) { parser.query }
|
8
|
+
it 'parses node-only' do
|
9
|
+
assert query.parse_with_debug("node(4) { id, name } ")
|
10
|
+
end
|
11
|
+
it 'parses node and variables' do
|
12
|
+
assert query.parse_with_debug(%{
|
13
|
+
like_page(<page>) {
|
14
|
+
page { id }
|
15
|
+
}
|
16
|
+
<page>: {
|
17
|
+
"page": {"id": 1},
|
18
|
+
"person" : { "id", 4}
|
19
|
+
}
|
20
|
+
<other>: {
|
21
|
+
"page": {"id": 1},
|
22
|
+
"person" : { "id", 4}
|
23
|
+
}
|
24
|
+
})
|
25
|
+
end
|
26
|
+
end
|
9
27
|
describe 'field' do
|
10
28
|
let(:field) { parser.field }
|
11
29
|
it 'finds words' do
|
12
|
-
assert field.parse_with_debug("name")
|
13
30
|
assert field.parse_with_debug("date_of_birth")
|
14
31
|
end
|
15
|
-
end
|
16
32
|
|
17
|
-
|
18
|
-
|
33
|
+
it 'finds aliases' do
|
34
|
+
assert field.parse_with_debug("name as moniker")
|
35
|
+
end
|
19
36
|
|
20
37
|
it 'finds calls on fields' do
|
21
|
-
assert
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
")
|
38
|
+
assert field.parse_with_debug("url.site(www).upcase()")
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'fields that return objects' do
|
42
|
+
it 'finds them' do
|
43
|
+
assert field.parse_with_debug("birthdate { month, year }")
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'finds them with aliases' do
|
47
|
+
assert field.parse_with_debug("birthdate as d_o_b { month, year }")
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'finds them with calls' do
|
51
|
+
assert field.parse_with_debug("friends.after(123) { count { edges { node { id } } } }")
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'finds them with calls and aliases' do
|
55
|
+
assert field.parse_with_debug("friends.after(123) as pals { count { edges { node { id } } } }")
|
56
|
+
end
|
31
57
|
end
|
32
58
|
end
|
33
59
|
|
@@ -37,15 +63,13 @@ describe GraphQL::Parser do
|
|
37
63
|
assert call.parse_with_debug("node(123)")
|
38
64
|
assert call.parse_with_debug("viewer()")
|
39
65
|
end
|
40
|
-
end
|
41
66
|
|
42
|
-
|
43
|
-
|
44
|
-
it 'finds deep calls' do
|
45
|
-
assert call_chain.parse_with_debug("friends.after(123).first(2)")
|
67
|
+
it 'finds calls with multiple arguments' do
|
68
|
+
assert call.parse_with_debug("node(4, 6)")
|
46
69
|
end
|
47
|
-
|
48
|
-
|
70
|
+
|
71
|
+
it 'finds calls with variables' do
|
72
|
+
assert call.parse_with_debug("like_page(<page>)")
|
49
73
|
end
|
50
74
|
end
|
51
75
|
|
@@ -71,7 +95,8 @@ describe GraphQL::Parser do
|
|
71
95
|
end
|
72
96
|
|
73
97
|
it 'parses nested nodes' do
|
74
|
-
assert node.parse_with_debug("
|
98
|
+
assert node.parse_with_debug("
|
99
|
+
node(someone)
|
75
100
|
{
|
76
101
|
id,
|
77
102
|
name,
|
@@ -86,4 +111,29 @@ describe GraphQL::Parser do
|
|
86
111
|
")
|
87
112
|
end
|
88
113
|
end
|
114
|
+
|
115
|
+
describe 'variable' do
|
116
|
+
let(:variable) { parser.variable }
|
117
|
+
|
118
|
+
it 'gets scalar variables' do
|
119
|
+
assert variable.parse_with_debug(%{<some_number>: 888})
|
120
|
+
assert variable.parse_with_debug(%{<some_string>: my_string})
|
121
|
+
end
|
122
|
+
it 'gets json variables' do
|
123
|
+
assert variable.parse_with_debug(%{<my_input>: {"key": "value"}})
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'gets variables with nesting' do
|
127
|
+
assert variable.parse_with_debug(%{
|
128
|
+
<my_input>: {
|
129
|
+
"key": "value",
|
130
|
+
"1": 2,
|
131
|
+
"true": false,
|
132
|
+
"nested": {
|
133
|
+
"key" : "value"
|
134
|
+
}
|
135
|
+
}
|
136
|
+
})
|
137
|
+
end
|
138
|
+
end
|
89
139
|
end
|
data/spec/graphql/query_spec.rb
CHANGED
@@ -2,153 +2,255 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe GraphQL::Query do
|
4
4
|
let(:query_string) { "post(123) { title, content } "}
|
5
|
-
let(:
|
6
|
-
let(:query) { GraphQL::Query.new(query_string,
|
5
|
+
let(:context) { Context.new(person_name: "Han Solo") }
|
6
|
+
let(:query) { GraphQL::Query.new(query_string, context: context) }
|
7
|
+
let(:result) { query.as_result }
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
before do
|
10
|
+
@post = Post.create(id: 123, content: "So many great things", title: "My great post", published_at: Date.new(2010,1,4))
|
11
|
+
@comment1 = Comment.create(id: 444, post_id: 123, content: "I agree", rating: 5)
|
12
|
+
@comment2 = Comment.create(id: 445, post_id: 123, content: "I disagree", rating: 1)
|
13
|
+
@like1 = Like.create(id: 991, post_id: 123)
|
14
|
+
@like2 = Like.create(id: 992, post_id: 123)
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
@post.destroy
|
19
|
+
@comment1.destroy
|
20
|
+
@comment2.destroy
|
21
|
+
@like1.destroy
|
22
|
+
@like2.destroy
|
12
23
|
end
|
13
24
|
|
14
|
-
describe '#
|
15
|
-
|
16
|
-
|
17
|
-
@comment1 = Comment.create(id: 444, post_id: 123, content: "I agree")
|
18
|
-
@comment2 = Comment.create(id: 445, post_id: 123, content: "I disagree")
|
25
|
+
describe '#as_result' do
|
26
|
+
it 'finds fields that delegate to a target' do
|
27
|
+
assert_equal result, {"123" => {"title" => "My great post", "content" => "So many great things"}}
|
19
28
|
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
30
|
+
describe 'with multiple roots' do
|
31
|
+
let(:query_string) { "comment(444, 445) { content } "}
|
32
|
+
it 'adds each as a key-value of the response' do
|
33
|
+
assert_equal ["444", "445"], result.keys
|
34
|
+
end
|
25
35
|
end
|
26
36
|
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
describe 'when accessing fields that return objects' do
|
38
|
+
describe 'when making calls on the field' do
|
39
|
+
let(:query_string) { "post(123) { published_at.minus_days(200) { year } }"}
|
40
|
+
it 'returns the modified value' do
|
41
|
+
assert_equal 2009, result["123"]["published_at"]["year"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
describe 'when requesting more fields' do
|
45
|
+
let(:query_string) { "post(123) { published_at { month, year } }"}
|
46
|
+
it 'returns those fields' do
|
47
|
+
assert_equal({"month" => 1, "year" => 2010}, result["123"]["published_at"])
|
48
|
+
end
|
49
|
+
end
|
30
50
|
end
|
51
|
+
describe 'when aliasing things' do
|
52
|
+
let(:query_string) { "post(123) { title as headline, content as what_it_says }"}
|
31
53
|
|
32
|
-
|
33
|
-
|
34
|
-
"123"
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
}
|
54
|
+
it 'applies aliases to fields' do
|
55
|
+
assert_equal @post.title, result["123"]["headline"]
|
56
|
+
assert_equal @post.content, result["123"]["what_it_says"]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'applies aliases to edges' # dunno the syntax yet
|
39
60
|
end
|
40
61
|
|
41
62
|
describe 'when requesting fields defined on the node' do
|
42
|
-
let(:query_string) { "post(123) {
|
63
|
+
let(:query_string) { "post(123) { length } "}
|
43
64
|
it 'finds fields defined on the node' do
|
44
|
-
assert_equal
|
65
|
+
assert_equal 20, result["123"]["length"]
|
45
66
|
end
|
46
67
|
end
|
47
68
|
|
69
|
+
describe 'when accessing custom fields' do
|
70
|
+
let(:query_string) { "comment(444) { letters }"}
|
71
|
+
it 'uses the custom field' do
|
72
|
+
assert_equal "I agree", result["444"]["letters"]
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'when making calls on fields' do
|
76
|
+
let(:query_string) { "comment(444) {
|
77
|
+
letters.select(4, 3),
|
78
|
+
letters.from(3).for(2) as snippet
|
79
|
+
}"}
|
80
|
+
|
81
|
+
it 'works with aliases' do
|
82
|
+
assert result["444"]["snippet"].present?
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'applies calls' do
|
86
|
+
assert_equal "gr", result["444"]["snippet"]
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'applies calls with multiple arguments' do
|
90
|
+
assert_equal "ree", result["444"]["letters"]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'when requesting fields overriden on a child class' do
|
95
|
+
let(:query_string) { 'thumb_up(991) { id }'}
|
96
|
+
it 'uses the child implementation' do
|
97
|
+
assert_equal '991991', result["991991"]["id"]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
48
101
|
|
49
102
|
describe 'when requesting an undefined field' do
|
50
103
|
let(:query_string) { "post(123) { destroy } "}
|
51
104
|
it 'raises a FieldNotDefined error' do
|
52
|
-
assert_raises(GraphQL::FieldNotDefinedError) { query.
|
105
|
+
assert_raises(GraphQL::FieldNotDefinedError) { query.as_result }
|
53
106
|
assert(Post.find(123).present?)
|
54
107
|
end
|
55
108
|
end
|
56
109
|
|
57
110
|
describe 'when the root call doesnt have an argument' do
|
58
|
-
let(:query_string) { "
|
59
|
-
it 'calls the node with
|
60
|
-
|
61
|
-
query.to_json
|
111
|
+
let(:query_string) { "context() { person_name }"}
|
112
|
+
it 'calls the node with no arguments' do
|
113
|
+
assert_equal "Han Solo", result["context"]["person_name"]
|
62
114
|
end
|
63
115
|
end
|
64
116
|
|
65
117
|
describe 'when requesting a collection' do
|
66
118
|
let(:query_string) { "post(123) {
|
67
119
|
title,
|
68
|
-
comments {
|
69
|
-
count,
|
70
|
-
edges {
|
71
|
-
cursor,
|
72
|
-
node {
|
73
|
-
content
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
120
|
+
comments { count, edges { cursor, node { content } } }
|
77
121
|
}"}
|
122
|
+
|
78
123
|
it 'returns collection data' do
|
79
|
-
assert_equal
|
124
|
+
assert_equal result, {
|
80
125
|
"123" => {
|
81
126
|
"title" => "My great post",
|
82
127
|
"comments" => {
|
83
128
|
"count" => 2,
|
84
129
|
"edges" => [
|
85
|
-
{
|
86
|
-
|
87
|
-
"node" => {
|
88
|
-
"content" => "I agree"
|
89
|
-
}
|
90
|
-
},
|
91
|
-
{
|
92
|
-
"cursor" => "445",
|
93
|
-
"node" => {
|
94
|
-
"content" => "I disagree"
|
95
|
-
}
|
96
|
-
}
|
130
|
+
{ "cursor" => "444", "node" => {"content" => "I agree"} },
|
131
|
+
{ "cursor" => "445", "node" => {"content" => "I disagree"}}
|
97
132
|
]
|
98
|
-
|
99
|
-
}
|
100
|
-
}
|
133
|
+
}}}
|
101
134
|
end
|
102
135
|
end
|
103
136
|
|
104
137
|
describe 'when making calls on a collection' do
|
105
|
-
let(:query_string) { "post(123) {
|
106
|
-
comments.first(1) {
|
107
|
-
edges { cursor, node { content } }
|
108
|
-
}
|
109
|
-
}"}
|
138
|
+
let(:query_string) { "post(123) { comments.first(1) { edges { cursor, node { content } } } }"}
|
110
139
|
|
111
140
|
it 'executes those calls' do
|
112
|
-
assert_equal
|
141
|
+
assert_equal result, {
|
113
142
|
"123" => {
|
114
143
|
"comments" => {
|
115
144
|
"edges" => [
|
116
|
-
{
|
117
|
-
"cursor" => "444",
|
118
|
-
"node" => {
|
119
|
-
"content" => "I agree"
|
120
|
-
}
|
121
|
-
}
|
145
|
+
{ "cursor" => "444", "node" => { "content" => "I agree"} }
|
122
146
|
]
|
123
|
-
|
124
|
-
}
|
125
|
-
}
|
147
|
+
}}}
|
126
148
|
end
|
127
149
|
end
|
128
150
|
|
129
151
|
describe 'when making DEEP calls on a collection' do
|
130
|
-
let(:query_string) { "post(123) {
|
131
|
-
comments.after(444).first(1) {
|
152
|
+
let(:query_string) { "post(123) { comments.after(444).first(1) {
|
132
153
|
edges { cursor, node { content } }
|
133
|
-
}
|
134
|
-
}"}
|
154
|
+
}}"}
|
135
155
|
|
136
156
|
it 'executes those calls' do
|
137
|
-
assert_equal
|
157
|
+
assert_equal result, {
|
138
158
|
"123" => {
|
139
159
|
"comments" => {
|
140
160
|
"edges" => [
|
141
161
|
{
|
142
162
|
"cursor" => "445",
|
143
|
-
"node" => {
|
144
|
-
"content" => "I disagree"
|
145
|
-
}
|
163
|
+
"node" => { "content" => "I disagree"}
|
146
164
|
}
|
147
165
|
]
|
148
|
-
|
149
|
-
}
|
150
|
-
}
|
166
|
+
}}}
|
151
167
|
end
|
152
168
|
end
|
169
|
+
|
170
|
+
describe 'when requesting fields at collection-level' do
|
171
|
+
let(:query_string) { "post(123) { comments { average_rating } }"}
|
172
|
+
|
173
|
+
it 'executes those calls' do
|
174
|
+
assert_equal result, { "123" => { "comments" => { "average_rating" => 3 } } }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'when making calls on node fields' do
|
179
|
+
let(:query_string) { "post(123) { comments { edges { node { letters.from(3).for(3) }} } }"}
|
180
|
+
|
181
|
+
it 'makes calls on the fields' do
|
182
|
+
assert_equal ["gre", "isa"], result["123"]["comments"]["edges"].map {|e| e["node"]["letters"] }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'when requesting collection-level fields that dont exist' do
|
187
|
+
let(:query_string) { "post(123) { comments { bogus_field } }"}
|
188
|
+
|
189
|
+
it 'raises FieldNotDefined' do
|
190
|
+
assert_raises(GraphQL::FieldNotDefinedError) { query.as_result }
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe 'when requesting fields on a related object' do
|
196
|
+
let(:query_string) { "comment(444) { post { title } }"}
|
197
|
+
it 'finds fields on that object' do
|
198
|
+
assert_equal "My great post", result["444"]["post"]["title"]
|
199
|
+
end
|
200
|
+
|
201
|
+
describe 'when the object doesnt exist' do
|
202
|
+
before do
|
203
|
+
Post.all.map(&:destroy)
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'blows_up' do # what _should_ this do?
|
207
|
+
assert_raises(RuntimeError) { result }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe 'when edge classes were named explicitly' do
|
213
|
+
let(:query_string) { "post(123) { likes { any, edges { node { id } } } }"}
|
214
|
+
|
215
|
+
it 'gets node values' do
|
216
|
+
assert_equal ["991991","992992"], result["123"]["likes"]["edges"].map {|e| e["node"]["id"] }
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'gets edge values' do
|
220
|
+
assert_equal true, result["123"]["likes"]["any"]
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#context' do
|
225
|
+
let(:query_string) { "context() { person_name }"}
|
226
|
+
|
227
|
+
it 'is accessible inside nodes' do
|
228
|
+
assert_equal({"context" => {"person_name" => "Han Solo"}}, result)
|
229
|
+
end
|
230
|
+
|
231
|
+
describe 'inside edges' do
|
232
|
+
let(:query_string) { "post(123) { comments { viewer_name_length } }"}
|
233
|
+
it 'is accessible' do
|
234
|
+
assert_equal 8, result["123"]["comments"]["viewer_name_length"]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe 'parsing error' do
|
240
|
+
let(:query_string) { "\n\n<< bogus >>"}
|
241
|
+
|
242
|
+
it 'raises SyntaxError' do
|
243
|
+
assert_raises(GraphQL::SyntaxError) { result }
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'contains line an character number' do
|
247
|
+
err = assert_raises(GraphQL::SyntaxError) { result }
|
248
|
+
assert_match(/1, 1/, err.to_s)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'contains sample of text' do
|
252
|
+
err = assert_raises(GraphQL::SyntaxError) { result }
|
253
|
+
assert_includes(err.to_s, "<< bogus >>")
|
254
|
+
end
|
153
255
|
end
|
154
256
|
end
|