graphql-connections 1.2.0 → 1.3.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
  SHA256:
3
- metadata.gz: 1206b9dbdd7d135f43b52b0a62b4d28b4d5d8c93bb5e8a18800ef5abcc5a6157
4
- data.tar.gz: 509ba8323078d1dee9095c18ec227b8416967ad0ce51bb4e3bd2806f09a28a57
3
+ metadata.gz: ed09e36d3d30a8e2cd939a1b9b39341168478d547e4b0e990798e612f6f83e49
4
+ data.tar.gz: bc35f6d38dcebf5a97ce0cf69d6540db2712046bddc15f5c1bca0c685115eac4
5
5
  SHA512:
6
- metadata.gz: 63f191221ff5b1e2133ad84bb8cb377941cad75de324310f18734729f5a519dae01f466a88539e895a155f32e39eccfab60f423271a220b4cf3fcfa33bfdbb3b
7
- data.tar.gz: da4d990bcc8941fbce16049f0a4ea6aa642f73194a34bfb757c1c0080bd16da1736e4466f2ee7f2868dcc5ceebc8677b745a6c3ea43328561af8ad09f9c9e71f
6
+ metadata.gz: 2408beb9fb3346e5ce7e980b462dc34762d5507bb65eb9c1051672d1dc0b8d33c2807d5113b0837a704ad3311bcbc27baeddb5bd4bb1177e8e748d00ca0f2b98
7
+ data.tar.gz: 042f6ee93e8fbc8391122ac5546bf18c3be595b2c45504daeff6dbec16240278141ad3e9a5d145e30a14c84e70d37ef03fb242e282c110f41ffd2605ebca81c4
data/README.md CHANGED
@@ -59,7 +59,9 @@ Also, you can pass the `:desc` option to reverse the relation:
59
59
  GraphQL::Connections::Stable.new(Message.all, keys: %w[name id], desc: true)
60
60
  ```
61
61
 
62
- **NOTE:** `:desc` option is not implemented for stable connections with `:primary_key` passed; if you need it—use keyset pagination or implement `:desc` option for us 🙂.
62
+ ```ruby
63
+ GraphQL::Connections::Stable.new(Message.all, primary_key: :created_at, desc: true)
64
+ ```
63
65
 
64
66
  Also, you can disable opaque cursors by setting `opaque_cursor` param:
65
67
 
@@ -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
- raise NotImplementedError, "desc connection is not implemented yet" if desc
17
+ cls = desc ? GraphQL::Connections::PrimaryKey::Desc : GraphQL::Connections::PrimaryKey::Asc
18
18
 
19
- return GraphQL::Connections::KeyAsc.new(*args, **kwargs)
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module GraphQL
4
4
  module Paging
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.0"
6
6
  end
7
7
  end
@@ -11,10 +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"
19
18
 
19
+ require "graphql/connections/primary_key/base"
20
+ require "graphql/connections/primary_key/asc"
21
+ require "graphql/connections/primary_key/desc"
22
+
20
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.2.0
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: 2022-04-04 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -210,10 +210,12 @@ files:
210
210
  - lib/graphql/connections.rb
211
211
  - lib/graphql/connections/base.rb
212
212
  - lib/graphql/connections/chewy.rb
213
- - lib/graphql/connections/key_asc.rb
214
213
  - lib/graphql/connections/keyset/asc.rb
215
214
  - lib/graphql/connections/keyset/base.rb
216
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
217
219
  - lib/graphql/connections/stable.rb
218
220
  - lib/graphql/connections/version.rb
219
221
  homepage: https://github.com/bibendi/graphql-connections
@@ -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
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
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
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
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