issue rendering some ecto changeset errors
Closed this issue · 2 comments
Hi, i'm doing some uniqueness validations using Changeset.unsafe_validate_unique
with the following code, but ja_serializer is throwing me an error when parsing that error.
error:
** (ArgumentError) cannot convert the given list to a string.
To be converted to a string, a list must contain only:
* strings
* integers representing Unicode codepoints
* or a list containing one of these three elements
Please check the given list or call inspect/1 to get the list representation, got:
[:email]
code: conn = post(conn, api_registration_path(conn, :create), payload)
stacktrace:
(elixir) lib/list.ex:821: List.to_string/1
(ja_serializer) lib/ja_serializer/ecto_error_serializer.ex:48: anonymous fn/2 in JaSerializer.EctoErrorSerializer.format_each/2
(elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
(ja_serializer) lib/ja_serializer/ecto_error_serializer.ex:45: JaSerializer.EctoErrorSerializer.format_each/2
(elixir) lib/enum.ex:1294: Enum."-map/2-lists^map/1-0-"/2
(ja_serializer) lib/ja_serializer/ecto_error_serializer.ex:37: JaSerializer.EctoErrorSerializer.format/2
(phoenix) lib/phoenix/view.ex:332: Phoenix.View.render_to_iodata/3
(phoenix) lib/phoenix/controller.ex:740: Phoenix.Controller.do_render/4
(twd) lib/twd_web/controllers/coherence/registration_controller.ex:1: TwdWeb.Coherence.RegistrationController.action/2
(twd) lib/twd_web/controllers/coherence/registration_controller.ex:1: TwdWeb.Coherence.RegistrationController.phoenix_controller_pipeline/2
(twd) lib/twd_web/endpoint.ex:1: TwdWeb.Endpoint.instrument/4
(phoenix) lib/phoenix/router.ex:278: Phoenix.Router.__call__/1
(twd) lib/twd_web/endpoint.ex:1: TwdWeb.Endpoint.plug_builder_call/2
(twd) lib/twd_web/endpoint.ex:1: TwdWeb.Endpoint.call/2
(phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
test/controllers/coherence/registration_controller_test.exs:337: (test)
and this is my code
def changeset(model, params \\ %{}) do
model
|> cast(params, @allowed_fields)
|> validate_required(@required_fields)
|> update_change(:email, &String.downcase/1)
|> validate_format(:email, ~r/@/)
|> unsafe_validate_unique([:email], Repo)
|> unique_constraint(:email)
end
# controller code
%User{}
|> User.changeset(params)
|> Registration.register_new_user(plan)
|> case do
{:ok, user} ->
...
{:error, %Ecto.Changeset{} = changeset} ->
conn
|> put_status(:unprocessable_entity)
|> render(TwdWeb.ChangesetView, "error.json-api", changeset: changeset)
end
# TwdWeb.ChangesetView
def render("error.json-api", %{changeset: changeset}) do
JaSerializer.EctoErrorSerializer.format(changeset)
end
this is the changeset error that the unsafe_validate_unique
returns
{:error,
#Ecto.Changeset<
action: nil,
changes: %{
email: "test@test.com",
lastname: "lastname",
name: "name",
password: "rubbish",
password_confirmation: "rubbish",
password_hash: "$2b$12$WH2wtNjndDbVd0m5Qpv/rucozXigSvDfawm3d0VC0fRUIGyrctLrS",
stripe_plan_id: "mochilero",
username: "test"
},
errors: [
email: {"has already been taken",
[validation: :unsafe_unique, fields: [:email]]}
],
data: #Twd.User<>,
valid?: false
>}
while doing some debugging i found that the line who throws that error is https://github.com/vt-elixir/ja_serializer/blob/master/lib/ja_serializer/ecto_error_serializer.ex#L47
who is being called with {:email, {"has already been taken", [validation: :unsafe_unique, fields: [:email]]}}
BUT a normal changelog error, like the one presence validation returns is in the following format {:email, {"can't be blank", [validation: :required]}}
let me know if i was clear or need more info.
EDIT: i think the error is when trying to format fields: [:email]
, adding :fields -> acc
to the case seems to work. let me know if i can make a PR with that solution
title = Enum.reduce(vals, message, fn {key, value}, acc ->
case key do
:type -> acc
:fields -> acc
_ -> String.replace(acc, "%{#{key}}", to_string(value))
end
end)
thanks
@sescobb27 this would be helpful to have a PR for. I'm also running into the issue that unique validations are causing errors when serialized.