defmodule Solicit.Plugs.Validation.PaginationParams do
@moduledoc """
Check to make sure for an endpoint that allows for pagination that both the `offset` and `page` query parameter
cannot be provided in the same call. It can only be 1 or the other.
Also that the provided `offset`, `page`, and `limit` are valid values
"""
alias Plug.Conn
alias Solicit.Response
@spec init(keyword()) :: keyword()
def init(opts \\ []), do: opts
@spec call(Conn.t(), keyword()) :: Plug.Conn.t()
def call(%{query_params: params} = conn, opts) do
max_limit = get_max_limit(opts)
offset = Map.get(params, "offset")
page = Map.get(params, "page")
limit = Map.get(params, "limit")
if pagination_params_valid?(offset, page) do
if is_valid_limit_value?(limit, max_limit) do
conn
else
struct(conn,
query_params: Map.put(conn.query_params, "limit", max_limit),
params: Map.put(conn.params, "limit", max_limit)
)
end
else
Response.unprocessable_entity(conn)
end
end
@spec pagination_params_valid?(any(), any()) :: boolean
defp pagination_params_valid?(offset, page),
do:
(is_nil(offset) or is_nil(page)) and
(is_valid_offset_value?(offset) and is_valid_page_value?(page))
@spec is_valid_offset_value?(any) :: boolean()
defp is_valid_offset_value?(offset_value) when not is_nil(offset_value) do
case Integer.parse(offset_value) do
{parsed_number, ""} ->
parsed_number > -1
:error ->
false
end
end
defp is_valid_offset_value?(_), do: true
@spec is_valid_page_value?(any) :: boolean()
defp is_valid_page_value?(page_value) when not is_nil(page_value) do
case Integer.parse(page_value) do
{parsed_number, ""} ->
parsed_number > 0
:error ->
false
end
end
defp is_valid_page_value?(_), do: true
@spec is_valid_limit_value?(any(), pos_integer()) :: boolean()
defp is_valid_limit_value?(limit, max_limit) when not is_nil(limit) do
case Integer.parse(limit) do
{parsed_number, ""} ->
parsed_number <= max_limit and parsed_number > -1
:error ->
false
end
end
defp is_valid_limit_value?(_, _), do: true
@spec get_max_limit(keyword()) :: number()
defp get_max_limit(opts),
do: Keyword.get(opts, :max_limit, Application.get_env(:solicit, :pagination_max_limit, 1000))
end