Ash: 5 - Customizing Actions
Application.put_env(:ash, :validate_domain_resource_inclusion?, false)
Application.put_env(:ash, :validate_domain_config_inclusion?, false)
Mix.install([{:ash, "~> 3.0"}], consolidate_protocols: false)
Customizing Actions
Customizing Actions
Attributes HomeRelationshipsIn this tutorial, you will add custom actions on the Issue resource
Create 2 custom actions, :open
and :close
.
Custom actions allow you to attach semantics to actions.
- Instead of Creating an issue, you can Open an issue.
- Instead of Updating the state of an issue, you can Close it.
In addition, you can customize the behavior of these actions. For example, closing an issue only sets the state to :closed
.
It also allows you to define what attributes can be set when calling that action. For example, for the :open
action, you can only allow the :title
and :body
to be set.
It does not make sense to set the :state
in this case as it should always be :open
. For the closing action, you won’t allow any attributes to be set.
Custom actions go inside the actions do ... end
block.
actions do
defaults [:read]
# Custom actions go inside the actions do ... end block.
create :open do
# You then can define the accepted attributes like so:
accept [:title, :body]
end
# The :close action uses update.
update :close do
# No input should be accepted here
accept []
# This only changes the attribute
change set_attribute(:state, :closed)
end
end
That’s it! You defined your first custom actions.
Show Solution
defmodule BitHub.Issues.Issue do
use Ash.Resource,
domain: BitHub.Issues,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
create :open do
# This action should only accept title and body
accept [:title, :body]
end
update :close do
# No input should be accepted here
accept []
change set_attribute(:state, :closed)
end
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false
attribute :body, :string
attribute :state, :atom do
constraints [one_of: [:open, :closed]]
default :open
allow_nil? false
end
create_timestamp :created_at
update_timestamp :updated_at
end
end
defmodule BitHub.Issues do
use Ash.Domain
resources do
resource BitHub.Issues.Issue
end
end
defmodule BitHub.Issues.Issue do
use Ash.Resource,
domain: BitHub.Issues,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
# <-- Add the open and close actions here.
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false
attribute :body, :string
attribute :state, :atom do
constraints [one_of: [:open, :closed]]
default :open
allow_nil? false
end
create_timestamp :created_at
update_timestamp :updated_at
end
end
defmodule BitHub.Issues do
use Ash.Domain
resources do
resource BitHub.Issues.Issue
end
end
Open an Issue
Open a ticket.
Remember, when creating a resource, use a changeset (Ash.Changeset.for_create/3
), which gets passed to Ash.create!/1
. But in this case use the :open
argument instead of :create
. Then set the :status
to :closed
and see if it works.
Show Solution
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My Subject"})
|> Ash.create!()
Show Solution
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My Subject", status: :closed})
|> Ash.create!()
The output when trying to set :status
should look something like this:
** (Ash.Error.Invalid) Input Invalid
* Invalid value provided for status: cannot be changed.
This is because you set the accepted attributes to :subject
and :description
only.
Enter your solution
Create a Ticket and store it in the ticket
variable.
Show Solution
ticket =
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My Subject"})
|> Ash.create!()
Enter your solution
Close a Ticket
Close the ticket
you created in the previous section.
Remember to use Ash.Changeset.for_update/2
with the :close
action.
To update use Ash.update!/1
.
Show Solution
ticket
|> Ash.Changeset.for_update(:close)
|> Ash.update!()
Attributes
HomeRelationships