tfwright / live_admin

Low-config admin UI for Phoenix apps, built on LiveView
MIT License
266 stars 27 forks source link

LiveAdmin crashes when rendering a field that is list of binaries #101

Closed tmaszk closed 9 months ago

tmaszk commented 9 months ago

Describe the bug LiveAdmin crashes when rendering a field that is a list of binaries

To Reproduce

  1. Define a field on an Ecto resource that is a list of strings
    field :home_notifications, {:array, :string}
  2. Populate the filed on the record with a simple list
    ["background_check"]
  3. Try to list the record in LiveAdmin
  4. See error

Expected behavior Show the list of binaries

Screenshots Example console error

[error] GenServer #PID<0.4154.0> terminating
** (Protocol.UndefinedError) protocol String.Chars not implemented for {:safe, [[[60, "p", [], 62, ["background_check"], 60, 47, "p", 62], 10]]} of type Tuple. This protocol is implemented for the following type(s): Atom, BitString, Date, DateTime, Decimal, Explorer.Duration, Float, Floki.Selector, Floki.Selector.AttributeSelector, Floki.Selector.Combinator, Floki.Selector.Functional, Floki.Selector.PseudoClass, Integer, List, NaiveDateTime, Phoenix.LiveComponent.CID, Postgrex.Copy, Postgrex.Query, RemoteIp.Block, Time, URI, Version, Version.Requirement
    (elixir 1.15.7) lib/string/chars.ex:3: String.Chars.impl_for!/1
    (elixir 1.15.7) lib/string/chars.ex:22: String.Chars.to_string/1
    (elixir 1.15.7) lib/enum.ex:4369: Enum.map_intersperse_list/3
    (elixir 1.15.7) lib/enum.ex:1794: Enum.map_join/3
    (live_admin 0.11.4) lib/live_admin/components/resource/index.ex:124: anonymous fn/4 in LiveAdmin.Components.Container.Index.render/1
    (elixir 1.15.7) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
    (live_admin 0.11.4) lib/live_admin/components/resource/index.ex:115: anonymous fn/2 in LiveAdmin.Components.Container.Index.render/1
    (elixir 1.15.7) lib/enum.ex:1693: Enum."-map/2-lists^map/1-1-"/2
    (live_admin 0.11.4) lib/live_admin/components/resource/index.ex:103: anonymous fn/2 in LiveAdmin.Components.Container.Index.render/1
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:391: Phoenix.LiveView.Diff.traverse/7
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:766: Phoenix.LiveView.Diff.render_component/8
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:705: Phoenix.LiveView.Diff.zip_components/5
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:690: anonymous fn/4 in Phoenix.LiveView.Diff.render_pending_components/6
    (stdlib 5.1.1) maps.erl:416: :maps.fold_1/4
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:635: Phoenix.LiveView.Diff.render_pending_components/6
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/diff.ex:143: Phoenix.LiveView.Diff.render/3
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/channel.ex:953: anonymous fn/4 in Phoenix.LiveView.Channel.render_diff/3
    (telemetry 1.2.1) /Users/tmaszk/code/colife/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/channel.ex:948: Phoenix.LiveView.Channel.render_diff/3
    (phoenix_live_view 0.20.5) lib/phoenix_live_view/channel.ex:575: Phoenix.LiveView.Channel.mount_handle_params_result/3

Environment:

Additional context I think the problem is this line

defp render_field(val) when is_list(val), do: Enum.map_join(val, ", ", &render_field/1)

I think it is rendering a field within a field, which is causing the crash. I tried this locally and it worked as I would have expected

defp render_field(val) when is_list(val), do: Enum.map_join(val, ", ", &inspect/1)
tfwright commented 9 months ago

Thanks for the report. I do think this was fixed in main in https://github.com/tfwright/live_admin/commit/ed1315971c01ecb07795a6eb3a34e88c0a517cb6

I'll backport the fix and release a new version

tfwright commented 9 months ago

Just noticed the line you linked is in main, in fact the exact line in that fix. I cannot reproduce this issue in main, only 0.11.4.

Can you double check that you are using main locally when testing (not master)?

tmaszk commented 9 months ago

Sorry, I didn't refer to the right name for the main branch. I'm able to reproduce the bug on main. I just pulled main and added this one line to test_helper.exs

diff --git a/test/test_helper.exs b/test/test_helper.exs
index 693c803..c482d4e 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -98,6 +98,7 @@ defmodule LiveAdminTest.Post do
   @derive {Phoenix.Param, key: :post_id}
   schema "posts" do
     field(:title, :string)
+    field(:tags, {:array, :string}, default: ["test"])
     belongs_to(:user, LiveAdminTest.User, type: :binary_id)

     embeds_many(:previous_versions, __MODULE__.Version, on_replace: :delete)

Then when I run the unit tests I get this error:

  1) test view resource with associated resource links to user (LiveAdmin.Components.ContainerTest)
     test/live_admin/components/container_test.exs:299
     ** (exit) exited in: Phoenix.LiveViewTest.live("/live_admin_test_post/3")
         ** (EXIT) an exception was raised:
             ** (Protocol.UndefinedError) protocol String.Chars not implemented for {:safe, [[[60, "p", [], 62, ["test"], 60, 47, "p", 62], 10]]} of type Tuple
                 (elixir 1.15.7) lib/string/chars.ex:3: String.Chars.impl_for!/1
                 (elixir 1.15.7) lib/string/chars.ex:22: String.Chars.to_string/1
                 (elixir 1.15.7) lib/enum.ex:4369: Enum.map_intersperse_list/3
                 (elixir 1.15.7) lib/enum.ex:1794: Enum.map_join/3
                 (live_admin 0.11.4) lib/live_admin/components/resource/view.ex:34: anonymous fn/3 in LiveAdmin.Components.Container.View.render/1
                 (elixir 1.15.7) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
                 (live_admin 0.11.4) lib/live_admin/components/resource/view.ex:27: anonymous fn/2 in LiveAdmin.Components.Container.View.render/1
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:391: Phoenix.LiveView.Diff.traverse/7
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:765: Phoenix.LiveView.Diff.render_component/9
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:705: Phoenix.LiveView.Diff.zip_components/5
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:690: anonymous fn/4 in Phoenix.LiveView.Diff.render_pending_components/6
                 (stdlib 4.3.1.3) maps.erl:411: :maps.fold_1/3
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:635: Phoenix.LiveView.Diff.render_pending_components/6
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/diff.ex:143: Phoenix.LiveView.Diff.render/3
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/channel.ex:936: anonymous fn/4 in Phoenix.LiveView.Channel.render_diff/3
                 (telemetry 1.2.1) /opt/app/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/channel.ex:931: Phoenix.LiveView.Channel.render_diff/3
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/channel.ex:565: Phoenix.LiveView.Channel.mount_handle_params_result/3
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/channel.ex:1152: Phoenix.LiveView.Channel.verified_mount/8
                 (phoenix_live_view 0.20.3) lib/phoenix_live_view/channel.ex:84: Phoenix.LiveView.Channel.handle_info/2
     stacktrace:
       (phoenix_live_view 0.20.3) lib/phoenix_live_view/test/live_view_test.ex:400: Phoenix.LiveViewTest.start_proxy/2
       test/live_admin/components/container_test.exs:295: LiveAdmin.Components.ContainerTest.__ex_unit_setup_14_0/1
       LiveAdmin.Components.ContainerTest.__ex_unit_describe_14/1
tfwright commented 9 months ago

That's very helpful, thanks. This should now be fixed in main.