Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
/pkg/
/spec/reports/
/tmp/
/vendor/bundle/
/vendor/

# rspec failure tracking
.rspec_status
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ field :fruits, resolver: Resolvers::FruitsResolver

Value returned by query resolver must be a kaminari object or implements its page scope methods (`current_page`, `limit_value`, `total_count`, `total_pages`).

## Paginated Field Helper

For a less verbose way to define paginated fields, use the `paginated_field` helper:

```ruby
paginated_field :fruits, Types::FruitType.collection_type, null: true

def fruits(page: nil, per: nil)
::Fruit.page(page).per(per)
end
```

This automatically adds `page` and `per` arguments (both optional) to the field. You can also add additional arguments using a block:

```ruby
paginated_field :fruits, Types::FruitType.collection_type, null: true do
argument :where, String, required: false
argument :order, String, required: false
end

def fruits(page: nil, per: nil, where: nil, order: nil)
::Fruit.page(page).per(per)
end
```

## GraphQL query

```graphql
Expand Down
2 changes: 2 additions & 0 deletions lib/graphql_pagination.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ module GraphqlPagination
require 'graphql_pagination/collection_type'
require 'graphql_pagination/collection_metadata_type'
require 'graphql_pagination/collection_field'
require 'graphql_pagination/paginated_field'

GraphQL::Schema::Object.extend GraphqlPagination::CollectionType
GraphQL::Schema::Object.extend GraphqlPagination::PaginatedField
GraphQL::Schema::Resolver.extend GraphqlPagination::CollectionType
11 changes: 11 additions & 0 deletions lib/graphql_pagination/paginated_field.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module GraphqlPagination
module PaginatedField
def paginated_field(name, type, **, &)
field(name, type, **) do
argument :page, Integer, required: false
argument :per, Integer, required: false
instance_eval(&) if block_given?
end
end
end
end
134 changes: 134 additions & 0 deletions spec/graphql_pagination/paginated_field_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
RSpec.describe GraphqlPagination::PaginatedField do
describe '.paginated_field' do
let(:query_type) do
Class.new(GraphQL::Schema::Object) do
graphql_name 'TestQuery'

paginated_field :fruits, FruitType.collection_type, null: true

paginated_field :vegetables, VegetableType.collection_type, null: true do
argument :where, String, required: false
argument :order, String, required: false
end

def fruits(page: nil, per: nil)
FruitModel.all.page(page).per(per)
end

def vegetables(page: nil, per: nil, **)
VegetableModel.all.page(page).per(per)
end
end
end

let(:schema) do
query = query_type
Class.new(GraphQL::Schema) do
query query
end
end

it 'defines field with pagination arguments' do
field = query_type.fields['fruits']
expect(field).not_to be_nil
expect(field.arguments.keys).to include('page', 'per')
end

it 'page argument is optional' do
field = query_type.fields['fruits']
expect(field.arguments['page'].type.non_null?).to be false
end

it 'per argument is optional' do
field = query_type.fields['fruits']
expect(field.arguments['per'].type.non_null?).to be false
end

context 'with additional arguments in block' do
it 'includes additional arguments' do
field = query_type.fields['vegetables']
expect(field.arguments.keys).to include('page', 'per', 'where', 'order')
end

it 'additional arguments are optional' do
field = query_type.fields['vegetables']
expect(field.arguments['where'].type.non_null?).to be false
expect(field.arguments['order'].type.non_null?).to be false
end
end

context 'when used in queries' do
subject(:result) { schema.execute(query).to_h['data']['result'] }

context 'with page and per arguments' do
let(:query) do
%|{
result: fruits(page: 2, per: 2) {
collection {
id
name
}
metadata {
totalCount
totalPages
limitValue
currentPage
}
}
}|
end

it 'executes query successfully' do
expect(result['collection'].size).to eq(2)
expect(result['metadata']['currentPage']).to eq(2)
expect(result['metadata']['limitValue']).to eq(2)
end
end

context 'without page and per arguments' do
let(:query) do
%|{
result: fruits {
collection {
id
name
}
metadata {
totalCount
totalPages
limitValue
currentPage
}
}
}|
end

it 'executes query successfully' do
expect(result['collection'].size).to eq(11)
expect(result['metadata']['currentPage']).to eq(1)
end
end

context 'with additional arguments' do
let(:query) do
%|{
result: vegetables(page: 1, per: 5, where: "test") {
collection {
id
name
}
metadata {
totalCount
}
}
}|
end

it 'executes query successfully with additional arguments' do
expect(result['collection'].size).to eq(5)
expect(result['metadata']['totalCount']).to eq(11)
end
end
end
end
end