I have a read action like this:
read :get_by_name do
get? true
argument :name, :string, allow_nil?: false
filter expr(name == ^arg(:name))
prepare build(limit: 1, sort: {:updated_at, :desc}, load: :columns)
end
I want to make it so that if no results are returned, the action will create a new row for it and return that.
I think I can do that with a manual read action, so I changed the action to:
read :get_by_name do
get? true
argument :name, :string, allow_nil?: false
manual Actions.GetByName
end
And implemented GetByName
as:
defmodule Ui.Resources.TableManagement.Actions.GetByName do
@moduledoc false
use Ash.Resource.ManualRead
require Ash.Query
import Ash.Expr
def read(query, _data_layer_query, _opts, _context) do
with {:ok, nil} <- read(query),
{:ok, result} <- create(query) do
{:ok, [result]}
end
end
defp read(query) do
query
|> Ash.Query.filter(name == ^arg(:name))
|> Ash.Query.limit(1)
|> Ash.Query.sort(updated_at: :desc)
|> Ash.Query.load(:columns)
|> Ash.read_one()
end
defp create(query) do
name = Ash.Query.get_argument(query, :name)
TableManagement
|> Ash.Changeset.for_create(:create, %{name: name})
|> Ash.create()
|> Ash.load(:columns)
end
end
This will not work however, basically the Ash.read_one
call will recursively call the same action in an infinite loop.
I though about adding a Ash.Query.for_read(:read)
to my query in the read
function, but that will make the query return the following error:
{:error,
%Ash.Error.Invalid{
query: "#Query<>",
errors: [
%Ash.Error.Invalid.TimeoutNotSupported{
resource: Ui.Resources.TableManagement,
splode: Ash.Error,
bread_crumbs: [],
vars: [],
path: [],
stacktrace: #Splode.Stacktrace<>,
class: :invalid
}
]
}}
I guess I can simply create a new query from scratch, but I was wondering if there is some way to use the existing query or if I’m not supposed to use that.