graphql-relay 0.11.2 → 0.12.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/README.md +16 -3
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/relay/base_connection.rb +18 -0
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/define.rb +2 -2
- data/lib/graphql/relay/global_id_field.rb +1 -1
- data/lib/graphql/relay/global_node_identification.rb +13 -14
- data/lib/graphql/relay/monkey_patches/schema.rb +3 -0
- data/lib/graphql/relay/mutation.rb +6 -2
- data/lib/graphql/relay/page_info.rb +2 -0
- data/lib/graphql/relay/version.rb +1 -1
- data/spec/graphql/relay/array_connection_spec.rb +15 -0
- data/spec/graphql/relay/global_node_identification_spec.rb +17 -25
- data/spec/graphql/relay/page_info_spec.rb +42 -1
- data/spec/graphql/relay/relation_connection_spec.rb +48 -2
- data/spec/support/star_wars_schema.rb +17 -19
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cb85e8ce09b47b2744246ed62e75c66571da8aa
|
4
|
+
data.tar.gz: 5aa23b9143e8d18a668346bc52f9c7da6dd7f12f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc3fda7351589e2769e908adcd6afb436f4ca94f917cd6815e539125e72b133e89b199ba581e110780fcdea1ad22713422be3bbb29a961fae4c220e90a24a5f0
|
7
|
+
data.tar.gz: 86b5a015dddec76252da2aa70d1f33970bf343fb10b759e3f08a0ab8b94ce71428e97f9a1c2419c17267f88fd1c3cf0a9ecd4fec73d23c6dbd9c00991afe0143
|
data/README.md
CHANGED
@@ -34,7 +34,9 @@ Global ids (or UUIDs) provide refetching & global identification for Relay.
|
|
34
34
|
|
35
35
|
#### UUID Lookup
|
36
36
|
|
37
|
-
Use `GraphQL::Relay::GlobalNodeIdentification` helper by defining `object_from_id(global_id, ctx)` & `type_from_object(object)`.
|
37
|
+
Use `GraphQL::Relay::GlobalNodeIdentification` helper by defining `object_from_id(global_id, ctx)` & `type_from_object(object)`. Then, assign the result to `Schema#node_identification` so that it can be used for query execution.
|
38
|
+
|
39
|
+
For example, define a node identification helper:
|
38
40
|
|
39
41
|
|
40
42
|
```ruby
|
@@ -59,6 +61,14 @@ NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
|
59
61
|
end
|
60
62
|
```
|
61
63
|
|
64
|
+
Then assign it to the schema:
|
65
|
+
|
66
|
+
```ruby
|
67
|
+
MySchema = GraphQL::Schema.new(...)
|
68
|
+
# Assign your node identification helper:
|
69
|
+
MySchema.node_identification = NodeIdentification
|
70
|
+
```
|
71
|
+
|
62
72
|
#### UUID fields
|
63
73
|
|
64
74
|
ObjectTypes in your schema should implement `NodeIdentification.interface` with the `global_id_field` helper, for example:
|
@@ -107,9 +117,13 @@ NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
|
107
117
|
type_name = id_parts[0]
|
108
118
|
id = id_parts[1]
|
109
119
|
# Return *both*:
|
110
|
-
type_name, id
|
120
|
+
[type_name, id]
|
111
121
|
}
|
112
122
|
end
|
123
|
+
|
124
|
+
# ...
|
125
|
+
|
126
|
+
MySchema.node_identification = NodeIdentification
|
113
127
|
```
|
114
128
|
|
115
129
|
`graphql-relay` will use those procs for interacting with global ids.
|
@@ -443,7 +457,6 @@ The resolve proc:
|
|
443
457
|
## Todo
|
444
458
|
|
445
459
|
- `GlobalNodeIdentification.to_global_id` should receive the type name and _object_, not `id`. (Or, maintain the "`type_name, id` in, `type_name, id` out" pattern?)
|
446
|
-
- Make GlobalId a property of the schema, not a global
|
447
460
|
- Reduce duplication in ArrayConnection / RelationConnection
|
448
461
|
- Improve API for creating edges (better RANGE_ADD support)
|
449
462
|
- If the new edge isn't a member of the connection's objects, raise a nice error
|
data/lib/graphql/relay.rb
CHANGED
@@ -101,6 +101,24 @@ module GraphQL
|
|
101
101
|
!!(last && sliced_nodes.count > last)
|
102
102
|
end
|
103
103
|
|
104
|
+
# Used by `pageInfo`
|
105
|
+
def start_cursor
|
106
|
+
if start_node = (respond_to?(:paged_nodes_array, true) ? paged_nodes_array : paged_nodes).first
|
107
|
+
return cursor_from_node(start_node)
|
108
|
+
else
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Used by `pageInfo`
|
114
|
+
def end_cursor
|
115
|
+
if end_node = (respond_to?(:paged_nodes_array, true) ? paged_nodes_array : paged_nodes).last
|
116
|
+
return cursor_from_node(end_node)
|
117
|
+
else
|
118
|
+
return nil
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
104
122
|
# An opaque operation which returns a connection-specific cursor.
|
105
123
|
def cursor_from_node(object)
|
106
124
|
raise NotImplementedError, "must return a cursor for this object/connection pair"
|
data/lib/graphql/relay/define.rb
CHANGED
@@ -2,8 +2,8 @@ module GraphQL
|
|
2
2
|
module Relay
|
3
3
|
module Define
|
4
4
|
module AssignConnection
|
5
|
-
def self.call(type_defn,
|
6
|
-
underlying_field = GraphQL::Define::AssignObjectField.call(type_defn,
|
5
|
+
def self.call(type_defn, *field_args, max_page_size: nil, **field_kwargs, &field_block)
|
6
|
+
underlying_field = GraphQL::Define::AssignObjectField.call(type_defn, *field_args, **field_kwargs, &field_block)
|
7
7
|
connection_field = GraphQL::Relay::ConnectionField.create(underlying_field, max_page_size: max_page_size)
|
8
8
|
type_defn.fields[name.to_s] = connection_field
|
9
9
|
end
|
@@ -10,7 +10,7 @@ module GraphQL
|
|
10
10
|
self.arguments = {}
|
11
11
|
self.type = !GraphQL::ID_TYPE
|
12
12
|
self.resolve = -> (obj, args, ctx) {
|
13
|
-
|
13
|
+
ctx.query.schema.node_identification.to_global_id(type_name, obj.public_send(property))
|
14
14
|
}
|
15
15
|
end
|
16
16
|
end
|
@@ -10,21 +10,10 @@ module GraphQL
|
|
10
10
|
class GlobalNodeIdentification
|
11
11
|
include GraphQL::Define::InstanceDefinable
|
12
12
|
accepts_definitions(:object_from_id, :type_from_object, :to_global_id, :from_global_id, :description)
|
13
|
-
|
13
|
+
lazy_defined_attr_accessor :description
|
14
14
|
|
15
15
|
class << self
|
16
|
-
attr_accessor :
|
17
|
-
def new(*args, &block)
|
18
|
-
@instance = super
|
19
|
-
end
|
20
|
-
|
21
|
-
def from_global_id(id)
|
22
|
-
instance.from_global_id(id)
|
23
|
-
end
|
24
|
-
|
25
|
-
def to_global_id(type_name, id)
|
26
|
-
instance.to_global_id(type_name, id)
|
27
|
-
end
|
16
|
+
attr_accessor :id_separator
|
28
17
|
end
|
29
18
|
|
30
19
|
self.id_separator = "-"
|
@@ -37,6 +26,7 @@ module GraphQL
|
|
37
26
|
# Returns `NodeInterface`, which all Relay types must implement
|
38
27
|
def interface
|
39
28
|
@interface ||= begin
|
29
|
+
ensure_defined
|
40
30
|
ident = self
|
41
31
|
GraphQL::InterfaceType.define do
|
42
32
|
name "Node"
|
@@ -50,12 +40,13 @@ module GraphQL
|
|
50
40
|
|
51
41
|
# Returns a field for finding objects from a global ID, which Relay needs
|
52
42
|
def field
|
43
|
+
ensure_defined
|
53
44
|
ident = self
|
54
45
|
GraphQL::Field.define do
|
55
46
|
type(ident.interface)
|
56
47
|
argument :id, !types.ID
|
57
48
|
resolve -> (obj, args, ctx) {
|
58
|
-
|
49
|
+
ctx.query.schema.node_identification.object_from_id(args[:id], ctx)
|
59
50
|
}
|
60
51
|
description ident.description
|
61
52
|
end
|
@@ -76,26 +67,31 @@ module GraphQL
|
|
76
67
|
# Create a global ID for type-name & ID
|
77
68
|
# (This is an opaque transform)
|
78
69
|
def to_global_id(type_name, id)
|
70
|
+
ensure_defined
|
79
71
|
@to_global_id_proc.call(type_name, id)
|
80
72
|
end
|
81
73
|
|
82
74
|
def to_global_id=(proc)
|
75
|
+
ensure_defined
|
83
76
|
@to_global_id_proc = proc
|
84
77
|
end
|
85
78
|
|
86
79
|
# Get type-name & ID from global ID
|
87
80
|
# (This reverts the opaque transform)
|
88
81
|
def from_global_id(global_id)
|
82
|
+
ensure_defined
|
89
83
|
@from_global_id_proc.call(global_id)
|
90
84
|
end
|
91
85
|
|
92
86
|
def from_global_id=(proc)
|
87
|
+
ensure_defined
|
93
88
|
@from_global_id_proc = proc
|
94
89
|
end
|
95
90
|
|
96
91
|
# Use the provided config to
|
97
92
|
# get a type for a given object
|
98
93
|
def type_from_object(object)
|
94
|
+
ensure_defined
|
99
95
|
type_result = @type_from_object_proc.call(object)
|
100
96
|
if type_result.nil?
|
101
97
|
nil
|
@@ -108,16 +104,19 @@ module GraphQL
|
|
108
104
|
end
|
109
105
|
|
110
106
|
def type_from_object=(proc)
|
107
|
+
ensure_defined
|
111
108
|
@type_from_object_proc = proc
|
112
109
|
end
|
113
110
|
|
114
111
|
# Use the provided config to
|
115
112
|
# get an object from a UUID
|
116
113
|
def object_from_id(id, ctx)
|
114
|
+
ensure_defined
|
117
115
|
@object_from_id_proc.call(id, ctx)
|
118
116
|
end
|
119
117
|
|
120
118
|
def object_from_id=(proc)
|
119
|
+
ensure_defined
|
121
120
|
@object_from_id_proc = proc
|
122
121
|
end
|
123
122
|
end
|
@@ -54,8 +54,8 @@ module GraphQL
|
|
54
54
|
input_field: GraphQL::Define::AssignArgument,
|
55
55
|
return_field: GraphQL::Define::AssignObjectField,
|
56
56
|
)
|
57
|
-
|
58
|
-
|
57
|
+
lazy_defined_attr_accessor :name, :description
|
58
|
+
lazy_defined_attr_accessor :fields, :arguments
|
59
59
|
|
60
60
|
# For backwards compat, but do we need this separate API?
|
61
61
|
alias :return_fields :fields
|
@@ -67,11 +67,13 @@ module GraphQL
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def resolve=(proc)
|
70
|
+
ensure_defined
|
70
71
|
@resolve_proc = proc
|
71
72
|
end
|
72
73
|
|
73
74
|
def field
|
74
75
|
@field ||= begin
|
76
|
+
ensure_defined
|
75
77
|
field_return_type = self.return_type
|
76
78
|
field_input_type = self.input_type
|
77
79
|
field_resolve_proc = -> (obj, args, ctx){
|
@@ -88,6 +90,7 @@ module GraphQL
|
|
88
90
|
|
89
91
|
def return_type
|
90
92
|
@return_type ||= begin
|
93
|
+
ensure_defined
|
91
94
|
mutation_name = name
|
92
95
|
type_name = "#{mutation_name}Payload"
|
93
96
|
type_fields = return_fields
|
@@ -104,6 +107,7 @@ module GraphQL
|
|
104
107
|
|
105
108
|
def input_type
|
106
109
|
@input_type ||= begin
|
110
|
+
ensure_defined
|
107
111
|
mutation_name = name
|
108
112
|
type_name = "#{mutation_name}Input"
|
109
113
|
type_fields = input_fields
|
@@ -6,6 +6,8 @@ module GraphQL
|
|
6
6
|
description("Metadata about a connection")
|
7
7
|
field :hasNextPage, !types.Boolean, "Indicates if there are more pages to fetch", property: :has_next_page
|
8
8
|
field :hasPreviousPage, !types.Boolean, "Indicates if there are any pages prior to the current page", property: :has_previous_page
|
9
|
+
field :startCursor, types.String, "When paginating backwards, the cursor to continue", property: :start_cursor
|
10
|
+
field :endCursor, types.String, "When paginating forwards, the cursor to continue", property: :end_cursor
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -22,6 +22,9 @@ describe GraphQL::Relay::ArrayConnection do
|
|
22
22
|
}
|
23
23
|
pageInfo {
|
24
24
|
hasNextPage
|
25
|
+
hasPreviousPage
|
26
|
+
startCursor
|
27
|
+
endCursor
|
25
28
|
}
|
26
29
|
}
|
27
30
|
}
|
@@ -33,6 +36,9 @@ describe GraphQL::Relay::ArrayConnection do
|
|
33
36
|
number_of_ships = get_names(result).length
|
34
37
|
assert_equal(2, number_of_ships)
|
35
38
|
assert_equal(true, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
39
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
40
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
41
|
+
assert_equal("Mg==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
36
42
|
|
37
43
|
result = query(query_string, "first" => 3)
|
38
44
|
number_of_ships = get_names(result).length
|
@@ -42,9 +48,15 @@ describe GraphQL::Relay::ArrayConnection do
|
|
42
48
|
it 'provides pageInfo' do
|
43
49
|
result = query(query_string, "first" => 2)
|
44
50
|
assert_equal(true, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
51
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
52
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
53
|
+
assert_equal("Mg==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
45
54
|
|
46
55
|
result = query(query_string, "first" => 100)
|
47
56
|
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
57
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
58
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
59
|
+
assert_equal("NQ==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
48
60
|
end
|
49
61
|
|
50
62
|
it 'slices the result' do
|
@@ -82,6 +94,9 @@ describe GraphQL::Relay::ArrayConnection do
|
|
82
94
|
result = query(query_string)
|
83
95
|
|
84
96
|
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
|
97
|
+
assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
|
98
|
+
assert_equal("MQ==", result["data"]["rebels"]["ships"]["pageInfo"]["startCursor"])
|
99
|
+
assert_equal("NQ==", result["data"]["rebels"]["ships"]["pageInfo"]["endCursor"])
|
85
100
|
assert_equal(5, result["data"]["rebels"]["ships"]["edges"].length)
|
86
101
|
end
|
87
102
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe GraphQL::Relay::GlobalNodeIdentification do
|
4
|
-
let(:node_identification) {
|
4
|
+
let(:node_identification) { StarWarsSchema.node_identification }
|
5
5
|
describe 'NodeField' do
|
6
6
|
it 'finds objects by id' do
|
7
7
|
global_id = node_identification.to_global_id("Faction", "1")
|
@@ -77,9 +77,8 @@ describe GraphQL::Relay::GlobalNodeIdentification do
|
|
77
77
|
end
|
78
78
|
|
79
79
|
describe "custom definitions" do
|
80
|
-
|
81
|
-
|
82
|
-
@new_node_id = GraphQL::Relay::GlobalNodeIdentification.define do
|
80
|
+
let(:custom_node_identification) {
|
81
|
+
ident = GraphQL::Relay::GlobalNodeIdentification.define do
|
83
82
|
to_global_id -> (type_name, id) {
|
84
83
|
"#{type_name}/#{id}"
|
85
84
|
}
|
@@ -88,12 +87,22 @@ describe GraphQL::Relay::GlobalNodeIdentification do
|
|
88
87
|
global_id.split("/")
|
89
88
|
}
|
90
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
|
+
|
91
95
|
description "Hello, World!"
|
92
96
|
end
|
97
|
+
}
|
98
|
+
|
99
|
+
before do
|
100
|
+
@prev_node_identification = StarWarsSchema.node_identification
|
101
|
+
StarWarsSchema.node_identification = custom_node_identification
|
93
102
|
end
|
94
103
|
|
95
104
|
after do
|
96
|
-
|
105
|
+
StarWarsSchema.node_identification = @prev_node_identification
|
97
106
|
end
|
98
107
|
|
99
108
|
describe "generating IDs" do
|
@@ -114,17 +123,16 @@ describe GraphQL::Relay::GlobalNodeIdentification do
|
|
114
123
|
|
115
124
|
describe "setting a description" do
|
116
125
|
it "allows you to set a description" do
|
117
|
-
assert_equal "Hello, World!",
|
126
|
+
assert_equal "Hello, World!", custom_node_identification.field.description
|
118
127
|
end
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
122
131
|
|
123
132
|
describe "type_from_object" do
|
124
|
-
|
125
133
|
describe "when the return value is nil" do
|
126
134
|
it "returns nil" do
|
127
|
-
result =
|
135
|
+
result = node_identification.type_from_object(123)
|
128
136
|
assert_equal(nil, result)
|
129
137
|
end
|
130
138
|
end
|
@@ -132,26 +140,10 @@ describe GraphQL::Relay::GlobalNodeIdentification do
|
|
132
140
|
describe "when the return value is not a BaseType" do
|
133
141
|
it "raises an error " do
|
134
142
|
err = assert_raises(RuntimeError) {
|
135
|
-
|
143
|
+
node_identification.type_from_object(:test_error)
|
136
144
|
}
|
137
145
|
assert_includes err.message, "not_a_type (Symbol)"
|
138
146
|
end
|
139
147
|
end
|
140
148
|
end
|
141
|
-
|
142
|
-
describe 'making a second instance' do
|
143
|
-
before do
|
144
|
-
@first_instance = GraphQL::Relay::GlobalNodeIdentification.instance
|
145
|
-
end
|
146
|
-
|
147
|
-
after do
|
148
|
-
GraphQL::Relay::GlobalNodeIdentification.instance = @first_instance
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'overrides the first instance' do
|
152
|
-
GraphQL::Relay::GlobalNodeIdentification.define {}
|
153
|
-
second_instance = GraphQL::Relay::GlobalNodeIdentification.instance
|
154
|
-
refute_equal(@first_instance, second_instance)
|
155
|
-
end
|
156
|
-
end
|
157
149
|
end
|
@@ -5,6 +5,10 @@ describe GraphQL::Relay::PageInfo do
|
|
5
5
|
result["data"]["empire"]["bases"]["pageInfo"]
|
6
6
|
end
|
7
7
|
|
8
|
+
def get_first_cursor(result)
|
9
|
+
result["data"]["empire"]["bases"]["edges"].first["cursor"]
|
10
|
+
end
|
11
|
+
|
8
12
|
def get_last_cursor(result)
|
9
13
|
result["data"]["empire"]["bases"]["edges"].last["cursor"]
|
10
14
|
end
|
@@ -24,6 +28,8 @@ describe GraphQL::Relay::PageInfo do
|
|
24
28
|
pageInfo {
|
25
29
|
hasNextPage
|
26
30
|
hasPreviousPage
|
31
|
+
startCursor
|
32
|
+
endCursor
|
27
33
|
}
|
28
34
|
}
|
29
35
|
}
|
@@ -35,28 +41,63 @@ describe GraphQL::Relay::PageInfo do
|
|
35
41
|
result = query(query_string, "first" => 2)
|
36
42
|
assert_equal(true, get_page_info(result)["hasNextPage"])
|
37
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"])
|
38
46
|
|
39
47
|
last_cursor = get_last_cursor(result)
|
40
48
|
result = query(query_string, "first" => 100, "after" => last_cursor)
|
41
49
|
assert_equal(false, get_page_info(result)["hasNextPage"])
|
42
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"])
|
43
53
|
end
|
44
54
|
|
45
55
|
it "hasPreviousPage if there are more items" do
|
46
56
|
result = query(query_string, "last" => 100, "before" => cursor_of_last_base)
|
47
57
|
assert_equal(false, get_page_info(result)["hasNextPage"])
|
48
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"])
|
49
61
|
|
50
62
|
result = query(query_string, "last" => 1, "before" => cursor_of_last_base)
|
51
63
|
assert_equal(false, get_page_info(result)["hasNextPage"])
|
52
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"])
|
53
67
|
end
|
54
68
|
|
55
|
-
|
56
69
|
it "has both if first and last are present" do
|
57
70
|
result = query(query_string, "last" => 1, "first" => 1, "before" => cursor_of_last_base)
|
58
71
|
assert_equal(true, get_page_info(result)["hasNextPage"])
|
59
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))
|
60
101
|
end
|
61
102
|
end
|
62
103
|
|
@@ -6,6 +6,14 @@ describe GraphQL::Relay::RelationConnection do
|
|
6
6
|
names = ships.map { |e| e["node"]["name"] }
|
7
7
|
end
|
8
8
|
|
9
|
+
def get_page_info(result)
|
10
|
+
result["data"]["empire"]["bases"]["pageInfo"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_first_cursor(result)
|
14
|
+
result["data"]["empire"]["bases"]["edges"].first["cursor"]
|
15
|
+
end
|
16
|
+
|
9
17
|
def get_last_cursor(result)
|
10
18
|
result["data"]["empire"]["bases"]["edges"].last["cursor"]
|
11
19
|
end
|
@@ -30,6 +38,9 @@ describe GraphQL::Relay::RelationConnection do
|
|
30
38
|
},
|
31
39
|
pageInfo {
|
32
40
|
hasNextPage
|
41
|
+
hasPreviousPage
|
42
|
+
startCursor
|
43
|
+
endCursor
|
33
44
|
}
|
34
45
|
}
|
35
46
|
|}
|
@@ -37,12 +48,24 @@ describe GraphQL::Relay::RelationConnection do
|
|
37
48
|
it 'limits the result' do
|
38
49
|
result = query(query_string, "first" => 2)
|
39
50
|
assert_equal(2, get_names(result).length)
|
51
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
52
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
53
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
54
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
55
|
+
assert_equal("MQ==", get_first_cursor(result))
|
56
|
+
assert_equal("Mg==", get_last_cursor(result))
|
40
57
|
|
41
58
|
result = query(query_string, "first" => 3)
|
42
59
|
assert_equal(3, get_names(result).length)
|
60
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
61
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
62
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
63
|
+
assert_equal("Mw==", get_page_info(result)["endCursor"])
|
64
|
+
assert_equal("MQ==", get_first_cursor(result))
|
65
|
+
assert_equal("Mw==", get_last_cursor(result))
|
43
66
|
end
|
44
67
|
|
45
|
-
it 'provides custom
|
68
|
+
it 'provides custom fields on the connection type' do
|
46
69
|
result = query(query_string, "first" => 2)
|
47
70
|
assert_equal(
|
48
71
|
Base.where(faction_id: 2).count,
|
@@ -200,6 +223,14 @@ describe GraphQL::Relay::RelationConnection do
|
|
200
223
|
names = ships.map { |e| e["node"]["name"] }
|
201
224
|
end
|
202
225
|
|
226
|
+
def get_page_info(result)
|
227
|
+
result["data"]["empire"]["basesAsSequelDataset"]["pageInfo"]
|
228
|
+
end
|
229
|
+
|
230
|
+
def get_first_cursor(result)
|
231
|
+
result["data"]["empire"]["basesAsSequelDataset"]["edges"].first["cursor"]
|
232
|
+
end
|
233
|
+
|
203
234
|
def get_last_cursor(result)
|
204
235
|
result["data"]["empire"]["basesAsSequelDataset"]["edges"].last["cursor"]
|
205
236
|
end
|
@@ -224,6 +255,9 @@ describe GraphQL::Relay::RelationConnection do
|
|
224
255
|
},
|
225
256
|
pageInfo {
|
226
257
|
hasNextPage
|
258
|
+
hasPreviousPage
|
259
|
+
startCursor
|
260
|
+
endCursor
|
227
261
|
}
|
228
262
|
}
|
229
263
|
|}
|
@@ -231,12 +265,24 @@ describe GraphQL::Relay::RelationConnection do
|
|
231
265
|
it 'limits the result' do
|
232
266
|
result = query(query_string, "first" => 2)
|
233
267
|
assert_equal(2, get_names(result).length)
|
268
|
+
assert_equal(true, get_page_info(result)["hasNextPage"])
|
269
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
270
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
271
|
+
assert_equal("Mg==", get_page_info(result)["endCursor"])
|
272
|
+
assert_equal("MQ==", get_first_cursor(result))
|
273
|
+
assert_equal("Mg==", get_last_cursor(result))
|
234
274
|
|
235
275
|
result = query(query_string, "first" => 3)
|
236
276
|
assert_equal(3, get_names(result).length)
|
277
|
+
assert_equal(false, get_page_info(result)["hasNextPage"])
|
278
|
+
assert_equal(false, get_page_info(result)["hasPreviousPage"])
|
279
|
+
assert_equal("MQ==", get_page_info(result)["startCursor"])
|
280
|
+
assert_equal("Mw==", get_page_info(result)["endCursor"])
|
281
|
+
assert_equal("MQ==", get_first_cursor(result))
|
282
|
+
assert_equal("Mw==", get_last_cursor(result))
|
237
283
|
end
|
238
284
|
|
239
|
-
it 'provides custom
|
285
|
+
it 'provides custom fields on the connection type' do
|
240
286
|
result = query(query_string, "first" => 2)
|
241
287
|
assert_equal(
|
242
288
|
Base.where(faction_id: 2).count,
|
@@ -7,12 +7,8 @@
|
|
7
7
|
# - an interface for Relay ObjectTypes to implement
|
8
8
|
# See global_node_identification.rb for the full API.
|
9
9
|
NodeIdentification = GraphQL::Relay::GlobalNodeIdentification.define do
|
10
|
-
object_from_id -> (
|
11
|
-
|
12
|
-
# type_name, id = NodeIdentification.from_global_id(id)
|
13
|
-
#
|
14
|
-
# But to support our testing setup, reach for the global:
|
15
|
-
type_name, id = GraphQL::Relay::GlobalNodeIdentification.from_global_id(id)
|
10
|
+
object_from_id -> (node_id, ctx) do
|
11
|
+
type_name, id = NodeIdentification.from_global_id(node_id)
|
16
12
|
STAR_WARS_DATA[type_name][id]
|
17
13
|
end
|
18
14
|
|
@@ -84,25 +80,26 @@ CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBase
|
|
84
80
|
end
|
85
81
|
end
|
86
82
|
|
83
|
+
FactionShipsField = GraphQL::Field.define do
|
84
|
+
# Resolve field should return an Array, the Connection
|
85
|
+
# will do the rest!
|
86
|
+
resolve -> (obj, args, ctx) {
|
87
|
+
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
88
|
+
if args[:nameIncludes]
|
89
|
+
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
90
|
+
end
|
91
|
+
all_ships
|
92
|
+
}
|
93
|
+
# You can define arguments here and use them in the connection
|
94
|
+
argument :nameIncludes, types.String
|
95
|
+
end
|
87
96
|
|
88
97
|
Faction = GraphQL::ObjectType.define do
|
89
98
|
name "Faction"
|
90
99
|
interfaces [NodeIdentification.interface]
|
91
100
|
field :id, field: GraphQL::Relay::GlobalIdField.new("Faction")
|
92
101
|
field :name, types.String
|
93
|
-
connection :ships, Ship.connection_type
|
94
|
-
# Resolve field should return an Array, the Connection
|
95
|
-
# will do the rest!
|
96
|
-
resolve -> (obj, args, ctx) {
|
97
|
-
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
98
|
-
if args[:nameIncludes]
|
99
|
-
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
100
|
-
end
|
101
|
-
all_ships
|
102
|
-
}
|
103
|
-
# You can define arguments here and use them in the connection
|
104
|
-
argument :nameIncludes, types.String
|
105
|
-
end
|
102
|
+
connection :ships, Ship.connection_type, field: FactionShipsField
|
106
103
|
connection :bases, BaseConnectionWithTotalCountType do
|
107
104
|
# Resolve field should return an Array, the Connection
|
108
105
|
# will do the rest!
|
@@ -206,3 +203,4 @@ MutationType = GraphQL::ObjectType.define do
|
|
206
203
|
end
|
207
204
|
|
208
205
|
StarWarsSchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)
|
206
|
+
StarWarsSchema.node_identification = NodeIdentification
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-relay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: graphql
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.17'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0.
|
26
|
+
version: '0.17'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- lib/graphql/relay/global_id_field.rb
|
228
228
|
- lib/graphql/relay/global_node_identification.rb
|
229
229
|
- lib/graphql/relay/monkey_patches/base_type.rb
|
230
|
+
- lib/graphql/relay/monkey_patches/schema.rb
|
230
231
|
- lib/graphql/relay/mutation.rb
|
231
232
|
- lib/graphql/relay/page_info.rb
|
232
233
|
- lib/graphql/relay/range_add.rb
|