diff --git a/README.md b/README.md index 792e47e4e..cb20f43c0 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lib/jsonapi/configuration.rb b/lib/jsonapi/configuration.rb index a5c715d30..586b959fb 100644 --- a/lib/jsonapi/configuration.rb +++ b/lib/jsonapi/configuration.rb @@ -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, @@ -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. @@ -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 diff --git a/lib/jsonapi/operation.rb b/lib/jsonapi/operation.rb index 9d8859b82..f51307139 100644 --- a/lib/jsonapi/operation.rb +++ b/lib/jsonapi/operation.rb @@ -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 = {} @@ -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) @@ -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 @@ -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 diff --git a/lib/jsonapi/operation_result.rb b/lib/jsonapi/operation_result.rb index a01361cad..43e996a15 100644 --- a/lib/jsonapi/operation_result.rb +++ b/lib/jsonapi/operation_result.rb @@ -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 diff --git a/lib/jsonapi/paginator.rb b/lib/jsonapi/paginator.rb index e21f97f2e..3ad00abbe 100644 --- a/lib/jsonapi/paginator.rb +++ b/lib/jsonapi/paginator.rb @@ -131,6 +131,10 @@ 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) @@ -138,7 +142,7 @@ def apply(relation, _order_options) 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 = {} diff --git a/lib/jsonapi/response_document.rb b/lib/jsonapi/response_document.rb index a4febcee9..9d3f7e8c5 100644 --- a/lib/jsonapi/response_document.rb +++ b/lib/jsonapi/response_document.rb @@ -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) } diff --git a/test/controllers/controller_test.rb b/test/controllers/controller_test.rb index 72d5b7a6e..6898a38dc 100644 --- a/test/controllers/controller_test.rb +++ b/test/controllers/controller_test.rb @@ -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 @@ -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 @@ -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 diff --git a/test/integration/requests/request_test.rb b/test/integration/requests/request_test.rb index 7f43e8323..bce4e444f 100644 --- a/test/integration/requests/request_test.rb +++ b/test/integration/requests/request_test.rb @@ -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