graphql-relay 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 510d5fdf6df3443f504f9f3500a72c123e66af12
4
- data.tar.gz: 47490930231ee88bf559225d5ca1bcfdb660d537
3
+ metadata.gz: 3cb85e8ce09b47b2744246ed62e75c66571da8aa
4
+ data.tar.gz: 5aa23b9143e8d18a668346bc52f9c7da6dd7f12f
5
5
  SHA512:
6
- metadata.gz: 4b0be8853c488bd84622a2c69bbaf5a0ff8b18c5bac9cd34dc57a2b9ed0d6368f5e16b0b12d9779a104b3472fa407a94a382173ffa8d27258152fed26c79d416
7
- data.tar.gz: 445cfec2d8da0f8b5b57c580036e0621453e724313619783212a402542cb10ff161a45223d6f8968b709ed5f140624396c324c6fb54924f423ce2b4d32116825
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)`. The resulting `NodeIdentification` object is in your schema _and_ internally by `GraphQL::Relay`.
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
@@ -2,6 +2,7 @@ require 'base64'
2
2
  require 'graphql'
3
3
  # MONKEY PATCHES 😬
4
4
  require 'graphql/relay/monkey_patches/base_type'
5
+ require 'graphql/relay/monkey_patches/schema'
5
6
 
6
7
  require 'graphql/relay/define'
7
8
  require 'graphql/relay/global_node_identification'
@@ -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"
@@ -14,7 +14,7 @@ module GraphQL
14
14
  obj.edge_nodes.map { |item| edge_class.new(item, obj) }
15
15
  }
16
16
  end
17
- field :pageInfo, PageInfo, property: :page_info
17
+ field :pageInfo, !PageInfo, property: :page_info
18
18
  block && instance_eval(&block)
19
19
  end
20
20
 
@@ -2,8 +2,8 @@ module GraphQL
2
2
  module Relay
3
3
  module Define
4
4
  module AssignConnection
5
- def self.call(type_defn, name, type = nil, desc = nil, property: nil, max_page_size: nil, &block)
6
- underlying_field = GraphQL::Define::AssignObjectField.call(type_defn, name, type, desc, property: property, &block)
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
- GraphQL::Relay::GlobalNodeIdentification.to_global_id(type_name, obj.public_send(property))
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
- attr_accessor :description
13
+ lazy_defined_attr_accessor :description
14
14
 
15
15
  class << self
16
- attr_accessor :instance, :id_separator
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
- ident.object_from_id(args[:id], ctx)
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
@@ -0,0 +1,3 @@
1
+ class GraphQL::Schema
2
+ attr_accessor :node_identification
3
+ end
@@ -54,8 +54,8 @@ module GraphQL
54
54
  input_field: GraphQL::Define::AssignArgument,
55
55
  return_field: GraphQL::Define::AssignObjectField,
56
56
  )
57
- attr_accessor :name, :description
58
- attr_reader :fields, :arguments
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
@@ -1,5 +1,5 @@
1
1
  module GraphQL
2
2
  module Relay
3
- VERSION = "0.11.2"
3
+ VERSION = "0.12.0"
4
4
  end
5
5
  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) { NodeIdentification }
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
- before do
81
- @previous_global_node_id = GraphQL::Relay::GlobalNodeIdentification.instance
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
- GraphQL::Relay::GlobalNodeIdentification.instance = @previous_global_node_id
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!", @new_node_id.field.description
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 = GraphQL::Relay::GlobalNodeIdentification.instance.type_from_object(123)
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
- GraphQL::Relay::GlobalNodeIdentification.instance.type_from_object(:test_error)
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 fileds on the connection type' do
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 fileds on the connection type' do
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 -> (id, ctx) do
11
- # In a normal app, you could call `from_global_id` on your defined object
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 do
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.11.2
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-06 00:00:00.000000000 Z
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.13'
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.13'
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