graphql-pagination 2.4.0 → 2.5.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/.bundler-version +1 -1
- data/.github/PULL_REQUEST_TEMPLATE.md +2 -2
- data/.github/workflows/auto-approve.yml +1 -1
- data/.github/workflows/ci.yml +2 -2
- data/.gitignore +3 -1
- data/Gemfile +11 -9
- data/Gemfile.lock +94 -51
- data/README.md +99 -0
- data/Rakefile +5 -6
- data/examples/complexity_example.rb +116 -0
- data/graphql-pagination.gemspec +16 -16
- data/lib/graphql-pagination.rb +1 -1
- data/lib/graphql_pagination/collection_field.rb +60 -0
- data/lib/graphql_pagination/paginated_field.rb +11 -0
- data/lib/graphql_pagination/version.rb +1 -1
- data/lib/graphql_pagination.rb +10 -6
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9475b513e2ced37bbd798e3c254cd6e59e6ba3bb84fca82ceb3735b9202cc7f1
|
|
4
|
+
data.tar.gz: 6ff8b16a00b6e2036aac8e137913008fead15f6ed5f201758c371380b8e1801e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c4cae67537d17e7de4e0844aff5c13a068c9e9b6b84bdd086b477229bcb80d9a88aafffad4d02ddaebc0d4719b903d8088d540a12af123a7a2e3a871a9677e5
|
|
7
|
+
data.tar.gz: c4e76b225701a6025e2924b4aa6600815272a26883a4990ae81c9607152032db965941f3fd6a91af92703f1825957b2cb367c9f768654896be6d925ac138a61f
|
data/.bundler-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4.0.7
|
|
@@ -8,7 +8,7 @@ jobs:
|
|
|
8
8
|
auto-approve:
|
|
9
9
|
runs-on: ubuntu-latest
|
|
10
10
|
steps:
|
|
11
|
-
- uses: hmarr/auto-approve-action@
|
|
11
|
+
- uses: hmarr/auto-approve-action@v4.0.0
|
|
12
12
|
if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' || github.actor == 'renofidev' || contains(github.event.pull_request.labels.*.name, 'HOTFIX-AUTO-APPROVE') || contains(github.event.pull_request.labels.*.name, 'self-approve') || contains(github.event.pull_request.labels.*.name, 'dependencies')
|
|
13
13
|
with:
|
|
14
14
|
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
data/.github/workflows/ci.yml
CHANGED
data/.gitignore
CHANGED
data/Gemfile
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
source
|
|
1
|
+
source "https://rubygems.org"
|
|
2
2
|
|
|
3
3
|
gemspec
|
|
4
4
|
|
|
5
|
-
gem
|
|
6
|
-
gem
|
|
7
|
-
gem
|
|
8
|
-
gem
|
|
9
|
-
gem
|
|
10
|
-
gem
|
|
11
|
-
gem
|
|
12
|
-
gem
|
|
5
|
+
gem "irb"
|
|
6
|
+
gem "kaminari-activerecord"
|
|
7
|
+
gem "kaminari-core"
|
|
8
|
+
gem "ostruct"
|
|
9
|
+
gem "pry"
|
|
10
|
+
gem "rake"
|
|
11
|
+
gem "rspec"
|
|
12
|
+
gem "standard"
|
|
13
|
+
|
|
14
|
+
gem "openssl"
|
data/Gemfile.lock
CHANGED
|
@@ -1,105 +1,147 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
graphql-pagination (2.
|
|
4
|
+
graphql-pagination (2.5.0)
|
|
5
5
|
graphql (>= 2.4.7)
|
|
6
6
|
|
|
7
7
|
GEM
|
|
8
8
|
remote: https://rubygems.org/
|
|
9
9
|
specs:
|
|
10
|
-
activemodel (8.
|
|
11
|
-
activesupport (= 8.
|
|
12
|
-
activerecord (8.
|
|
13
|
-
activemodel (= 8.
|
|
14
|
-
activesupport (= 8.
|
|
10
|
+
activemodel (8.1.3)
|
|
11
|
+
activesupport (= 8.1.3)
|
|
12
|
+
activerecord (8.1.3)
|
|
13
|
+
activemodel (= 8.1.3)
|
|
14
|
+
activesupport (= 8.1.3)
|
|
15
15
|
timeout (>= 0.4.0)
|
|
16
|
-
activesupport (8.
|
|
16
|
+
activesupport (8.1.3)
|
|
17
17
|
base64
|
|
18
|
-
benchmark (>= 0.3)
|
|
19
18
|
bigdecimal
|
|
20
19
|
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
21
20
|
connection_pool (>= 2.2.5)
|
|
22
21
|
drb
|
|
23
22
|
i18n (>= 1.6, < 2)
|
|
23
|
+
json
|
|
24
24
|
logger (>= 1.4.2)
|
|
25
25
|
minitest (>= 5.1)
|
|
26
26
|
securerandom (>= 0.3)
|
|
27
27
|
tzinfo (~> 2.0, >= 2.0.5)
|
|
28
28
|
uri (>= 0.13.1)
|
|
29
|
-
ast (2.4.
|
|
30
|
-
base64 (0.
|
|
31
|
-
|
|
32
|
-
bigdecimal (3.1.9)
|
|
29
|
+
ast (2.4.3)
|
|
30
|
+
base64 (0.3.0)
|
|
31
|
+
bigdecimal (4.1.1)
|
|
33
32
|
coderay (1.1.3)
|
|
34
|
-
concurrent-ruby (1.3.
|
|
35
|
-
connection_pool (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
concurrent-ruby (1.3.6)
|
|
34
|
+
connection_pool (3.0.2)
|
|
35
|
+
date (3.5.1)
|
|
36
|
+
diff-lcs (1.6.2)
|
|
37
|
+
drb (2.2.3)
|
|
38
|
+
erb (6.0.2)
|
|
39
|
+
fiber-storage (1.0.1)
|
|
40
|
+
graphql (2.5.23)
|
|
40
41
|
base64
|
|
41
42
|
fiber-storage
|
|
42
43
|
logger
|
|
43
|
-
i18n (1.14.
|
|
44
|
+
i18n (1.14.8)
|
|
44
45
|
concurrent-ruby (~> 1.0)
|
|
45
|
-
|
|
46
|
+
io-console (0.8.2)
|
|
47
|
+
irb (1.17.0)
|
|
48
|
+
pp (>= 0.6.0)
|
|
49
|
+
prism (>= 1.3.0)
|
|
50
|
+
rdoc (>= 4.0.0)
|
|
51
|
+
reline (>= 0.4.2)
|
|
52
|
+
json (2.19.3)
|
|
46
53
|
kaminari-activerecord (1.2.2)
|
|
47
54
|
activerecord
|
|
48
55
|
kaminari-core (= 1.2.2)
|
|
49
56
|
kaminari-core (1.2.2)
|
|
50
|
-
language_server-protocol (3.17.0.
|
|
51
|
-
|
|
57
|
+
language_server-protocol (3.17.0.5)
|
|
58
|
+
lint_roller (1.1.0)
|
|
59
|
+
logger (1.7.0)
|
|
52
60
|
method_source (1.1.0)
|
|
53
|
-
minitest (
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
minitest (6.0.3)
|
|
62
|
+
drb (~> 2.0)
|
|
63
|
+
prism (~> 1.5)
|
|
64
|
+
openssl (4.0.1)
|
|
65
|
+
ostruct (0.6.3)
|
|
66
|
+
parallel (1.28.0)
|
|
67
|
+
parser (3.3.11.1)
|
|
56
68
|
ast (~> 2.4.1)
|
|
57
69
|
racc
|
|
58
|
-
|
|
70
|
+
pp (0.6.3)
|
|
71
|
+
prettyprint
|
|
72
|
+
prettyprint (0.2.0)
|
|
73
|
+
prism (1.9.0)
|
|
74
|
+
pry (0.16.0)
|
|
59
75
|
coderay (~> 1.1)
|
|
60
76
|
method_source (~> 1.0)
|
|
77
|
+
reline (>= 0.6.0)
|
|
78
|
+
psych (5.3.1)
|
|
79
|
+
date
|
|
80
|
+
stringio
|
|
61
81
|
racc (1.8.1)
|
|
62
82
|
rainbow (3.1.1)
|
|
63
|
-
rake (13.
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
rake (13.3.1)
|
|
84
|
+
rdoc (7.2.0)
|
|
85
|
+
erb
|
|
86
|
+
psych (>= 4.0.0)
|
|
87
|
+
tsort
|
|
88
|
+
regexp_parser (2.12.0)
|
|
89
|
+
reline (0.6.3)
|
|
90
|
+
io-console (~> 0.5)
|
|
91
|
+
rspec (3.13.2)
|
|
66
92
|
rspec-core (~> 3.13.0)
|
|
67
93
|
rspec-expectations (~> 3.13.0)
|
|
68
94
|
rspec-mocks (~> 3.13.0)
|
|
69
|
-
rspec-core (3.13.
|
|
95
|
+
rspec-core (3.13.6)
|
|
70
96
|
rspec-support (~> 3.13.0)
|
|
71
|
-
rspec-expectations (3.13.
|
|
97
|
+
rspec-expectations (3.13.5)
|
|
72
98
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
73
99
|
rspec-support (~> 3.13.0)
|
|
74
|
-
rspec-mocks (3.13.
|
|
100
|
+
rspec-mocks (3.13.8)
|
|
75
101
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
76
102
|
rspec-support (~> 3.13.0)
|
|
77
|
-
rspec-support (3.13.
|
|
78
|
-
rubocop (1.
|
|
103
|
+
rspec-support (3.13.7)
|
|
104
|
+
rubocop (1.84.2)
|
|
79
105
|
json (~> 2.3)
|
|
80
|
-
language_server-protocol (
|
|
106
|
+
language_server-protocol (~> 3.17.0.2)
|
|
107
|
+
lint_roller (~> 1.1.0)
|
|
81
108
|
parallel (~> 1.10)
|
|
82
109
|
parser (>= 3.3.0.2)
|
|
83
110
|
rainbow (>= 2.2.2, < 4.0)
|
|
84
111
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
85
|
-
rubocop-ast (>= 1.
|
|
112
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
86
113
|
ruby-progressbar (~> 1.7)
|
|
87
114
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
88
|
-
rubocop-ast (1.
|
|
89
|
-
parser (>= 3.3.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
rubocop (
|
|
115
|
+
rubocop-ast (1.49.1)
|
|
116
|
+
parser (>= 3.3.7.2)
|
|
117
|
+
prism (~> 1.7)
|
|
118
|
+
rubocop-performance (1.26.1)
|
|
119
|
+
lint_roller (~> 1.1)
|
|
120
|
+
rubocop (>= 1.75.0, < 2.0)
|
|
121
|
+
rubocop-ast (>= 1.47.1, < 2.0)
|
|
94
122
|
ruby-progressbar (1.13.0)
|
|
95
123
|
securerandom (0.4.1)
|
|
96
|
-
|
|
124
|
+
standard (1.54.0)
|
|
125
|
+
language_server-protocol (~> 3.17.0.2)
|
|
126
|
+
lint_roller (~> 1.0)
|
|
127
|
+
rubocop (~> 1.84.0)
|
|
128
|
+
standard-custom (~> 1.0.0)
|
|
129
|
+
standard-performance (~> 1.8)
|
|
130
|
+
standard-custom (1.0.2)
|
|
131
|
+
lint_roller (~> 1.0)
|
|
132
|
+
rubocop (~> 1.50)
|
|
133
|
+
standard-performance (1.9.0)
|
|
134
|
+
lint_roller (~> 1.1)
|
|
135
|
+
rubocop-performance (~> 1.26.0)
|
|
136
|
+
stringio (3.2.0)
|
|
137
|
+
timeout (0.6.1)
|
|
138
|
+
tsort (0.2.0)
|
|
97
139
|
tzinfo (2.0.6)
|
|
98
140
|
concurrent-ruby (~> 1.0)
|
|
99
|
-
unicode-display_width (3.
|
|
100
|
-
unicode-emoji (~> 4.
|
|
101
|
-
unicode-emoji (4.0
|
|
102
|
-
uri (1.
|
|
141
|
+
unicode-display_width (3.2.0)
|
|
142
|
+
unicode-emoji (~> 4.1)
|
|
143
|
+
unicode-emoji (4.2.0)
|
|
144
|
+
uri (1.1.1)
|
|
103
145
|
|
|
104
146
|
PLATFORMS
|
|
105
147
|
aarch64-linux-musl
|
|
@@ -112,14 +154,15 @@ PLATFORMS
|
|
|
112
154
|
|
|
113
155
|
DEPENDENCIES
|
|
114
156
|
graphql-pagination!
|
|
157
|
+
irb
|
|
115
158
|
kaminari-activerecord
|
|
116
159
|
kaminari-core
|
|
160
|
+
openssl
|
|
161
|
+
ostruct
|
|
117
162
|
pry
|
|
118
163
|
rake
|
|
119
164
|
rspec
|
|
120
|
-
|
|
121
|
-
rubocop-rake
|
|
122
|
-
rubocop-rspec
|
|
165
|
+
standard
|
|
123
166
|
|
|
124
167
|
BUNDLED WITH
|
|
125
|
-
|
|
168
|
+
4.0.3
|
data/README.md
CHANGED
|
@@ -16,6 +16,8 @@ Add `graphql-pagination` to your Gemfile, you can use `kaminari-activerecord` or
|
|
|
16
16
|
|
|
17
17
|
## Usage example
|
|
18
18
|
|
|
19
|
+
### With Fields
|
|
20
|
+
|
|
19
21
|
Use `collection_type` instead of `connection_type` to define your type:
|
|
20
22
|
|
|
21
23
|
```ruby
|
|
@@ -29,8 +31,55 @@ Use `collection_type` instead of `connection_type` to define your type:
|
|
|
29
31
|
end
|
|
30
32
|
```
|
|
31
33
|
|
|
34
|
+
### With Resolvers
|
|
35
|
+
|
|
36
|
+
You can also use `collection_type` with GraphQL resolvers:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
module Resolvers
|
|
40
|
+
class FruitsResolver < GraphQL::Schema::Resolver
|
|
41
|
+
type Types::FruitType.collection_type, null: false
|
|
42
|
+
|
|
43
|
+
argument :page, Integer, required: false
|
|
44
|
+
argument :limit, Integer, required: false
|
|
45
|
+
|
|
46
|
+
def resolve(page: nil, limit: nil)
|
|
47
|
+
::Fruit.page(page).per(limit)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# In your query type
|
|
53
|
+
field :fruits, resolver: Resolvers::FruitsResolver
|
|
54
|
+
```
|
|
55
|
+
|
|
32
56
|
Value returned by query resolver must be a kaminari object or implements its page scope methods (`current_page`, `limit_value`, `total_count`, `total_pages`).
|
|
33
57
|
|
|
58
|
+
## Paginated Field Helper
|
|
59
|
+
|
|
60
|
+
For a less verbose way to define paginated fields, use the `paginated_field` helper:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
paginated_field :fruits, Types::FruitType.collection_type, null: true
|
|
64
|
+
|
|
65
|
+
def fruits(page: nil, per: nil)
|
|
66
|
+
::Fruit.page(page).per(per)
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This automatically adds `page` and `per` arguments (both optional) to the field. You can also add additional arguments using a block:
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
paginated_field :fruits, Types::FruitType.collection_type, null: true do
|
|
74
|
+
argument :where, String, required: false
|
|
75
|
+
argument :order, String, required: false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fruits(page: nil, per: nil, where: nil, order: nil)
|
|
79
|
+
::Fruit.page(page).per(per)
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
34
83
|
## GraphQL query
|
|
35
84
|
|
|
36
85
|
```graphql
|
|
@@ -117,6 +166,56 @@ end
|
|
|
117
166
|
field :fruits, Types::FruitType.collection_type(metadata_type: MyMetadataType)
|
|
118
167
|
```
|
|
119
168
|
|
|
169
|
+
## Complexity Calculation
|
|
170
|
+
|
|
171
|
+
To prevent loading too much data, GraphQL supports complexity calculation. This gem provides the `GraphqlPagination::CollectionField` module that can be prepended to your base field class to automatically calculate complexity for collection type fields.
|
|
172
|
+
|
|
173
|
+
The complexity calculation takes into account:
|
|
174
|
+
- The page size (from `limit` or `per` argument)
|
|
175
|
+
- Metadata fields requested
|
|
176
|
+
- Collection fields requested
|
|
177
|
+
- Nested fields within collection items
|
|
178
|
+
|
|
179
|
+
### Usage
|
|
180
|
+
|
|
181
|
+
First, create a base field class and prepend the `GraphqlPagination::CollectionField` module:
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
class Types::BaseField < GraphQL::Schema::Field
|
|
185
|
+
prepend GraphqlPagination::CollectionField
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Then configure your base object to use this field class:
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
class Types::BaseObject < GraphQL::Schema::Object
|
|
193
|
+
field_class Types::BaseField
|
|
194
|
+
end
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Now all fields that return collection types will automatically have complexity calculation:
|
|
198
|
+
|
|
199
|
+
```ruby
|
|
200
|
+
class Types::QueryType < Types::BaseObject
|
|
201
|
+
field :fruits, Types::FruitType.collection_type, null: true do
|
|
202
|
+
argument :page, Integer, required: false
|
|
203
|
+
argument :limit, Integer, required: false
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def fruits(page: nil, limit: nil)
|
|
207
|
+
::Fruit.page(page).per(limit)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The complexity will be calculated as:
|
|
213
|
+
```
|
|
214
|
+
1 (field itself) + (page_size * items_complexity) + metadata_complexity + collection_complexity
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
If no `limit` or `per` argument is provided, it will use the schema's default page size or fall back to 25 (Kaminari's default).
|
|
218
|
+
|
|
120
219
|
## Contributing
|
|
121
220
|
|
|
122
221
|
Bug reports and pull requests are welcome on GitHub at https://github.com/renofi/graphql-pagination. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
data/Rakefile
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require "rspec/core/rake_task"
|
|
3
|
+
require "standard/rake"
|
|
4
4
|
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec)
|
|
6
|
-
RuboCop::RakeTask.new
|
|
7
6
|
|
|
8
|
-
task ci: %i[spec
|
|
9
|
-
task default: %i[spec
|
|
7
|
+
task ci: %i[spec standard]
|
|
8
|
+
task default: %i[spec standard:fix]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Complexity Calculation Example
|
|
2
|
+
#
|
|
3
|
+
# This example demonstrates how to use the CollectionField module
|
|
4
|
+
# to automatically calculate complexity for collection type fields.
|
|
5
|
+
|
|
6
|
+
require "bundler/setup"
|
|
7
|
+
require "graphql-pagination"
|
|
8
|
+
require "kaminari/core"
|
|
9
|
+
|
|
10
|
+
# Sample data model
|
|
11
|
+
class User
|
|
12
|
+
attr_reader :id, :name
|
|
13
|
+
|
|
14
|
+
def initialize(id, name)
|
|
15
|
+
@id = id
|
|
16
|
+
@name = name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.all
|
|
20
|
+
users = (1..100).map { |i| User.new(i, "User #{i}") }
|
|
21
|
+
Kaminari.paginate_array(users)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# GraphQL Types
|
|
26
|
+
class UserType < GraphQL::Schema::Object
|
|
27
|
+
field :id, ID, null: false
|
|
28
|
+
field :name, String, null: false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Base field with complexity calculation
|
|
32
|
+
class BaseField < GraphQL::Schema::Field
|
|
33
|
+
prepend GraphqlPagination::CollectionField
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class BaseObject < GraphQL::Schema::Object
|
|
37
|
+
field_class BaseField
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class QueryType < BaseObject
|
|
41
|
+
field :users, UserType.collection_type, null: false do
|
|
42
|
+
argument :page, Integer, required: false
|
|
43
|
+
argument :limit, Integer, required: false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def users(page: nil, limit: nil)
|
|
47
|
+
User.all.page(page).per(limit)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class ExampleSchema < GraphQL::Schema
|
|
52
|
+
query QueryType
|
|
53
|
+
max_complexity 500
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Example queries
|
|
57
|
+
simple_query = <<~GRAPHQL
|
|
58
|
+
{
|
|
59
|
+
users(limit: 5) {
|
|
60
|
+
collection {
|
|
61
|
+
id
|
|
62
|
+
name
|
|
63
|
+
}
|
|
64
|
+
metadata {
|
|
65
|
+
currentPage
|
|
66
|
+
totalPages
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
GRAPHQL
|
|
71
|
+
|
|
72
|
+
complex_query = <<~GRAPHQL
|
|
73
|
+
{
|
|
74
|
+
users(limit: 50) {
|
|
75
|
+
collection {
|
|
76
|
+
id
|
|
77
|
+
name
|
|
78
|
+
}
|
|
79
|
+
metadata {
|
|
80
|
+
currentPage
|
|
81
|
+
totalPages
|
|
82
|
+
totalCount
|
|
83
|
+
limitValue
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
GRAPHQL
|
|
88
|
+
|
|
89
|
+
puts "Example 1: Simple query with limit: 5"
|
|
90
|
+
puts "=" * 50
|
|
91
|
+
result = ExampleSchema.execute(simple_query)
|
|
92
|
+
if result["errors"]
|
|
93
|
+
puts "Errors: #{result["errors"]}"
|
|
94
|
+
else
|
|
95
|
+
puts "Success! Retrieved #{result["data"]["users"]["collection"].size} users"
|
|
96
|
+
puts "Current Page: #{result["data"]["users"]["metadata"]["currentPage"]}"
|
|
97
|
+
puts "Total Pages: #{result["data"]["users"]["metadata"]["totalPages"]}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
puts "\n"
|
|
101
|
+
puts "Example 2: Complex query with limit: 50"
|
|
102
|
+
puts "=" * 50
|
|
103
|
+
result = ExampleSchema.execute(complex_query)
|
|
104
|
+
if result["errors"]
|
|
105
|
+
puts "Errors: #{result["errors"]}"
|
|
106
|
+
else
|
|
107
|
+
puts "Success! Retrieved #{result["data"]["users"]["collection"].size} users"
|
|
108
|
+
puts "Current Page: #{result["data"]["users"]["metadata"]["currentPage"]}"
|
|
109
|
+
puts "Total Pages: #{result["data"]["users"]["metadata"]["totalPages"]}"
|
|
110
|
+
puts "Total Count: #{result["data"]["users"]["metadata"]["totalCount"]}"
|
|
111
|
+
puts "Limit Value: #{result["data"]["users"]["metadata"]["limitValue"]}"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
puts "\n"
|
|
115
|
+
puts "Note: With the CollectionField module, complexity is automatically calculated"
|
|
116
|
+
puts "based on the limit argument, preventing queries from loading too much data."
|
data/graphql-pagination.gemspec
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
lib = File.expand_path(
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
require
|
|
3
|
+
require "graphql_pagination/version"
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name =
|
|
6
|
+
spec.name = "graphql-pagination"
|
|
7
7
|
spec.version = GraphqlPagination::VERSION
|
|
8
|
-
spec.authors = [
|
|
9
|
-
spec.email = [
|
|
8
|
+
spec.authors = ["Krzysztof Knapik", "RenoFi Engineering Team"]
|
|
9
|
+
spec.email = ["knapo@knapo.net", "engineering@renofi.com"]
|
|
10
10
|
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
13
|
-
spec.homepage =
|
|
14
|
-
spec.license =
|
|
11
|
+
spec.summary = "Page-based kaminari pagination for graphql."
|
|
12
|
+
spec.description = "Page-based kaminari pagination for graphql returning collection and pagination metadata."
|
|
13
|
+
spec.homepage = "https://github.com/RenoFi/graphql-pagination"
|
|
14
|
+
spec.license = "MIT"
|
|
15
15
|
|
|
16
|
-
spec.metadata[
|
|
17
|
-
spec.metadata[
|
|
18
|
-
spec.metadata[
|
|
16
|
+
spec.metadata["homepage_uri"] = "https://github.com/RenoFi/graphql-pagination"
|
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/RenoFi/graphql-pagination"
|
|
18
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
19
19
|
|
|
20
|
-
spec.required_ruby_version = Gem::Requirement.new(
|
|
20
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 3.4.0")
|
|
21
21
|
|
|
22
22
|
spec.files = Dir.chdir(__dir__) do
|
|
23
23
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin/|spec/|\.rub)}) }
|
|
24
24
|
end
|
|
25
|
-
spec.bindir =
|
|
25
|
+
spec.bindir = "exe"
|
|
26
26
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
|
-
spec.require_paths = [
|
|
27
|
+
spec.require_paths = ["lib"]
|
|
28
28
|
|
|
29
|
-
spec.add_dependency
|
|
29
|
+
spec.add_dependency "graphql", ">= 2.4.7"
|
|
30
30
|
end
|
data/lib/graphql-pagination.rb
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "graphql_pagination"
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module GraphqlPagination
|
|
2
|
+
module CollectionField
|
|
3
|
+
# Check if the field returns a collection type
|
|
4
|
+
def collection_type?
|
|
5
|
+
if @return_type_expr.respond_to?(:graphql_name)
|
|
6
|
+
@return_type_expr.graphql_name.end_with?("Collection")
|
|
7
|
+
else
|
|
8
|
+
false
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Calculate complexity for collection fields
|
|
13
|
+
# This is based on graphql-ruby's connection complexity calculation
|
|
14
|
+
# See: https://github.com/rmosolgo/graphql-ruby/blob/master/lib/graphql/schema/field.rb#L472
|
|
15
|
+
def calculate_complexity(query:, nodes:, child_complexity:)
|
|
16
|
+
if collection_type?
|
|
17
|
+
arguments = query.arguments_for(nodes.first, self)
|
|
18
|
+
|
|
19
|
+
# Get the page size from the `limit` or `per` argument
|
|
20
|
+
# Default to a reasonable value if not provided
|
|
21
|
+
max_possible_page_size = arguments[:limit] || arguments[:per]
|
|
22
|
+
|
|
23
|
+
if max_possible_page_size.nil?
|
|
24
|
+
# Use schema defaults or a reasonable default
|
|
25
|
+
# Note: default_page_size, max_page_size are GraphQL::Schema::Field methods
|
|
26
|
+
# query.schema.default_page_size and query.schema.default_max_page_size are schema-level configs
|
|
27
|
+
max_possible_page_size = default_page_size ||
|
|
28
|
+
query.schema.default_page_size ||
|
|
29
|
+
max_page_size ||
|
|
30
|
+
query.schema.default_max_page_size ||
|
|
31
|
+
25 # Reasonable default for kaminari
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
metadata_complexity = 0
|
|
35
|
+
lookahead = GraphQL::Execution::Lookahead.new(query:, field: self, ast_nodes: nodes, owner_type: owner)
|
|
36
|
+
|
|
37
|
+
# Calculate metadata complexity
|
|
38
|
+
if (metadata_lookahead = lookahead.selection(:metadata)).selected?
|
|
39
|
+
metadata_complexity += 1 # metadata field itself
|
|
40
|
+
metadata_complexity += metadata_lookahead.selections.size # metadata subfields
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Check for total_pages and total_count (they are part of metadata)
|
|
44
|
+
# Note: These are already counted above if metadata is selected
|
|
45
|
+
|
|
46
|
+
nodes_edges_complexity = 0
|
|
47
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:collection)
|
|
48
|
+
|
|
49
|
+
# Calculate items complexity
|
|
50
|
+
# Subtract metadata and collection field complexity from child complexity
|
|
51
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
|
52
|
+
|
|
53
|
+
# Apply complexity: 1 (this field) + (page_size * items) + metadata + collection
|
|
54
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
|
55
|
+
else
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module GraphqlPagination
|
|
2
|
+
module PaginatedField
|
|
3
|
+
def paginated_field(name, type, **, &)
|
|
4
|
+
field(name, type, **) do
|
|
5
|
+
argument :page, Integer, required: false
|
|
6
|
+
argument :per, Integer, required: false
|
|
7
|
+
instance_eval(&) if block_given?
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
data/lib/graphql_pagination.rb
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
1
|
+
require "graphql_pagination/version"
|
|
2
|
+
require "graphql"
|
|
3
|
+
require "graphql/schema"
|
|
4
4
|
|
|
5
5
|
module GraphqlPagination
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
8
|
+
require "graphql_pagination/collection_base_error"
|
|
9
|
+
require "graphql_pagination/collection_type"
|
|
10
|
+
require "graphql_pagination/collection_metadata_type"
|
|
11
|
+
require "graphql_pagination/collection_field"
|
|
12
|
+
require "graphql_pagination/paginated_field"
|
|
11
13
|
|
|
12
14
|
GraphQL::Schema::Object.extend GraphqlPagination::CollectionType
|
|
15
|
+
GraphQL::Schema::Resolver.extend GraphqlPagination::CollectionType
|
|
16
|
+
GraphQL::Schema::Object.extend GraphqlPagination::PaginatedField
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphql-pagination
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Krzysztof Knapik
|
|
8
8
|
- RenoFi Engineering Team
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: graphql
|
|
@@ -50,12 +50,15 @@ files:
|
|
|
50
50
|
- LICENSE
|
|
51
51
|
- README.md
|
|
52
52
|
- Rakefile
|
|
53
|
+
- examples/complexity_example.rb
|
|
53
54
|
- graphql-pagination.gemspec
|
|
54
55
|
- lib/graphql-pagination.rb
|
|
55
56
|
- lib/graphql_pagination.rb
|
|
56
57
|
- lib/graphql_pagination/collection_base_error.rb
|
|
58
|
+
- lib/graphql_pagination/collection_field.rb
|
|
57
59
|
- lib/graphql_pagination/collection_metadata_type.rb
|
|
58
60
|
- lib/graphql_pagination/collection_type.rb
|
|
61
|
+
- lib/graphql_pagination/paginated_field.rb
|
|
59
62
|
- lib/graphql_pagination/version.rb
|
|
60
63
|
homepage: https://github.com/RenoFi/graphql-pagination
|
|
61
64
|
licenses:
|
|
@@ -71,14 +74,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
71
74
|
requirements:
|
|
72
75
|
- - ">="
|
|
73
76
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 3.
|
|
77
|
+
version: 3.4.0
|
|
75
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
79
|
requirements:
|
|
77
80
|
- - ">="
|
|
78
81
|
- !ruby/object:Gem::Version
|
|
79
82
|
version: '0'
|
|
80
83
|
requirements: []
|
|
81
|
-
rubygems_version:
|
|
84
|
+
rubygems_version: 4.0.9
|
|
82
85
|
specification_version: 4
|
|
83
86
|
summary: Page-based kaminari pagination for graphql.
|
|
84
87
|
test_files: []
|