graphql-connections 1.0.0 → 1.3.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 +31 -7
- data/lib/graphql/connections/base.rb +2 -2
- data/lib/graphql/connections/chewy.rb +37 -0
- data/lib/graphql/connections/keyset/asc.rb +3 -4
- data/lib/graphql/connections/keyset/desc.rb +3 -4
- data/lib/graphql/connections/primary_key/asc.rb +30 -0
- data/lib/graphql/connections/primary_key/base.rb +107 -0
- data/lib/graphql/connections/primary_key/desc.rb +30 -0
- data/lib/graphql/connections/stable.rb +2 -2
- data/lib/graphql/connections/version.rb +1 -1
- data/lib/graphql/connections.rb +6 -1
- metadata +57 -6
- data/lib/graphql/connections/key_asc.rb +0 -69
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed09e36d3d30a8e2cd939a1b9b39341168478d547e4b0e990798e612f6f83e49
|
4
|
+
data.tar.gz: bc35f6d38dcebf5a97ce0cf69d6540db2712046bddc15f5c1bca0c685115eac4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2408beb9fb3346e5ce7e980b462dc34762d5507bb65eb9c1051672d1dc0b8d33c2807d5113b0837a704ad3311bcbc27baeddb5bd4bb1177e8e748d00ca0f2b98
|
7
|
+
data.tar.gz: 042f6ee93e8fbc8391122ac5546bf18c3be595b2c45504daeff6dbec16240278141ad3e9a5d145e30a14c84e70d37ef03fb242e282c110f41ffd2605ebca81c4
|
data/README.md
CHANGED
@@ -3,10 +3,7 @@
|
|
3
3
|
|
4
4
|
# GraphQL::Connections
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
Implements [Relay specification](https://relay.dev/graphql/connections.htm) for serving stable connections based on column values.
|
9
|
-
If objects are created or destroyed during pagination, the list of items won’t be disrupted.
|
6
|
+
Additional implementations of cursor-based paginations for [GraphQL Ruby](https://graphql-ruby.org/).
|
10
7
|
|
11
8
|
<a href="https://evilmartians.com/?utm_source=graphql-connections">
|
12
9
|
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
@@ -21,6 +18,11 @@ gem "graphql-connections"
|
|
21
18
|
|
22
19
|
## Usage
|
23
20
|
|
21
|
+
### ActiveRecord
|
22
|
+
|
23
|
+
Implements [Relay specification](https://relay.dev/graphql/connections.htm) for serving stable connections based on column values.
|
24
|
+
If objects are created or destroyed during pagination, the list of items won’t be disrupted.
|
25
|
+
|
24
26
|
You can use a stable connection wrapper on a specific field:
|
25
27
|
|
26
28
|
```ruby
|
@@ -57,7 +59,9 @@ Also, you can pass the `:desc` option to reverse the relation:
|
|
57
59
|
GraphQL::Connections::Stable.new(Message.all, keys: %w[name id], desc: true)
|
58
60
|
```
|
59
61
|
|
60
|
-
|
62
|
+
```ruby
|
63
|
+
GraphQL::Connections::Stable.new(Message.all, primary_key: :created_at, desc: true)
|
64
|
+
```
|
61
65
|
|
62
66
|
Also, you can disable opaque cursors by setting `opaque_cursor` param:
|
63
67
|
|
@@ -69,14 +73,34 @@ Or you can apply a stable connection to all Active Record relations returning by
|
|
69
73
|
|
70
74
|
```ruby
|
71
75
|
class ApplicationSchema < GraphQL::Schema
|
72
|
-
use GraphQL::Pagination::Connections
|
73
|
-
|
74
76
|
connections.add(ActiveRecord::Relation, GraphQL::Connections::Stable)
|
75
77
|
end
|
76
78
|
```
|
77
79
|
|
78
80
|
**NOTE:** Don't use stable connections for relations whose ordering is too complicated for cursor generation.
|
79
81
|
|
82
|
+
### Elasticsearch via Chewy
|
83
|
+
|
84
|
+
Register connection for all Chewy requests:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class ApplicationSchema < GraphQL::Schema
|
88
|
+
connections.add(Chewy::Search::Request, GraphQL::Connections::ChewyConnection)
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
And define field like below:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
field :messages, Types::Message.connection_type, null: false
|
96
|
+
|
97
|
+
def messages
|
98
|
+
CitiesIndex.query(match: {name: "Moscow"})
|
99
|
+
end
|
100
|
+
```
|
101
|
+
|
102
|
+
**NOTE:** Using `first` and `last`arguments simultaneously is not supported yet.
|
103
|
+
|
80
104
|
## Development
|
81
105
|
|
82
106
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -22,11 +22,11 @@ module GraphQL
|
|
22
22
|
@nodes ||= limited_relation
|
23
23
|
end
|
24
24
|
|
25
|
-
def has_previous_page
|
25
|
+
def has_previous_page
|
26
26
|
raise NotImplementedError
|
27
27
|
end
|
28
28
|
|
29
|
-
def has_next_page
|
29
|
+
def has_next_page
|
30
30
|
raise NotImplementedError
|
31
31
|
end
|
32
32
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Connections
|
5
|
+
class Chewy < ::GraphQL::Pagination::RelationConnection
|
6
|
+
private
|
7
|
+
|
8
|
+
def load_nodes
|
9
|
+
@nodes ||= limited_nodes.objects
|
10
|
+
end
|
11
|
+
|
12
|
+
def relation_count(relation)
|
13
|
+
offset = relation_offset(relation) || 0
|
14
|
+
limit = relation_limit(relation)
|
15
|
+
count = relation.count - offset
|
16
|
+
|
17
|
+
if limit.nil?
|
18
|
+
count
|
19
|
+
else
|
20
|
+
[count, limit].min
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def relation_limit(relation)
|
25
|
+
relation.send(:raw_limit_value)&.to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
def relation_offset(relation)
|
29
|
+
relation.send(:raw_offset_value)&.to_i
|
30
|
+
end
|
31
|
+
|
32
|
+
def null_relation(relation)
|
33
|
+
relation.none
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -5,7 +5,6 @@ module GraphQL
|
|
5
5
|
module Keyset
|
6
6
|
# Implements keyset pagination by two fields with asc order
|
7
7
|
class Asc < ::GraphQL::Connections::Keyset::Base
|
8
|
-
# rubocop:disable Naming/PredicateName, Metrics/AbcSize, Metrics/MethodLength
|
9
8
|
def has_previous_page
|
10
9
|
if last
|
11
10
|
nodes.any? &&
|
@@ -47,7 +46,7 @@ module GraphQL
|
|
47
46
|
|
48
47
|
private
|
49
48
|
|
50
|
-
def limited_relation
|
49
|
+
def limited_relation
|
51
50
|
scope = sliced_relation
|
52
51
|
nodes = []
|
53
52
|
|
@@ -66,14 +65,14 @@ module GraphQL
|
|
66
65
|
nodes
|
67
66
|
end
|
68
67
|
|
69
|
-
def sliced_relation_after(relation)
|
68
|
+
def sliced_relation_after(relation)
|
70
69
|
relation
|
71
70
|
.where(arel_table[field_key].eq(after_cursor_date))
|
72
71
|
.where(arel_table[primary_key].gt(after_cursor_primary_key))
|
73
72
|
.or(relation.where(arel_table[field_key].gt(after_cursor_date)))
|
74
73
|
end
|
75
74
|
|
76
|
-
def sliced_relation_before(relation)
|
75
|
+
def sliced_relation_before(relation)
|
77
76
|
relation
|
78
77
|
.where(arel_table[field_key].eq(before_cursor_date))
|
79
78
|
.where(arel_table[primary_key].lt(before_cursor_primary_key))
|
@@ -5,7 +5,6 @@ module GraphQL
|
|
5
5
|
module Keyset
|
6
6
|
# Implements keyset pagination by two fields with desc order
|
7
7
|
class Desc < ::GraphQL::Connections::Keyset::Base
|
8
|
-
# rubocop:disable Naming/PredicateName, Metrics/AbcSize, Metrics/MethodLength
|
9
8
|
def has_previous_page
|
10
9
|
if last
|
11
10
|
nodes.any? &&
|
@@ -53,7 +52,7 @@ module GraphQL
|
|
53
52
|
|
54
53
|
private
|
55
54
|
|
56
|
-
def limited_relation
|
55
|
+
def limited_relation
|
57
56
|
scope = sliced_relation
|
58
57
|
nodes = []
|
59
58
|
|
@@ -73,14 +72,14 @@ module GraphQL
|
|
73
72
|
nodes
|
74
73
|
end
|
75
74
|
|
76
|
-
def sliced_relation_after(relation)
|
75
|
+
def sliced_relation_after(relation)
|
77
76
|
relation
|
78
77
|
.where(arel_table[field_key].eq(after_cursor_date))
|
79
78
|
.where(arel_table[primary_key].lt(after_cursor_primary_key))
|
80
79
|
.or(relation.where(arel_table[field_key].lt(after_cursor_date)))
|
81
80
|
end
|
82
81
|
|
83
|
-
def sliced_relation_before(relation)
|
82
|
+
def sliced_relation_before(relation)
|
84
83
|
relation
|
85
84
|
.where(arel_table[field_key].eq(before_cursor_date))
|
86
85
|
.where(arel_table[primary_key].gt(before_cursor_primary_key))
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Connections
|
5
|
+
# Implements pagination by one field with asc order
|
6
|
+
module PrimaryKey
|
7
|
+
class Asc < Base
|
8
|
+
PAGE_COMPARABLE_METHODS = {
|
9
|
+
previous: {query: :lt, cursor: :lteq},
|
10
|
+
next: {query: :gt, cursor: :gteq}
|
11
|
+
}
|
12
|
+
|
13
|
+
SLICED_COMPARABLE_METHODS = {
|
14
|
+
after: :gt,
|
15
|
+
before: :lt
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def first_limited_sorted_table
|
21
|
+
arel_table[primary_key].asc
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_limited_sorted_table
|
25
|
+
arel_table[primary_key].desc
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Connections
|
5
|
+
module PrimaryKey
|
6
|
+
# Base class for PrimaryKey pagination implementations
|
7
|
+
class Base < ::GraphQL::Connections::Base
|
8
|
+
COMPARABLE_METHODS = %i[
|
9
|
+
gt lt lteq gteq
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def initialize(*args, primary_key: nil, **kwargs)
|
13
|
+
@primary_key = primary_key
|
14
|
+
|
15
|
+
super(*args, **kwargs)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_previous_page
|
19
|
+
if last
|
20
|
+
nodes.any? && items_exist?(type: :query, search: nodes.first[primary_key], page_type: :previous)
|
21
|
+
elsif after
|
22
|
+
items_exist?(type: :cursor, search: after_cursor, page_type: :previous)
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_next_page
|
29
|
+
if first
|
30
|
+
items_exist?(type: :query, search: nodes.last[primary_key], page_type: :next)
|
31
|
+
elsif before
|
32
|
+
items_exist?(type: :cursor, search: before_cursor, page_type: :next)
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def cursor_for(item)
|
39
|
+
cursor = serialize(item[primary_key])
|
40
|
+
cursor = encode(cursor) if opaque_cursor
|
41
|
+
cursor
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def page_comparable_method(query_type:, page_type:)
|
47
|
+
self.class::PAGE_COMPARABLE_METHODS.fetch(page_type).fetch(query_type)
|
48
|
+
end
|
49
|
+
|
50
|
+
def items_exist?(type:, search:, page_type:)
|
51
|
+
comparable_method = page_comparable_method(query_type: type, page_type: page_type)
|
52
|
+
|
53
|
+
if COMPARABLE_METHODS.exclude?(comparable_method)
|
54
|
+
raise ArgumentError.new("Unknown #{comparable_method} comparable type. Allowed #{COMPARABLE_METHODS.join(", ")}")
|
55
|
+
end
|
56
|
+
|
57
|
+
items.where(arel_table[primary_key].send(comparable_method, search)).exists?
|
58
|
+
end
|
59
|
+
|
60
|
+
def limited_relation
|
61
|
+
scope = sliced_relation
|
62
|
+
nodes = []
|
63
|
+
|
64
|
+
if first
|
65
|
+
nodes |=
|
66
|
+
scope
|
67
|
+
.reorder(first_limited_sorted_table)
|
68
|
+
.limit(first)
|
69
|
+
.to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
if last
|
73
|
+
nodes |=
|
74
|
+
scope
|
75
|
+
.reorder(last_limited_sorted_table)
|
76
|
+
.limit(last)
|
77
|
+
.to_a.reverse!
|
78
|
+
end
|
79
|
+
|
80
|
+
nodes
|
81
|
+
end
|
82
|
+
|
83
|
+
def sliced_relation
|
84
|
+
items
|
85
|
+
.yield_self { |s| after ? sliced_items(items: s, cursor: after_cursor, type: :after) : s }
|
86
|
+
.yield_self { |s| before ? sliced_items(items: s, cursor: before_cursor, type: :before) : s }
|
87
|
+
end
|
88
|
+
|
89
|
+
def sliced_items(items:, cursor:, type:)
|
90
|
+
items.where(arel_table[primary_key].send(sliced_comparable_method(type), cursor))
|
91
|
+
end
|
92
|
+
|
93
|
+
def sliced_comparable_method(type)
|
94
|
+
self.class::SLICED_COMPARABLE_METHODS.fetch(type.to_sym)
|
95
|
+
end
|
96
|
+
|
97
|
+
def first_limited_sorted_table
|
98
|
+
raise NotImplementedError.new("method \"#{__method__}\" should be implemented in #{self.class.name} class")
|
99
|
+
end
|
100
|
+
|
101
|
+
def last_limited_sorted_table
|
102
|
+
raise NotImplementedError.new("method \"#{__method__}\" should be implemented in #{self.class.name} class")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Connections
|
5
|
+
# Implements pagination by one field with desc order
|
6
|
+
module PrimaryKey
|
7
|
+
class Desc < Base
|
8
|
+
PAGE_COMPARABLE_METHODS = {
|
9
|
+
previous: {query: :gt, cursor: :gteq},
|
10
|
+
next: {query: :lt, cursor: :lteq}
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
SLICED_COMPARABLE_METHODS = {
|
14
|
+
after: :lt,
|
15
|
+
before: :gt
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def first_limited_sorted_table
|
21
|
+
arel_table[primary_key].desc
|
22
|
+
end
|
23
|
+
|
24
|
+
def last_limited_sorted_table
|
25
|
+
arel_table[primary_key].asc
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -14,9 +14,9 @@ module GraphQL
|
|
14
14
|
module Stable
|
15
15
|
def self.new(*args, desc: false, keys: nil, **kwargs)
|
16
16
|
if kwargs[:primary_key] || keys.nil?
|
17
|
-
|
17
|
+
cls = desc ? GraphQL::Connections::PrimaryKey::Desc : GraphQL::Connections::PrimaryKey::Asc
|
18
18
|
|
19
|
-
return
|
19
|
+
return cls.new(*args, **kwargs)
|
20
20
|
end
|
21
21
|
|
22
22
|
raise ArgumentError, "keyset for more that 2 keys is not implemented yet" if keys.length > 2
|
data/lib/graphql/connections.rb
CHANGED
@@ -11,8 +11,13 @@ end
|
|
11
11
|
require "graphql/connections/stable"
|
12
12
|
|
13
13
|
require "graphql/connections/base"
|
14
|
-
require "graphql/connections/key_asc"
|
15
14
|
|
16
15
|
require "graphql/connections/keyset/base"
|
17
16
|
require "graphql/connections/keyset/asc"
|
18
17
|
require "graphql/connections/keyset/desc"
|
18
|
+
|
19
|
+
require "graphql/connections/primary_key/base"
|
20
|
+
require "graphql/connections/primary_key/asc"
|
21
|
+
require "graphql/connections/primary_key/desc"
|
22
|
+
|
23
|
+
require "graphql/connections/chewy"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql-connections
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Misha Merkushin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: graphql
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '1.10'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.0'
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
39
42
|
- !ruby/object:Gem::Version
|
40
43
|
version: '1.10'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: bundler
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +58,48 @@ dependencies:
|
|
52
58
|
- - ">="
|
53
59
|
- !ruby/object:Gem::Version
|
54
60
|
version: '1.16'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: factory_bot_rails
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '6.2'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '6.2'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: faker
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '2.7'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.7'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: chewy
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '7.2'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '7.2'
|
55
103
|
- !ruby/object:Gem::Dependency
|
56
104
|
name: combustion
|
57
105
|
requirement: !ruby/object:Gem::Requirement
|
@@ -161,10 +209,13 @@ files:
|
|
161
209
|
- README.md
|
162
210
|
- lib/graphql/connections.rb
|
163
211
|
- lib/graphql/connections/base.rb
|
164
|
-
- lib/graphql/connections/
|
212
|
+
- lib/graphql/connections/chewy.rb
|
165
213
|
- lib/graphql/connections/keyset/asc.rb
|
166
214
|
- lib/graphql/connections/keyset/base.rb
|
167
215
|
- lib/graphql/connections/keyset/desc.rb
|
216
|
+
- lib/graphql/connections/primary_key/asc.rb
|
217
|
+
- lib/graphql/connections/primary_key/base.rb
|
218
|
+
- lib/graphql/connections/primary_key/desc.rb
|
168
219
|
- lib/graphql/connections/stable.rb
|
169
220
|
- lib/graphql/connections/version.rb
|
170
221
|
homepage: https://github.com/bibendi/graphql-connections
|
@@ -187,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
187
238
|
- !ruby/object:Gem::Version
|
188
239
|
version: '0'
|
189
240
|
requirements: []
|
190
|
-
rubygems_version: 3.
|
241
|
+
rubygems_version: 3.2.32
|
191
242
|
signing_key:
|
192
243
|
specification_version: 4
|
193
244
|
summary: GraphQL cursor-based stable pagination to work with Active Record relations
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module GraphQL
|
4
|
-
module Connections
|
5
|
-
# Implements pagination by one field with asc order
|
6
|
-
class KeyAsc < ::GraphQL::Connections::Base
|
7
|
-
def initialize(*args, primary_key: nil, **kwargs)
|
8
|
-
@primary_key = primary_key
|
9
|
-
|
10
|
-
super(*args, **kwargs)
|
11
|
-
end
|
12
|
-
|
13
|
-
def has_previous_page # rubocop:disable Naming/PredicateName, Metrics/AbcSize
|
14
|
-
if last
|
15
|
-
nodes.any? && items.where(arel_table[primary_key].lt(nodes.first[primary_key])).exists?
|
16
|
-
elsif after
|
17
|
-
items.where(arel_table[primary_key].lteq(after_cursor)).exists?
|
18
|
-
else
|
19
|
-
false
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def has_next_page # rubocop:disable Naming/PredicateName, Metrics/AbcSize
|
24
|
-
if first
|
25
|
-
nodes.any? && items.where(arel_table[primary_key].gt(nodes.last[primary_key])).exists?
|
26
|
-
elsif before
|
27
|
-
items.where(arel_table[primary_key].gteq(before_cursor)).exists?
|
28
|
-
else
|
29
|
-
false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def cursor_for(item)
|
34
|
-
cursor = serialize(item[primary_key])
|
35
|
-
cursor = encode(cursor) if opaque_cursor
|
36
|
-
cursor
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def limited_relation # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
42
|
-
scope = sliced_relation
|
43
|
-
nodes = []
|
44
|
-
|
45
|
-
if first
|
46
|
-
nodes |= scope
|
47
|
-
.reorder(arel_table[primary_key].asc)
|
48
|
-
.limit(first)
|
49
|
-
.to_a
|
50
|
-
end
|
51
|
-
|
52
|
-
if last
|
53
|
-
nodes |= scope
|
54
|
-
.reorder(arel_table[primary_key].desc)
|
55
|
-
.limit(last)
|
56
|
-
.to_a.reverse!
|
57
|
-
end
|
58
|
-
|
59
|
-
nodes
|
60
|
-
end
|
61
|
-
|
62
|
-
def sliced_relation # rubocop:disable Metrics/AbcSize
|
63
|
-
items
|
64
|
-
.yield_self { |s| after ? s.where(arel_table[primary_key].gt(after_cursor)) : s }
|
65
|
-
.yield_self { |s| before ? s.where(arel_table[primary_key].lt(before_cursor)) : s }
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|