Hi
How to add generate custom JSON error for a JSON API. I see that we can do Handle Errors — ash v2.21.5 but how to generate JSON error say 409 from resource action blocks. Any pointers would be great.
Thanks
Srikanth
Hi
How to add generate custom JSON error for a JSON API. I see that we can do Handle Errors — ash v2.21.5 but how to generate JSON error say 409 from resource action blocks. Any pointers would be great.
Thanks
Srikanth
There wasn’t a way to do this, but we have had this pattern in other places for a while, so I just released a version of ash_json_api that supports this.
You would make a custom error, and implement AshJsonApi.ToJsonApiError for it.
For example:
defmodule NotAvailable do
use Ash.Error.Exception
def_ash_error([:reason], class: :invalid)
defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.ErrorKind.id(error),
status_code: 409,
code: Ash.ErrorKind.code(error),
title: Ash.ErrorKind.code(error),
detail: Ash.ErrorKind.message(error)
}
end
end
defimpl Ash.ErrorKind do
def id(_), do: Ash.UUID.generate()
def code(_), do: "not_available"
def message(error), do: "Not available: \#{error.reason}"
end
end
Then you’d use that exception:
change fn changeset, _ ->
...
Ash.Changeset.add_error(changeset, NotAvailable.exception([])
end
That is super quick Zach. Features are rolling out like chocolate from Chocolate Factory.
You indeed Willy Wonka.
Hi @zachdaniel
How to have a custom json api error in a create action. I tried your code seems for create
I have done
create action is like this
create :create do
primary?(true)
error_handler {__MODULE__, :handle_errors, []}
end
def handle_errors(changeset, error) do
case error do
%Ash.Error.Changes.InvalidAttribute{} ->
if error.message == "violates an exclusion constraint" do
Ash.Changeset.add_error(changeset, NotAvailable.exception([])
else
changeset
end
end
end
Interesting. That should work I think? Have you verified that your function is getting called and is getting to your add_error call?
Hi @zachdaniel
I have checked it is hitting the handle_errors function. Here are the stacktraces and logs
06:52:52.691 request_id=F6jjS5DwNnVpVNcAABGj [info] absence_setting.ex exclusion constraint
%AshJsonApi.Error.Conflict{
reason: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
%Ash.Error.Changes.InvalidChanges{
fields: [],
message: nil,
validation: nil,
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
warning: the following fields are unknown when raising Ash.Error.Unknown.UnknownError: [value: []]. Please make sure to only give known fields when raising or redefine Ash.Error$
(ash 2.17.22) lib/ash/error/unknown/unknown_error.ex:5: Ash.Error.Unknown.UnknownError."exception (overridable 1)"/1
(ash 2.17.22) lib/ash/error/exception.ex:58: Ash.Error.Unknown.UnknownError.exception/1
(ash 2.17.22) lib/ash/error/error.ex:441: Ash.Error.to_ash_error/3
(ash 2.17.22) lib/ash/error/error.ex:227: Ash.Error.to_error_class/2
(ash 2.17.22) lib/ash/actions/create/create.ex:132: Ash.Actions.Create.do_run/4
(ash 2.17.22) lib/ash/actions/create/create.ex:45: Ash.Actions.Create.run/4
(reservation 0.1.0) lib/reservation/reserve/api.ex:1: Reservation.Reserve.Api.create/2
(ash_json_api 0.34.2) lib/ash_json_api/controllers/helpers.ex:94: anonymous fn/1 in AshJsonApi.Controllers.Helpers.create_record/1
(ash_json_api 0.34.2) lib/ash_json_api/controllers/post.ex:19: AshJsonApi.Controllers.Post.call/2
(reservation 0.1.0) deps/plug/lib/plug/router.ex:246: anonymous fn/4 in ReservationWeb.Plugs.ReservationRouter.dispatch/2
(telemetry 1.2.1) /home/prasy/Projects/elixir/reservation/deps/telemetry/src/telemetry.erl:321: :telemetry.span/3
(reservation 0.1.0) deps/plug/lib/plug/router.ex:242: ReservationWeb.Plugs.ReservationRouter.dispatch/2
(reservation 0.1.0) lib/reservation_web/plugs/reservations_router.ex:1: ReservationWeb.Plugs.ReservationRouter.plug_builder_call/2
(phoenix 1.7.10) lib/phoenix/router/route.ex:42: Phoenix.Router.Route.call/2
(phoenix 1.7.10) lib/phoenix/router.ex:432: Phoenix.Router.__call__/5
(reservation 0.1.0) lib/reservation_web/endpoint.ex:1: ReservationWeb.Endpoint.plug_builder_call/2
(reservation 0.1.0) lib/reservation_web/endpoint.ex:1: ReservationWeb.Endpoint.call/2
(phoenix 1.7.10) lib/phoenix/test/conn_test.ex:225: Phoenix.ConnTest.dispatch/5
06:52:52.730 request_id=F6jjS5DwNnVpVNcAABGj [error] FrameworkError: Framework Error | something went wrong. %Ash.Error.Unknown.UnknownError{error: nil, field: nil, changeset: n$
.06:52:52.730 request_id=F6jjS5DwNnVpVNcAABGj [info] Sent 500 in 77ms
I am using
ash: 2.17.22
ash_json_api: 0.34.2
ash_postgres: 1.3.68
Is the rest of this line available in the logs?
06:52:52.730 request_id=F6jjS5DwNnVpVNcAABGj [error] FrameworkError: Framework Error | something went wrong. %Ash.Error.Unknown.UnknownError{error: nil, field: nil, changeset: n$
That would help me figure out where the issue is.
Hi
I am checking it. I don’t have any further logs. I am adding logs. I will post it.
Hi
If I write the handle_error like the following then its works.
What I did not understand is the handle_errors function is being invoked twice.
Not sure why.
def handle_errors(changeset, error) do
- # https://hexdocs.pm/ash/Ash.Changeset.html#handle_errors/2
case error do
%Ash.Error.Changes.InvalidAttribute{} ->
if error.message == "violates an exclusion constraint" do
Logger.info("absence_setting.ex exclusion constraint")
{changeset, AshJsonApi.Error.Conflict.exception([])}
else
:ignore
end
some_error ->
some_error
end
end
It should be invoked for each error on the changeset. Is it called with a different error each time?
First error is
%Ash.Error.Changes.InvalidAttribute{
field: :start_date,
message: "violates an exclusion constraint",
private_vars: [constraint: "overlapping_absence", constraint_type: :exclusion],
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
The second time, the error returned i.e Conflict error this is looped back
%AshJsonApi.Error.Conflict{
reason: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
But if I write the handle_error like this
def handle_errors(_changeset, error) do
# https://hexdocs.pm/ash/Ash.Changeset.html#handle_errors/2
case error do
%Ash.Error.Changes.InvalidAttribute{} ->
if error.message == "violates an exclusion constraint" do
AshJsonApi.Error.Conflict.exception([])
else
:ignore
end
some_error ->
some_error
end
end
Error is not looped back.
Interesting…I’ll need to look into it. It looks like you’ve got a workaround for now though.
Yeah it works for now. I can continue with my ash project.