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 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