Repository: woylie/flop_phoenix (elixir)

Issue:
Provide clearer error for incorrect pagination component usage
**Is your feature request related to a problem? Please describe.**
Recently while integration flop_phoenix into my application, I made a subtle error: I attempted to use `Flop.Phoenix.pagination` with a cursor-paginated result set. The error I got was `ArithmeticError: bad argument in arithmetic expression` which was not super-helpful.

**Describe the solution you'd like**
Ideally, the pagination component would detect that the provided `meta` is not of the correct type and raise an exception or provide some other clear indication of how to correct the problem.

**Describe alternatives you've considered**
I could document the error and give directions, but it seems better to provide that as an exception rather than expecting busy developers to read the fine print.

**Additional context**
I already have a PR written, but I wanted to follow proper procedure. I'll open it as soon as I finish typing up the issue.


Pull request description:
Raise on incorrect pagination component type
In order to provide a more useful error when there is a mismatch between the result set meta and the pagination component, detect this condition and raise an exception.

closes #363 

Patch:
diff --git a/lib/flop_phoenix.ex b/lib/flop_phoenix.ex
index 5ee49d7..a55cb32 100644
--- a/lib/flop_phoenix.ex
+++ b/lib/flop_phoenix.ex
@@ -433,6 +433,11 @@ defmodule Flop.Phoenix do
     raise Flop.Phoenix.PathOrJSError, component: :pagination
   end
 
+  def pagination(%{meta: %{start_cursor: start_cursor, end_cursor: end_cursor}})
+      when not is_nil(start_cursor) or not is_nil(end_cursor) do
+    raise Flop.Phoenix.IncorrectPaginationTypeError, component: :pagination
+  end
+
   def pagination(%{meta: meta, opts: opts, path: path} = assigns) do
     assigns =
       assigns
@@ -783,6 +788,13 @@ defmodule Flop.Phoenix do
     raise Flop.Phoenix.PathOrJSError, component: :cursor_pagination
   end
 
+  def cursor_pagination(%{
+        meta: %{total_count: total_count, total_pages: total_pages}
+      })
+      when not is_nil(total_count) or not is_nil(total_pages) do
+    raise Flop.Phoenix.IncorrectPaginationTypeError, component: :pagination
+  end
+
   def cursor_pagination(%{opts: opts} = assigns) do
     assigns = assign(assigns, :opts, CursorPagination.merge_opts(opts))
 
diff --git a/lib/flop_phoenix/errors.ex b/lib/flop_phoenix/errors.ex
index 0a54e79..4ce81af 100644
--- a/lib/flop_phoenix/errors.ex
+++ b/lib/flop_phoenix/errors.ex
@@ -102,3 +102,20 @@ defmodule Flop.Phoenix.PathOrJSError do
   defp on_examples(:table), do: "on_sort={JS.push(\"sort-table\")}"
   defp on_examples(_), do: "on_paginate={JS.push(\"paginate\")}"
 end
+
+defmodule Flop.Phoenix.IncorrectPaginationTypeError do
+  @moduledoc """
+  Raised when the pagination type used for a query is not supported by a
+  component.
+  """
+  defexception [:component]
+
+  def message(%{component: _component}) do
+    """
+    Pagination type not supported by component
+
+    - For page-based pagination, use `Flop.Phoenix.pagination/1`.
+    - For cursor-based pagination, use `Flop.Phoenix.cursor_pagination/1`.
+    """
+  end
+end
diff --git a/lib/flop_phoenix/pagination.ex b/lib/flop_phoenix/pagination.ex
index ad68f01..186b687 100644
--- a/lib/flop_phoenix/pagination.ex
+++ b/lib/flop_phoenix/pagination.ex
@@ -48,6 +48,11 @@ defmodule Flop.Phoenix.Pagination do
   def max_pages(:hide, _), do: 0
   def max_pages({:ellipsis, max_pages}, _), do: max_pages
 
+  @spec get_page_link_range(
+          non_neg_integer(),
+          non_neg_integer(),
+          non_neg_integer()
+        ) :: Range.t()
   def get_page_link_range(current_page, max_pages, total_pages) do
     # number of additional pages to show before or after current page
     additional = ceil(max_pages / 2)


Test patch:
diff --git a/test/flop_phoenix_test.exs b/test/flop_phoenix_test.exs
index d99c91f..1a2725b 100644
--- a/test/flop_phoenix_test.exs
+++ b/test/flop_phoenix_test.exs
@@ -101,6 +101,16 @@ defmodule Flop.PhoenixTest do
              """) == []
     end
 
+    test "raises an error if cursor pagination is in use" do
+      assigns = %{meta: build(:meta_with_cursors)}
+
+      assert_raise Flop.Phoenix.IncorrectPaginationTypeError, fn ->
+        parse_heex(~H"""
+        <Flop.Phoenix.pagination meta={@meta} on_paginate={%JS{}} />
+        """)
+      end
+    end
+
     test "allows to overwrite wrapper class" do
       assigns = %{meta: build(:meta_on_first_page)}
 
@@ -1141,6 +1151,16 @@ defmodule Flop.PhoenixTest do
       assert attribute(nav, "role") == "navigation"
     end
 
+    test "raises an error if an offest result set is provided" do
+      assigns = %{meta: build(:meta_on_first_page)}
+
+      assert_raise Flop.Phoenix.IncorrectPaginationTypeError, fn ->
+        parse_heex(~H"""
+        <Flop.Phoenix.cursor_pagination meta={@meta} on_paginate={%JS{}} />
+        """)
+      end
+    end
+
     test "allows to overwrite wrapper class" do
       assigns = %{
         meta: build(:meta_with_cursors),