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
data/spec/graphql/query_spec.rb
CHANGED
@@ -124,6 +124,16 @@ describe GraphQL::Query do
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
it "fails to execute a query containing a type definition" do
|
128
|
+
query_string = '
|
129
|
+
{ root }
|
130
|
+
|
131
|
+
type Query { foo: String }
|
132
|
+
'
|
133
|
+
exc = assert_raises(GraphQL::ExecutionError) { GraphQL::Query.new(schema, query_string) }
|
134
|
+
assert_equal "GraphQL query cannot contain a schema definition", exc.message
|
135
|
+
end
|
136
|
+
|
127
137
|
it "uses root_value as the object for the root type" do
|
128
138
|
result = GraphQL::Query.new(schema, '{ root }', root_value: "I am root").result
|
129
139
|
assert_equal 'I am root', result.fetch('data').fetch('root')
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Relay::ArrayConnection do
|
4
|
+
def get_names(result)
|
5
|
+
ships = result["data"]["rebels"]["ships"]["edges"]
|
6
|
+
names = ships.map { |e| e["node"]["name"] }
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_last_cursor(result)
|
10
|
+
result["data"]["rebels"]["ships"]["edges"].last["cursor"]
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "results" do
|
14
|
+
let(:query_string) {%|
|
15
|
+
query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
|
16
|
+
rebels {
|
17
|
+
ships(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) {
|
18
|
+
edges {
|
19
|
+
cursor
|
20
|
+
node {
|
21
|
+
name
|
22
|
+
}
|
23
|
+
}
|
24
|
+
pageInfo {
|
25
|
+
hasNextPage
|
26
|
+
hasPreviousPage
|
27
|
+
startCursor
|
28
|
+
endCursor
|
29
|
+
}
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|}
|
34
|
+
|
35
|
+
it 'limits the result' do
|
36
|
+
result = query(query_string, "first" => 2)
|
37
|
+
number_of_ships = get_names(result).length
|
38
|
+
assert_equal(2, number_of_ships)
|
39
|
+
assert_equal(true, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
40
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
41
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
42
|
+
assert_equal("Mg==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
43
|
+
|
44
|
+
result = query(query_string, "first" => 3)
|
45
|
+
number_of_ships = get_names(result).length
|
46
|
+
assert_equal(3, number_of_ships)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'provides pageInfo' do
|
50
|
+
result = query(query_string, "first" => 2)
|
51
|
+
assert_equal(true, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
52
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
53
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
54
|
+
assert_equal("Mg==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
55
|
+
|
56
|
+
result = query(query_string, "first" => 100)
|
57
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
58
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
59
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
60
|
+
assert_equal("NQ==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'slices the result' do
|
64
|
+
result = query(query_string, "first" => 1)
|
65
|
+
assert_equal(["X-Wing"], get_names(result))
|
66
|
+
|
67
|
+
# After the last result, find the next 2:
|
68
|
+
last_cursor = get_last_cursor(result)
|
69
|
+
|
70
|
+
result = query(query_string, "after" => last_cursor, "first" => 2)
|
71
|
+
assert_equal(["Y-Wing", "A-Wing"], get_names(result))
|
72
|
+
|
73
|
+
# After the last result, find the next 2:
|
74
|
+
last_cursor = get_last_cursor(result)
|
75
|
+
|
76
|
+
result = query(query_string, "after" => last_cursor, "first" => 2)
|
77
|
+
assert_equal(["Millenium Falcon", "Home One"], get_names(result))
|
78
|
+
|
79
|
+
result = query(query_string, "before" => last_cursor, "last" => 2)
|
80
|
+
assert_equal(["X-Wing", "Y-Wing"], get_names(result))
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'applies custom arguments' do
|
84
|
+
result = query(query_string, "nameIncludes" => "Wing", "first" => 2)
|
85
|
+
names = get_names(result)
|
86
|
+
assert_equal(2, names.length)
|
87
|
+
|
88
|
+
after = get_last_cursor(result)
|
89
|
+
result = query(query_string, "nameIncludes" => "Wing", "after" => after, "first" => 3)
|
90
|
+
names = get_names(result)
|
91
|
+
assert_equal(1, names.length)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'works without first/last/after/before' do
|
95
|
+
result = query(query_string)
|
96
|
+
|
97
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
98
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
99
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
100
|
+
assert_equal("NQ==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
101
|
+
assert_equal(5, result["data"]["rebels"]["ships"]["edges"].length)
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "applying max_page_size" do
|
105
|
+
def get_names(result)
|
106
|
+
result["data"]["rebels"]["bases"]["edges"].map { |e| e["node"]["name"] }
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_page_info(result)
|
110
|
+
result["data"]["rebels"]["bases"]["pageInfo"]
|
111
|
+
end
|
112
|
+
|
113
|
+
let(:query_string) {%|
|
114
|
+
query getShips($first: Int, $after: String, $last: Int, $before: String){
|
115
|
+
rebels {
|
116
|
+
bases: basesWithMaxLimitArray(first: $first, after: $after, last: $last, before: $before) {
|
117
|
+
edges {
|
118
|
+
cursor
|
119
|
+
node {
|
120
|
+
name
|
121
|
+
}
|
122
|
+
}
|
123
|
+
pageInfo {
|
124
|
+
hasNextPage
|
125
|
+
hasPreviousPage
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|}
|
131
|
+
|
132
|
+
it "applies to queries by `first`" do
|
133
|
+
result = query(query_string, "first" => 100)
|
134
|
+
assert_equal(["Yavin", "Echo Base"], get_names(result))
|
135
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
136
|
+
|
137
|
+
# Max page size is applied _without_ `first`, also
|
138
|
+
result = query(query_string)
|
139
|
+
assert_equal(["Yavin", "Echo Base"], get_names(result))
|
140
|
+
assert_equal(false, get_page_info(result)["hasNextPage"], "hasNextPage is false when first is not specified")
|
141
|
+
end
|
142
|
+
|
143
|
+
it "applies to queries by `last`" do
|
144
|
+
last_cursor = "Ng=="
|
145
|
+
second_to_last_two_names = ["Death Star", "Shield Generator"]
|
146
|
+
result = query(query_string, "last" => 100, "before" => last_cursor)
|
147
|
+
assert_equal(second_to_last_two_names, get_names(result))
|
148
|
+
assert_equal(true, get_page_info(result)["hasPreviousPage"])
|
149
|
+
|
150
|
+
result = query(query_string, "before" => last_cursor)
|
151
|
+
assert_equal(second_to_last_two_names, get_names(result))
|
152
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"], "hasPreviousPage is false when last is not specified")
|
153
|
+
|
154
|
+
third_cursor = "Mw=="
|
155
|
+
first_and_second_names = ["Yavin", "Echo Base"]
|
156
|
+
result = query(query_string, "last" => 100, "before" => third_cursor)
|
157
|
+
assert_equal(first_and_second_names, get_names(result))
|
158
|
+
|
159
|
+
result = query(query_string, "before" => third_cursor)
|
160
|
+
assert_equal(first_and_second_names, get_names(result))
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::Relay::ConnectionType do
|
4
|
+
describe ".create_type" do
|
5
|
+
describe "connections with custom Edge classes / EdgeTypes" do
|
6
|
+
let(:query_string) {%|
|
7
|
+
{
|
8
|
+
rebels {
|
9
|
+
basesWithCustomEdge {
|
10
|
+
totalCountTimes100
|
11
|
+
edges {
|
12
|
+
upcasedName
|
13
|
+
upcasedParentName
|
14
|
+
edgeClassName
|
15
|
+
node {
|
16
|
+
name
|
17
|
+
}
|
18
|
+
cursor
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|}
|
24
|
+
|
25
|
+
it "uses the custom edge and custom connection" do
|
26
|
+
result = query(query_string)
|
27
|
+
bases = result["data"]["rebels"]["basesWithCustomEdge"]
|
28
|
+
assert_equal 300, bases["totalCountTimes100"]
|
29
|
+
assert_equal ["YAVIN", "ECHO BASE", "SECRET HIDEOUT"] , bases["edges"].map { |e| e["upcasedName"] }
|
30
|
+
assert_equal ["Yavin", "Echo Base", "Secret Hideout"] , bases["edges"].map { |e| e["node"]["name"] }
|
31
|
+
assert_equal ["CustomBaseEdge"] , bases["edges"].map { |e| e["edgeClassName"] }.uniq
|
32
|
+
upcased_rebels_name = "ALLIANCE TO RESTORE THE REPUBLIC"
|
33
|
+
assert_equal [upcased_rebels_name] , bases["edges"].map { |e| e["upcasedParentName"] }.uniq
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Relay::GlobalNodeIdentification do
|
4
|
+
let(:node_identification) { StarWarsSchema.node_identification }
|
5
|
+
describe 'NodeField' do
|
6
|
+
it 'finds objects by id' do
|
7
|
+
global_id = node_identification.to_global_id("Faction", "1")
|
8
|
+
result = query(%|{
|
9
|
+
node(id: "#{global_id}") {
|
10
|
+
id,
|
11
|
+
... on Faction {
|
12
|
+
name
|
13
|
+
ships(first: 1) {
|
14
|
+
edges {
|
15
|
+
node {
|
16
|
+
name
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
}|)
|
23
|
+
expected = {"data" => {
|
24
|
+
"node"=>{
|
25
|
+
"id"=>"RmFjdGlvbi0x",
|
26
|
+
"name"=>"Alliance to Restore the Republic",
|
27
|
+
"ships"=>{
|
28
|
+
"edges"=>[
|
29
|
+
{"node"=>{
|
30
|
+
"name" => "X-Wing"
|
31
|
+
}
|
32
|
+
}
|
33
|
+
]
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}}
|
37
|
+
assert_equal(expected, result)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
after do
|
42
|
+
# Set the id_separator back to it's default after each spec, since some of
|
43
|
+
# them change it at runtime
|
44
|
+
GraphQL::Relay::GlobalNodeIdentification.id_separator = "-"
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'id_separator' do
|
48
|
+
it "allows you to change it at runtime" do
|
49
|
+
GraphQL::Relay::GlobalNodeIdentification.id_separator = "-zomg-"
|
50
|
+
|
51
|
+
assert_equal("-zomg-", GraphQL::Relay::GlobalNodeIdentification.id_separator)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'to_global_id / from_global_id ' do
|
56
|
+
it 'Converts typename and ID to and from ID' do
|
57
|
+
global_id = node_identification.to_global_id("SomeType", 123)
|
58
|
+
type_name, id = node_identification.from_global_id(global_id)
|
59
|
+
assert_equal("SomeType", type_name)
|
60
|
+
assert_equal("123", id)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "allows you to change the id_separator" do
|
64
|
+
GraphQL::Relay::GlobalNodeIdentification.id_separator = "---"
|
65
|
+
|
66
|
+
global_id = node_identification.to_global_id("Type-With-UUID", "250cda0e-a89d-41cf-99e1-2872d89f1100")
|
67
|
+
type_name, id = node_identification.from_global_id(global_id)
|
68
|
+
assert_equal("Type-With-UUID", type_name)
|
69
|
+
assert_equal("250cda0e-a89d-41cf-99e1-2872d89f1100", id)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "raises an error if you try and use a reserved character in the ID" do
|
73
|
+
err = assert_raises(RuntimeError) {
|
74
|
+
node_identification.to_global_id("Best-Thing", "234")
|
75
|
+
}
|
76
|
+
assert_includes err.message, "to_global_id(Best-Thing, 234) contains reserved characters `-`"
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "custom definitions" do
|
80
|
+
let(:custom_node_identification) {
|
81
|
+
ident = GraphQL::Relay::GlobalNodeIdentification.define do
|
82
|
+
to_global_id -> (type_name, id) {
|
83
|
+
"#{type_name}/#{id}"
|
84
|
+
}
|
85
|
+
|
86
|
+
from_global_id -> (global_id) {
|
87
|
+
global_id.split("/")
|
88
|
+
}
|
89
|
+
|
90
|
+
object_from_id -> (node_id, ctx) do
|
91
|
+
type_name, id = ident.from_global_id(node_id)
|
92
|
+
STAR_WARS_DATA[type_name][id]
|
93
|
+
end
|
94
|
+
|
95
|
+
description "Hello, World!"
|
96
|
+
end
|
97
|
+
}
|
98
|
+
|
99
|
+
before do
|
100
|
+
@prev_node_identification = StarWarsSchema.node_identification
|
101
|
+
StarWarsSchema.node_identification = custom_node_identification
|
102
|
+
end
|
103
|
+
|
104
|
+
after do
|
105
|
+
StarWarsSchema.node_identification = @prev_node_identification
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "generating IDs" do
|
109
|
+
it "Applies custom-defined ID generation" do
|
110
|
+
result = query(%| { largestBase { id } }|)
|
111
|
+
generated_id = result["data"]["largestBase"]["id"]
|
112
|
+
assert_equal "Base/3", generated_id
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "fetching by ID" do
|
117
|
+
it "Deconstructs the ID by the custom proc" do
|
118
|
+
result = query(%| { node(id: "Base/1") { ... on Base { name } } }|)
|
119
|
+
base_name = result["data"]["node"]["name"]
|
120
|
+
assert_equal "Yavin", base_name
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "setting a description" do
|
125
|
+
it "allows you to set a description" do
|
126
|
+
assert_equal "Hello, World!", custom_node_identification.field.description
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "type_from_object" do
|
133
|
+
describe "when the return value is nil" do
|
134
|
+
it "returns nil" do
|
135
|
+
result = node_identification.type_from_object(123)
|
136
|
+
assert_equal(nil, result)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "when the return value is not a BaseType" do
|
141
|
+
it "raises an error " do
|
142
|
+
err = assert_raises(RuntimeError) {
|
143
|
+
node_identification.type_from_object(:test_error)
|
144
|
+
}
|
145
|
+
assert_includes err.message, "not_a_type (Symbol)"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GraphQL::Relay::Mutation do
|
4
|
+
let(:query_string) {%|
|
5
|
+
mutation addBagel($clientMutationId: String) {
|
6
|
+
introduceShip(input: {shipName: "Bagel", factionId: "1", clientMutationId: $clientMutationId}) {
|
7
|
+
clientMutationId
|
8
|
+
ship { name, id }
|
9
|
+
faction { name }
|
10
|
+
}
|
11
|
+
}
|
12
|
+
|}
|
13
|
+
let(:introspect) {%|
|
14
|
+
{
|
15
|
+
__schema {
|
16
|
+
types { name, fields { name } }
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|}
|
20
|
+
|
21
|
+
after do
|
22
|
+
STAR_WARS_DATA["Ship"].delete("9")
|
23
|
+
STAR_WARS_DATA["Faction"]["1"]["ships"].delete("9")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "returns the result & clientMutationId" do
|
27
|
+
result = query(query_string, "clientMutationId" => "1234")
|
28
|
+
expected = {"data" => {
|
29
|
+
"introduceShip" => {
|
30
|
+
"clientMutationId" => "1234",
|
31
|
+
"ship" => {
|
32
|
+
"name" => "Bagel",
|
33
|
+
"id" => NodeIdentification.to_global_id("Ship", "9"),
|
34
|
+
},
|
35
|
+
"faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
|
36
|
+
}
|
37
|
+
}}
|
38
|
+
assert_equal(expected, result)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "doesn't require a clientMutationId to perform mutations" do
|
42
|
+
result = query(query_string)
|
43
|
+
expected = {"data" => {
|
44
|
+
"introduceShip" => {
|
45
|
+
"clientMutationId" => nil,
|
46
|
+
"ship" => {
|
47
|
+
"name" => "Bagel",
|
48
|
+
"id" => NodeIdentification.to_global_id("Ship", "9"),
|
49
|
+
},
|
50
|
+
"faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
|
51
|
+
}
|
52
|
+
}}
|
53
|
+
assert_equal(expected, result)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::Relay::PageInfo do
|
4
|
+
def get_page_info(result)
|
5
|
+
result["data"]["empire"]["bases"]["pageInfo"]
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_first_cursor(result)
|
9
|
+
result["data"]["empire"]["bases"]["edges"].first["cursor"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_last_cursor(result)
|
13
|
+
result["data"]["empire"]["bases"]["edges"].last["cursor"]
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:cursor_of_last_base) {
|
17
|
+
result = query(query_string, "first" => 100)
|
18
|
+
last_cursor = get_last_cursor(result)
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:query_string) {%|
|
22
|
+
query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){
|
23
|
+
empire {
|
24
|
+
bases(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) {
|
25
|
+
edges {
|
26
|
+
cursor
|
27
|
+
}
|
28
|
+
pageInfo {
|
29
|
+
hasNextPage
|
30
|
+
hasPreviousPage
|
31
|
+
startCursor
|
32
|
+
endCursor
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|}
|
38
|
+
|
39
|
+
describe 'hasNextPage / hasPreviousPage' do
|
40
|
+
it "hasNextPage is true if there are more items" do
|
41
|
+
result = query(query_string, "first" => 2)
|
42
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
43
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"], "hasPreviousPage is false if 'last' is missing")
|
44
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
45
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
46
|
+
|
47
|
+
last_cursor = get_last_cursor(result)
|
48
|
+
result = query(query_string, "first" => 100, "after" => last_cursor)
|
49
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
50
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
51
|
+
assert_equal("Mw==", get_page_info(result)["startCursor"])
|
52
|
+
assert_equal("Mw==", get_page_info(result)["endCursor"])
|
53
|
+
end
|
54
|
+
|
55
|
+
it "hasPreviousPage if there are more items" do
|
56
|
+
result = query(query_string, "last" => 100, "before" => cursor_of_last_base)
|
57
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
58
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
59
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
60
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
61
|
+
|
62
|
+
result = query(query_string, "last" => 1, "before" => cursor_of_last_base)
|
63
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
64
|
+
assert_equal(true, get_page_info(result)["hasPreviousPage"])
|
65
|
+
assert_equal("Mg==", get_page_info(result)["startCursor"])
|
66
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
67
|
+
end
|
68
|
+
|
69
|
+
it "has both if first and last are present" do
|
70
|
+
result = query(query_string, "last" => 1, "first" => 1, "before" => cursor_of_last_base)
|
71
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
72
|
+
assert_equal(true, get_page_info(result)["hasPreviousPage"])
|
73
|
+
assert_equal("Mg==", get_page_info(result)["startCursor"])
|
74
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
75
|
+
end
|
76
|
+
|
77
|
+
it "startCursor and endCursor are the cursors of the first and last edge" do
|
78
|
+
result = query(query_string, "first" => 2)
|
79
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
80
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
81
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
82
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
83
|
+
assert_equal("MQ==", get_first_cursor(result))
|
84
|
+
assert_equal("Mg==", get_last_cursor(result))
|
85
|
+
|
86
|
+
result = query(query_string, "first" => 1, "after" => get_page_info(result)["endCursor"])
|
87
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
88
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
89
|
+
assert_equal("Mw==", get_page_info(result)["startCursor"])
|
90
|
+
assert_equal("Mw==", get_page_info(result)["endCursor"])
|
91
|
+
assert_equal("Mw==", get_first_cursor(result))
|
92
|
+
assert_equal("Mw==", get_last_cursor(result))
|
93
|
+
|
94
|
+
result = query(query_string, "last" => 1, "before" => get_page_info(result)["endCursor"])
|
95
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
96
|
+
assert_equal(true, get_page_info(result)["hasPreviousPage"])
|
97
|
+
assert_equal("Mg==", get_page_info(result)["startCursor"])
|
98
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
99
|
+
assert_equal("Mg==", get_first_cursor(result))
|
100
|
+
assert_equal("Mg==", get_last_cursor(result))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
end
|