Skip to content
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1877,6 +1877,10 @@ JSONAPI.configure do |config|
config.top_level_meta_include_record_count = false
config.top_level_meta_record_count_key = :record_count

# For :paged paginators, the following are also available
config.top_level_meta_include_page_count = false
config.top_level_meta_page_count_key = :page_count

config.use_text_errors = false

# List of classes that should not be rescued by the operations processor.
Expand Down
9 changes: 9 additions & 0 deletions lib/jsonapi/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class Configuration
:top_level_links_include_pagination,
:top_level_meta_include_record_count,
:top_level_meta_record_count_key,
:top_level_meta_include_page_count,
:top_level_meta_page_count_key,
:exception_class_whitelist,
:always_include_to_one_linkage_data,
:always_include_to_many_linkage_data,
Expand Down Expand Up @@ -59,6 +61,9 @@ def initialize
self.top_level_meta_include_record_count = false
self.top_level_meta_record_count_key = :record_count

self.top_level_meta_include_page_count = false
self.top_level_meta_page_count_key = :page_count

self.use_text_errors = false

# List of classes that should not be rescued by the operations processor.
Expand Down Expand Up @@ -164,6 +169,10 @@ def exception_class_whitelisted?(e)

attr_writer :top_level_meta_record_count_key

attr_writer :top_level_meta_include_page_count

attr_writer :top_level_meta_page_count_key

attr_writer :exception_class_whitelist

attr_writer :always_include_to_one_linkage_data
Expand Down
17 changes: 17 additions & 0 deletions lib/jsonapi/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def record_count
include_directives: @include_directives)
end

def page_count
if @paginator.is_a?(PagedPaginator)
@_page_count ||= @paginator.calculate_page_count(record_count)
end
end

def pagination_params
if @paginator && JSONAPI.configuration.top_level_links_include_pagination
options = {}
Expand All @@ -57,6 +63,10 @@ def apply
options[:record_count] = record_count
end

if JSONAPI.configuration.top_level_meta_include_page_count
options[:page_count] = page_count
end

return JSONAPI::ResourcesOperationResult.new(:ok,
resource_records,
options)
Expand Down Expand Up @@ -153,6 +163,12 @@ def record_count
@_record_count ||= records.count(:all)
end

def page_count
if @paginator.is_a?(PagedPaginator)
@_page_count ||= @paginator.calculate_page_count(record_count)
end
end

def source_resource
@_source_resource ||= @source_klass.find_by_key(@source_id, context: @context)
end
Expand All @@ -176,6 +192,7 @@ def options
opts = {}
opts.merge!(pagination_params: pagination_params) if JSONAPI.configuration.top_level_links_include_pagination
opts.merge!(record_count: record_count) if JSONAPI.configuration.top_level_meta_include_record_count
opts.merge!(page_count: page_count) if JSONAPI.configuration.top_level_meta_include_page_count
opts
end

Expand Down
3 changes: 2 additions & 1 deletion lib/jsonapi/operation_result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ def initialize(code, resource, options = {})
end

class ResourcesOperationResult < OperationResult
attr_accessor :resources, :pagination_params, :record_count
attr_accessor :resources, :pagination_params, :record_count, :page_count

def initialize(code, resources, options = {})
@resources = resources
@pagination_params = options.fetch(:pagination_params, {})
@record_count = options[:record_count]
@page_count = options[:page_count]
super(code, options)
end
end
Expand Down
6 changes: 5 additions & 1 deletion lib/jsonapi/paginator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,18 @@ def self.requires_record_count
true
end

def calculate_page_count(record_count)
(record_count / @size.to_f).ceil
end

def apply(relation, _order_options)
offset = (@number - 1) * @size
relation.offset(offset).limit(@size)
end

def links_page_params(options = {})
record_count = options[:record_count]
page_count = (record_count / @size.to_f).ceil
page_count = calculate_page_count(record_count)

links_page_params = {}

Expand Down
4 changes: 4 additions & 0 deletions lib/jsonapi/response_document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ def top_level_meta
if JSONAPI.configuration.top_level_meta_include_record_count && result.respond_to?(:record_count)
meta[JSONAPI.configuration.top_level_meta_record_count_key] = result.record_count
end

if JSONAPI.configuration.top_level_meta_include_page_count && result.respond_to?(:page_count)
meta[JSONAPI.configuration.top_level_meta_page_count_key] = result.page_count
end
end

meta.deep_transform_keys { |key| @key_formatter.format(key) }
Expand Down
36 changes: 36 additions & 0 deletions test/controllers/controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,15 @@ def test_show_does_not_include_records_count_in_meta
JSONAPI.configuration.top_level_meta_include_record_count = false
end

def test_show_does_not_include_pages_count_in_meta
JSONAPI.configuration.top_level_meta_include_page_count = true
get :show, { id: Post.first.id }
assert_response :success
assert_equal json_response['meta'], nil
ensure
JSONAPI.configuration.top_level_meta_include_page_count = false
end

def test_show_single_with_includes
get :show, params: {id: '1', include: 'comments'}
assert_response :success
Expand Down Expand Up @@ -2678,6 +2687,18 @@ def test_books_record_count_in_meta
assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
end

def test_books_page_count_in_meta
Api::V2::BookResource.paginator :paged
JSONAPI.configuration.top_level_meta_include_page_count = true
get :index, params: {include: 'book-comments'}
JSONAPI.configuration.top_level_meta_include_page_count = false

assert_response :success
assert_equal 91, json_response['meta']['page-count']
assert_equal 10, json_response['data'].size
assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
end

def test_books_record_count_in_meta_custom_name
Api::V2::BookResource.paginator :offset
JSONAPI.configuration.top_level_meta_include_record_count = true
Expand All @@ -2693,6 +2714,21 @@ def test_books_record_count_in_meta_custom_name
assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
end

def test_books_page_count_in_meta_custom_name
Api::V2::BookResource.paginator :paged
JSONAPI.configuration.top_level_meta_include_page_count = true
JSONAPI.configuration.top_level_meta_page_count_key = 'total_pages'

get :index, params: {include: 'book-comments'}
JSONAPI.configuration.top_level_meta_include_page_count = false
JSONAPI.configuration.top_level_meta_page_count_key = :page_count

assert_response :success
assert_equal 91, json_response['meta']['total-pages']
assert_equal 10, json_response['data'].size
assert_equal 'Book 0', json_response['data'][0]['attributes']['title']
end

def test_books_offset_pagination_no_params_includes_query_count_one_level
Api::V2::BookResource.paginator :offset

Expand Down
16 changes: 16 additions & 0 deletions test/integration/requests/request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,22 @@ def test_filter_related_resources
JSONAPI.configuration.top_level_meta_include_record_count = false
end

def test_page_count_meta
Api::V2::BookCommentResource.paginator :paged
JSONAPI.configuration.top_level_meta_include_record_count = true
JSONAPI.configuration.top_level_meta_include_page_count = true
get '/api/v2/books/1/book_comments'
assert_equal 26, json_response['meta']['record_count']
# based on default page size
assert_equal 3, json_response['meta']['page_count']
get '/api/v2/books/1/book_comments?page[size]=5'
assert_equal 26, json_response['meta']['record_count']
assert_equal 6, json_response['meta']['page_count']
ensure
JSONAPI.configuration.top_level_meta_include_record_count = false
JSONAPI.configuration.top_level_meta_include_page_count = false
end

def test_pagination_related_resources_without_related
Api::V2::BookResource.paginator :offset
Api::V2::BookCommentResource.paginator :offset
Expand Down