From fcf847ddb64e4540e2849480c268cc8eb6644b68 Mon Sep 17 00:00:00 2001 From: Andrew Freeman Date: Fri, 20 Apr 2018 14:28:26 -0400 Subject: [PATCH 1/4] add page_count functionality and controller specs --- lib/jsonapi/utils/response/formatters.rb | 4 ++++ lib/jsonapi/utils/support/pagination.rb | 17 +++++++++++++++++ spec/controllers/posts_controller_spec.rb | 4 ++++ spec/controllers/users_controller_spec.rb | 13 +++++++++++++ spec/test_app.rb | 3 +++ 5 files changed, 41 insertions(+) diff --git a/lib/jsonapi/utils/response/formatters.rb b/lib/jsonapi/utils/response/formatters.rb index 899026a..c46641c 100644 --- a/lib/jsonapi/utils/response/formatters.rb +++ b/lib/jsonapi/utils/response/formatters.rb @@ -194,6 +194,10 @@ def result_options(records, options) if JSONAPI.configuration.top_level_meta_include_record_count data[:record_count] = record_count_for(records, options) end + + if JSONAPI.configuration.top_level_meta_include_page_count + data[:page_count] = page_count_for(data[:record_count]) + end end end diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index 3cff5da..6b0aa6b 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -157,6 +157,23 @@ def count_records(records, options) end end + # Count pages in order to build a proper pagination and to fill up the "page_count" response's member. + # + # @param record_count [Integer] number of records + # e.g.: 42 + # + # @return [Integer] + # e.g 5 + # + # @api private + def page_count_for(record_count) + return 0 if record_count.to_i < 1 + + size = page_params['size'] || page_params['limit'] + size = JSONAPI.configuration.default_page_size unless size.to_i.nonzero? + (record_count.to_f / size.to_i).ceil + end + # Count records from the datatase applying the given request filters # and skipping things like eager loading, grouping and sorting. # diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb index bdf6c42..9b4f3c4 100644 --- a/spec/controllers/posts_controller_spec.rb +++ b/spec/controllers/posts_controller_spec.rb @@ -94,6 +94,7 @@ expect(data.size).to eq(2) expect(response).to have_meta_record_count(4) + expect(json.dig('meta', 'page_count')).to be(2) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'next')).to be_present expect(json.dig('links', 'last')).to be_present @@ -109,6 +110,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(4) + expect(json.dig('meta', 'page_count')).to be(4) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'next')).to be_present @@ -126,6 +128,7 @@ expect(data.size).to eq(1) + expect(json.dig('meta', 'page_count')).to be(4) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'next')).not_to be_present @@ -142,6 +145,7 @@ expect(subject).to have_http_status :ok expect(subject).to have_meta_record_count(4) expect(data.size).to be <= JSONAPI.configuration.default_page_size + expect(json.dig('meta', 'page_count')).to be(1) end end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index e7971f8..56786ef 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -113,6 +113,7 @@ expect(data.size).to eq(2) expect(response).to have_meta_record_count(3) + expect(json.dig('meta', 'page_count')).to eq(2) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'next')).to be_present expect(json.dig('links', 'last')).to be_present @@ -128,6 +129,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'next')).to be_present @@ -144,6 +146,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'last')).to be_present @@ -162,6 +165,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(count) + expect(json.dig('meta', 'page_count')).to eq(1) expect(data.dig(0, 'attributes', 'full_name')).to eq(user.full_name) end end @@ -172,6 +176,7 @@ expect(response).to have_http_status :ok expect(data.size).to be <= JSONAPI.configuration.default_page_size expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(1) end end end @@ -190,6 +195,7 @@ expect(data.size).to eq(2) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(2) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'next')).to be_present expect(json.dig('links', 'last')).to be_present @@ -205,6 +211,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'next')).to be_present @@ -221,6 +228,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'last')).to be_present @@ -233,6 +241,7 @@ expect(response).to have_http_status :ok expect(data.size).to be <= JSONAPI.configuration.default_page_size expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(1) end end end @@ -251,6 +260,7 @@ expect(data.size).to eq(2) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(2) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'next')).to be_present expect(json.dig('links', 'last')).to be_present @@ -266,6 +276,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'next')).to be_present @@ -282,6 +293,7 @@ expect(data.size).to eq(1) expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(3) expect(json.dig('links', 'first')).to be_present expect(json.dig('links', 'prev')).to be_present expect(json.dig('links', 'last')).to be_present @@ -294,6 +306,7 @@ expect(response).to have_http_status :ok expect(data.size).to be <= JSONAPI.configuration.default_page_size expect(response).to have_meta_record_count(User.count) + expect(json.dig('meta', 'page_count')).to eq(1) end end end diff --git a/spec/test_app.rb b/spec/test_app.rb index 7c00004..4d5d4b4 100644 --- a/spec/test_app.rb +++ b/spec/test_app.rb @@ -16,6 +16,9 @@ config.top_level_meta_include_record_count = true config.top_level_meta_record_count_key = :record_count + + config.top_level_meta_include_page_count = true + config.top_level_meta_page_count_key = :page_count end ## From 6092a127e4bfd6c53ef5a5e41caa67f535466d95 Mon Sep 17 00:00:00 2001 From: Andrew Freeman Date: Fri, 20 Apr 2018 16:25:11 -0400 Subject: [PATCH 2/4] Adds pagination unit specs --- spec/jsonapi/utils/support/pagination_spec.rb | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/spec/jsonapi/utils/support/pagination_spec.rb b/spec/jsonapi/utils/support/pagination_spec.rb index 576b053..c1b2632 100644 --- a/spec/jsonapi/utils/support/pagination_spec.rb +++ b/spec/jsonapi/utils/support/pagination_spec.rb @@ -77,6 +77,43 @@ end end + describe '#count_pages_for' do + shared_examples_for 'counting pages' do + it 'returns the correct page count' do + allow(subject).to receive(:page_params).and_return(page_params) + expect(subject.send(:page_count_for, record_count)).to eq(page_count) + end + end + + context 'with paged paginator' do + let(:record_count) { 10 } + let(:page_count) { 2 } + let(:page_params) { { 'size' => 5 } } + it_behaves_like 'counting pages' + end + + context 'with offset paginator' do + let(:record_count) { 10 } + let(:page_count) { 2 } + let(:page_params) { { 'limit' => 5 } } + it_behaves_like 'counting pages' + end + + context 'with 0 records' do + let(:record_count) { 0 } + let(:page_count) { 0 } + let(:page_params) { {} } + it_behaves_like 'counting pages' + end + + context 'with no limit param' do + let(:record_count) { 10 } + let(:page_count) { 1 } + let(:page_params) { {} } + it_behaves_like 'counting pages' + end + end + describe '#count_records_from_database' do shared_examples_for 'skipping eager load SQL when counting records' do it 'skips any eager load for the SQL count query (default)' do From fda9d58ba1c72037895302ab9e5b591182e19e12 Mon Sep 17 00:00:00 2001 From: Andrew Freeman Date: Fri, 20 Apr 2018 16:39:48 -0400 Subject: [PATCH 3/4] Ensures page_count is customizable --- spec/features/page_count_spec.rb | 91 ++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 spec/features/page_count_spec.rb diff --git a/spec/features/page_count_spec.rb b/spec/features/page_count_spec.rb new file mode 100644 index 0000000..a85d761 --- /dev/null +++ b/spec/features/page_count_spec.rb @@ -0,0 +1,91 @@ +require 'rails_helper' + +## +# Configs +## + +# Resource +class PageCountTestResource < JSONAPI::Resource; end + +# Controller +class PageCountTestController < BaseController + def index + jsonapi_render json: User.all, options: { resource: UserResource } + end +end + +# Routes +def TestApp.draw_page_count_test_routes + JSONAPI.configuration.json_key_format = :underscored_key + + TestApp.routes.draw do + controller :page_count_test do + get :index + end + end +end + +## +# Feature Tests +## + + +describe PageCountTestController, type: :controller do + include_context 'JSON API headers' + + before(:all) do + TestApp.draw_page_count_test_routes + FactoryGirl.create_list(:user, 3, :with_posts) + end + + describe 'page count with a paged paginator' do + it 'returns the correct count' do + JSONAPI.configuration.default_paginator = :paged + + get :index, params: { page: { size: 2, number: 1 } } + + expect(json.dig('meta', 'page_count')).to eq(2) + end + end + + describe 'page count with an offset paginator' do + it 'returns the correct count' do + JSONAPI.configuration.default_paginator = :offset + + get :index, params: { page: { limit: 2, offset: 1 } } + + expect(json.dig('meta', 'page_count')).to eq(2) + end + end + + describe 'page count with a custom paginator' do + it 'returns the correct count' do + JSONAPI.configuration.default_paginator = :custom_offset + + get :index, params: { page: { limit: 2, offset: 1 } } + + expect(json.dig('meta', 'page_count')).to eq(2) + end + end + + describe 'using default limit param' do + it 'returns the correct count' do + JSONAPI.configuration.default_paginator = :offset + + get :index, params: { page: { offset: 1 } } + + expect(json.dig('meta', 'page_count')).to eq(1) + end + end + + describe 'using a custom page_count key' do + it 'returns the count with the correct key' do + JSONAPI.configuration.default_paginator = :paged + JSONAPI.configuration.top_level_meta_page_count_key = :total_pages + + get :index, params: { page: { limit: 2, offset: 1 } } + + expect(json.dig('meta', 'total_pages')).to eq(2) + end + end +end From dd817cbf0ff53f602e4493e1fcd6f6bd9639b09d Mon Sep 17 00:00:00 2001 From: Andrew Freeman Date: Sat, 21 Apr 2018 11:26:49 -0400 Subject: [PATCH 4/4] adjust type casting and add to README.md --- README.md | 3 +++ lib/jsonapi/utils/support/pagination.rb | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e39d817..3b523f4 100644 --- a/README.md +++ b/README.md @@ -464,6 +464,9 @@ JSONAPI.configure do |config| config.top_level_meta_include_record_count = true config.top_level_meta_record_count_key = :record_count + config.top_level_meta_include_page_count = true + config.top_level_meta_page_count_key = :page_count + config.use_text_errors = false config.exception_class_whitelist = [] diff --git a/lib/jsonapi/utils/support/pagination.rb b/lib/jsonapi/utils/support/pagination.rb index 6b0aa6b..c8f1eaf 100644 --- a/lib/jsonapi/utils/support/pagination.rb +++ b/lib/jsonapi/utils/support/pagination.rb @@ -169,9 +169,9 @@ def count_records(records, options) def page_count_for(record_count) return 0 if record_count.to_i < 1 - size = page_params['size'] || page_params['limit'] - size = JSONAPI.configuration.default_page_size unless size.to_i.nonzero? - (record_count.to_f / size.to_i).ceil + size = (page_params['size'] || page_params['limit']).to_i + size = JSONAPI.configuration.default_page_size unless size.nonzero? + (record_count.to_f / size).ceil end # Count records from the datatase applying the given request filters