Skip to content
Merged
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
34 changes: 31 additions & 3 deletions lib/graphql/resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1328,9 +1328,37 @@ defmodule AshGraphql.Graphql.Resolver do
end
end

defp paginate_with_offset(%Ash.Page.Offset{results: results, count: count, more?: more?}) do
{:ok, %{results: results, count: count, more?: more?}}
end
defp paginate_with_offset(%Ash.Page.Offset{
results: results,
count: count,
more?: more?,
offset: offset,
limit: limit
}) do
total_pages = get_total_pages(count, limit)
has_next_page = more?
has_previous_page = offset > 0
page_number = get_current_page(%Ash.Page.Offset{limit: limit, offset: offset}, total_pages)
last_page = total_pages

{:ok,
%{
results: results,
count: count,
more?: more?,
has_next_page: has_next_page,
has_previous_page: has_previous_page,
page_number: page_number,
last_page: last_page
}}
end

defp get_total_pages(count, _) when count in [0, nil], do: 1
defp get_total_pages(_, nil), do: 1
defp get_total_pages(total_count, limit), do: ceil(total_count / limit)

defp get_current_page(%Ash.Page.Offset{limit: limit, offset: offset}, total),
do: min(ceil(offset / limit) + 1, total)

defp paginate(_resource, _gql_query, _action, %Ash.Page.Keyset{} = keyset, relay?) do
paginate_with_keyset(keyset, relay?)
Expand Down
53 changes: 42 additions & 11 deletions lib/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3832,6 +3832,46 @@ defmodule AshGraphql.Resource do
]
end

defp add_pagination_metadata(fields, schema, true) do
[
%Absinthe.Blueprint.Schema.FieldDefinition{
description: "Whether or not there is a next page",
identifier: :has_next_page,
module: schema,
name: "has_next_page",
__reference__: ref(__ENV__),
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :boolean}
},
%Absinthe.Blueprint.Schema.FieldDefinition{
description: "Whether or not there is a previous page",
identifier: :has_previous_page,
module: schema,
name: "has_previous_page",
__reference__: ref(__ENV__),
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :boolean}
},
%Absinthe.Blueprint.Schema.FieldDefinition{
description: "The number of the current page",
identifier: :page_number,
module: schema,
name: "page_number",
__reference__: ref(__ENV__),
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :integer}
},
%Absinthe.Blueprint.Schema.FieldDefinition{
description: "The number of the last page",
identifier: :last_page,
module: schema,
name: "last_page",
__reference__: ref(__ENV__),
type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :integer}
}
| fields
]
end

defp add_pagination_metadata(fields, _, _), do: fields

defp add_count_to_page(fields, schema, true) do
[
%Absinthe.Blueprint.Schema.FieldDefinition{
Expand Down Expand Up @@ -3865,19 +3905,10 @@ defmodule AshGraphql.Resource do
of_type: type
}
}
},
%Absinthe.Blueprint.Schema.FieldDefinition{
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this field definition since it doesn't see be used anywhere

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you say its not used anywhere, wouldn't it have been used by the current type?

It remaps more? to has_next_page. Your current code handles that though so it should be fine to remove.

     %Absinthe.Blueprint.Schema.FieldDefinition{


         description: "Whether or not there is a next page",
         identifier: :has_next_page,
         module: schema,
         name: "has_next_page",
         __reference__: ref(__ENV__),
         type: %Absinthe.Blueprint.TypeReference.NonNull{of_type: :boolean}
       },

description: "Whether or not there is a next page",
identifier: :more?,
module: schema,
name: "has_next_page",
__reference__: ref(__ENV__),
type: %Absinthe.Blueprint.TypeReference.NonNull{
of_type: :boolean
}
}
]
|> add_count_to_page(schema, countable?),
|> add_count_to_page(schema, countable?)
|> add_pagination_metadata(schema, countable?),
identifier: String.to_atom("page_of_#{type}"),
module: schema,
name: Macro.camelize("page_of_#{type}"),
Expand Down
136 changes: 136 additions & 0 deletions test/paginate_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,142 @@ defmodule AshGraphql.PaginateTest do
:ok
end

test "retruns pagination metadata" do
doc = """
query PaginatedPosts {
paginatedPosts(limit: 1, sort: [{field: TEXT}]) {
count
hasNextPage
hasPreviousPage
pageNumber
lastPage
results{
text
}
}
}
"""

assert {:ok,
%{
data: %{
"paginatedPosts" => %{
"count" => 5,
"hasNextPage" => true,
"hasPreviousPage" => false,
"pageNumber" => 1,
"lastPage" => 5,
"results" => [
%{"text" => "a"}
]
}
}
}} = Absinthe.run(doc, AshGraphql.Test.Schema)
end

test "pagination metadata is correct when offset is 2" do
doc = """
query PaginatedPosts {
paginatedPosts(limit: 1, offset: 2, sort: [{field: TEXT}]) {
count
hasNextPage
hasPreviousPage
pageNumber
lastPage
results{
text
}
}
}
"""

assert {:ok,
%{
data: %{
"paginatedPosts" => %{
"count" => 5,
"hasNextPage" => true,
"hasPreviousPage" => true,
"pageNumber" => 3,
"lastPage" => 5,
"results" => [
%{"text" => "c"}
]
}
}
}} = Absinthe.run(doc, AshGraphql.Test.Schema)
end

test "pagination metadata is correct when offset is 4" do
doc = """
query PaginatedPosts {
paginatedPosts(limit: 1, offset: 4, sort: [{field: TEXT}]) {
count
hasNextPage
hasPreviousPage
pageNumber
lastPage
results{
text
}
}
}
"""

assert {:ok,
%{
data: %{
"paginatedPosts" => %{
"count" => 5,
"hasNextPage" => false,
"hasPreviousPage" => true,
"pageNumber" => 5,
"lastPage" => 5,
"results" => [
%{"text" => "e"}
]
}
}
}} = Absinthe.run(doc, AshGraphql.Test.Schema)
end

test "pagination metadata is correct when the is no limit" do
doc = """
query PaginatedPosts {
paginatedPosts(sort: [{field: TEXT}]) {
count
hasNextPage
hasPreviousPage
pageNumber
lastPage
results{
text
}
}
}
"""

assert {:ok,
%{
data: %{
"paginatedPosts" => %{
"count" => 5,
"hasNextPage" => false,
"hasPreviousPage" => false,
"pageNumber" => 1,
"lastPage" => 1,
"results" => [
%{"text" => "a"},
%{"text" => "b"},
%{"text" => "c"},
%{"text" => "d"},
%{"text" => "e"}
]
}
}
}} = Absinthe.run(doc, AshGraphql.Test.Schema)
end

test "default_limit records are fetched" do
doc = """
query PaginatedPosts {
Expand Down
Loading