Add pagination with Pagy using structure from JSON:API specification

https://jsonapi.org/examples/#pagination

Update schema for collections rendered with pagination data
This commit is contained in:
Matt-Yorkley
2021-10-11 19:47:28 +01:00
committed by Maikel Linke
parent c102ce8e7e
commit 3dbf00f302
6 changed files with 187 additions and 5 deletions

View File

@@ -582,6 +582,7 @@ Metrics/MethodLength:
- 'app/controllers/spree/orders_controller.rb'
- 'app/helpers/checkout_helper.rb'
- 'app/helpers/spree/admin/navigation_helper.rb'
- "app/json_schemas/json_api_schema.rb"
- 'app/models/spree/ability.rb'
- 'app/models/spree/gateway/pay_pal_express.rb'
- 'app/models/spree/order/checkout.rb'

View File

@@ -5,6 +5,8 @@ module Api
class BaseController < ActionController::API
include CanCan::ControllerAdditions
include RequestTimeouts
include Pagy::Backend
include JsonApiPagination
check_authorization

View File

@@ -11,9 +11,9 @@ module Api
before_action :authorize_action, only: [:show, :update, :destroy]
def index
customers = search_customers
@pagy, customers = pagy(search_customers, pagy_options)
render json: Api::V1::CustomerSerializer.new(customers, is_collection: true)
render json: Api::V1::CustomerSerializer.new(customers, pagination_options)
end
def show

View File

@@ -0,0 +1,74 @@
# frozen_string_literal: true
module JsonApiPagination
extend ActiveSupport::Concern
DEFAULT_PER_PAGE = 50
MAX_PER_PAGE = 200
def pagination_options
{
is_collection: true,
meta: meta_options,
links: links_options,
}
end
def pagy_options
{ items: final_per_page_value }
end
private
def meta_options
{
pagination: {
results: @pagy.count,
pages: total_pages,
page: current_page,
per_page: final_per_page_value
}
}
end
def links_options
{
self: pagination_url(current_page),
first: pagination_url(1),
prev: pagination_url(previous_page),
next: pagination_url(next_page),
last: pagination_url(total_pages)
}
end
def pagination_url(page_number)
return if page_number.nil?
url_for(only_path: false, params: request.query_parameters.merge(page: page_number))
end
# User-specified value, or DEFAULT_PER_PAGE, capped at MAX_PER_PAGE
def final_per_page_value
(params[:per_page] || DEFAULT_PER_PAGE).to_i.clamp(1, MAX_PER_PAGE)
end
def current_page
params[:page] || 1
end
def total_pages
@pagy.pages
end
def previous_page
return nil if current_page < 2
current_page - 1
end
def next_page
return nil if current_page >= total_pages
current_page + 1
end
end

View File

@@ -22,7 +22,8 @@ class JsonApiSchema
type: :object,
properties: data_properties(**options)
},
meta: { type: :object }
meta: { type: :object },
links: { type: :object }
},
required: [:data]
}
@@ -39,9 +40,33 @@ class JsonApiSchema
properties: data_properties(**options)
}
},
meta: { type: :object }
meta: {
type: :object,
properties: {
pagination: {
type: :object,
properties: {
results: { type: :integer, example: 250 },
pages: { type: :integer, example: 5 },
page: { type: :integer, example: 2 },
per_page: { type: :integer, example: 50 },
}
}
},
required: [:pagination]
},
links: {
type: :object,
properties: {
self: { type: :string },
first: { type: :string },
prev: { type: :string, nullable: true },
next: { type: :string, nullable: true },
last: { type: :string }
}
}
},
required: [:data]
required: [:data, :meta, :links]
}
end

View File

@@ -66,6 +66,8 @@ components:
type: object
meta:
type: object
links:
type: object
required:
- data
securitySchemas:
@@ -149,8 +151,43 @@ paths:
type: object
meta:
type: object
properties:
pagination:
type: object
properties:
results:
type: integer
example: 250
pages:
type: integer
example: 5
page:
type: integer
example: 2
per_page:
type: integer
example: 50
required:
- pagination
links:
type: object
properties:
self:
type: string
first:
type: string
prev:
type: string
nullable: true
next:
type: string
nullable: true
last:
type: string
required:
- data
- meta
- links
post:
summary: Create customer
tags:
@@ -208,6 +245,8 @@ paths:
type: object
meta:
type: object
links:
type: object
required:
- data
'422':
@@ -322,6 +361,8 @@ paths:
type: object
meta:
type: object
links:
type: object
required:
- data
'404':
@@ -430,6 +471,8 @@ paths:
type: object
meta:
type: object
links:
type: object
required:
- data
'422':
@@ -546,6 +589,8 @@ paths:
type: object
meta:
type: object
links:
type: object
required:
- data
"/api/v1/enterprises/{enterprise_id}/customers":
@@ -614,7 +659,42 @@ paths:
type: object
meta:
type: object
properties:
pagination:
type: object
properties:
results:
type: integer
example: 250
pages:
type: integer
example: 5
page:
type: integer
example: 2
per_page:
type: integer
example: 50
required:
- pagination
links:
type: object
properties:
self:
type: string
first:
type: string
prev:
type: string
nullable: true
next:
type: string
nullable: true
last:
type: string
required:
- data
- meta
- links
servers:
- url: "/"