Class: Qa::PaginationService

Inherits:
Object
  • Object
show all
Defined in:
app/services/qa/pagination_service.rb

Overview

Provide pagination processing used to respond to requests for paginated results.

Defaults for page_offset and page_limit: (see example responses under #build_response)

format == :json

  • if neither page_offset nor page_limit is passed in, then… (backward compatible)

    • returns results as an Array<Hash>

    • returns all results

    • page_offset is “1”

    • page_limit is total_num_found

  • if either page_offset or page_limit is passed in, then…

    • returns results as an Array<Hash>

    • returns a page of results

    • default for page_offset is “1”

    • default for page_limit is DEFAULT_PAGE_LIMIT (i.e., 10)

format == :jsonapi

  • response is always in the jsonapi format

  • results are always paginated

  • default for page_offset is “1”

  • default for page_limit is DEFAULT_PAGE_LIMIT (i.e., 10)

How page_offset is calculated for pagination links:

expected page boundaries

  • Expected page boundaries are always calculated starting from page_offset=1 and the current page_limit. The page boundaries will include the page_offsets that cover all results. For example, page_limit=10 with 36 results will have page boundaries 1, 11, 21, 31.

self url

  • The self url always has the page_offset for the current page, which defaults to 1 if not passed in.

first page url

  • The first page url always has page_offset=1.

last page url

  • The last page url always has page_offset equal to the last of the expected page boundaries regardless of the passed in page_offset. For the example where page_limit=10 with 36 results, the last page will always have page_offset=31.

prev page url

  • Previous’ page_offset is calculated from the passed in page_offset whether or not it is on an expected page boundary.

  • For prev, page_offset = passed in page_offset - page_limit || nil if calculated as < 1

    • when current page_offset (e.g. 1) is less than page_limit (e.g. 10), then page_offset for prev will be nil (e.g. 1 - 10 = -9 which is < 1)

    • when current page_offset is an expected page boundary (e.g. 21), then page_offset for prev will also be a page boundary (e.g. 21 - 10 = 11 which is an expected page boundary)

    • when current page_offset is not on an expected page boundary (e.g. 13), then page_offset for prev will not be on an expected page boundary (e.g. 13 - 10 = 3 which is not an expected page boundary)

next page url

  • Next’s page_offset is calculated from the passed in page_offset whether or not it is on an expected page boundary.

  • For next, page_offset = passed in page_offset + page_limit || nil if calculated > total number of results found

    • when current page_offset (e.g. 31) is greater than total number of results (e.g. 36), then page_offset for next will be nil (e.g. 31 + 10 = 41 which is > 36)

    • when current page_offset is an expected page boundary (e.g. 21), then page_offset for next will also be a page boundary (e.g. 21 + 10 = 31 which is an expected page boundary)

    • when current page_offset is not on an expected page boundary (e.g. 13), then page_offset for next will not be on an expected page boundary (e.g. 13 + 10 = 23 which is not an expected page boundary)

Constant Summary collapse

DEFAULT_PAGE_LIMIT =

Default page_limit to use if not passed in with the request.

10
ERROR_NOT_INTEGER =

Error code for page_limit and page_offset when the value is not an integer.

901
ERROR_OUT_OF_RANGE_TOO_SMALL =

Error code for page_limit and page_offset when the value is below the acceptable range (e.g. < 1).

902
ERROR_OUT_OF_RANGE_TOO_LARGE =

Error code for page_offset when the value is above the acceptable range (e.g. > total_num_found).

903

Instance Method Summary collapse

Constructor Details

#initialize(request:, results:, format: :json) ⇒ PaginationService

Returns a new instance of PaginationService.

Parameters:

  • request (ActionDispatch::Request)

    The request from the controller. To support pagination, it’s params need to respond to:

    • #page_offset [Integer] - the offset into the results for the start of the page (counts from 1; default: 1)

    • #page_limit [Integer] - the max number of records to return in a page

      • if format==:jsonapi, defaults to: DEFAULT_PAGE_LIMIT

      • if page_offset is passed in, defaults to: DEFAULT_PAGE_LIMIT

      • else when format==:json && page_offset.nil?, defaults to all results (backward compatible)

  • results (Array<Hash>)

    results of a search query as processed by the authority module

  • format (String) (defaults to: :json)
    • if present, supported values are [:json | :jsonapi]

    • when :json, the response is an array of results (default)

    • when :jsonapi, the response follows the JSON API specification

See Also:



106
107
108
109
110
111
112
# File 'app/services/qa/pagination_service.rb', line 106

def initialize(request:, results:, format: :json)
  @request = request
  @results = results
  @requested_format = format
  @page_offset_error = false
  @page_limit_error = false
end

Instance Method Details

#build_responseObject

Returns json results, optionally limited to requested page and optionally formatted according to the JSON-API standard. The default is to return just the results for backward compatibility. See examples.

Examples:

json without pagination (backward compatible) (used only if neither page_offset nor page_limit are passed in)

# request: q=term
# response: format=json, no pagination, all results
[
  { "id": "1", "label": "term 1" },
  { "id": "2", "label": "term 2" },
  ...
  { "id": "28", "label": "term 28" }
]

json with pagination (used if either page_offset or page_limit are passed in)

# request: q=term, page_offset=3, page_limit=2
# response: format=json, paginated, results 3..4
[
  { "id": "3", "label": "term 3" },
  { "id": "4", "label": "term 4" }
]

json-api with pagination using default page_offset and page_limit

# request: q=term, format=json-api
# response: format=json-api, paginated, results 1..10
{
  "data": [
    { "id": "1", "label": "term 1" },
    { "id": "2", "label": "term 2" },
    ...
    { "id": "10", "label": "term 10" }
  ],
  "meta": {
    "page": {
      "page_offset": "1",
      "page_limit": "10",
      "actual_page_size": "10",
      "total_num_found": "28",
    }
  }
  "links": {
    "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
    "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
    "prev_url": nil,
    "next_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=11",
    "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=21"
  }
}

json-api with pagination for page_offset=7 and page_limit=2

# request: q=term, format=json-api, page_offset=7, page_limit=2
# response: format=json, paginated, results 7..8
{
  "data": [
    { "id": "7", "label": "term 7" },
    { "id": "8", "label": "term 8" }
  ],
  "meta": {
    "page": {
      "page_offset": "7",
      "page_limit": "2",
      "actual_page_size": "2",
      "total_num_found": "28",
    }
  }
  "links": {
    "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=7",
    "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=1",
    "prev_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=5",
    "next_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=9",
    "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=2&page_offset=27"
  }
}

json-api with page_offset and page_limit errors

# request: q=term, format=json-api, page_offset=0, page_limit=-1
# response: format=json-api, paginated, no results, errors
{
  "data": [],
  "meta": {
    "page": {
      "page_offset": "0",
      "page_limit": "-1",
      "actual_page_size": nil,
      "total_num_found": "28",
    }
  }
  "links": {
    "self_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=-1&page_offset=0",
    "first_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=1",
    "prev_url": nil,
    "next_url": nil,
    "last_url": "http://example.com/qa/search/local/states?q=new&format=json-api&page_limit=10&page_offset=21"
  }
  "errors": [
    {
      "status" => "200",
      "source" => { "page_offset" => "0" },
      "title" => "Page Offset Out of Range",
      "detail" => "Offset 0 < 1 (first result).  Returning empty results."
    },
    {
      "status" => "200",
      "source" => { "page_limit" => "-1" },
      "title" => "Page Limit Out of Range",
      "detail" => "Page limit -1 < 1 (minimum limit).  Returning empty results."

    }
  ]
}

Returns:

  • json results, optionally limited to requested page and optionally formatted according to the JSON-API standard. The default is to return just the results for backward compatibility. See examples.

See Also:



229
230
231
# File 'app/services/qa/pagination_service.rb', line 229

def build_response
  json_api? ? build_json_api_response : build_json_response
end