# Ash: 5 - Customizing Actions
```elixir
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
### In this tutorial you will add custom Actions on the Ticket resource
Create 2 custom actions, `:open` and `:close`.
Custom actions allow you to attach *semantics* to actions.
* Instead of *Creating* a ticket, you can *Open* a ticket.
* Instead of *Updating* the status on a ticket you can *Close* it.
In addition, you can customize the behaviour of these actions. For example, closing a ticket only sets the status 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 `:subject` and `:description` to be set.
It does not make sense to set the `:status` 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.
To define the `:open` action, open a `do end` block like so:
```elixir
create :open do
end
```
Same for the `:close` action, but instead of `create`, use `update`.
You then can define the accepted attributes like so:
`accept [:subject, :description]`
Or in the case of the `:close` action:
`accept []`
Then for the `:close` action define a `change`:
`change set_attribute(:status, :closed)`
That's it, you defined your first custom actions.
Show Solution
```elixir
defmodule Tutorial.Support.Ticket do
use Ash.Resource,
domain: Tutorial.Support,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
create :open do
# By default you can provide all public attributes to an action
# This action should only accept the subject
accept [:subject, :description]
end
update :close do
# We don't want to accept any input here
accept []
change set_attribute(:status, :closed)
# A custom change could be added like so:
#
# change MyCustomChange
# change {MyCustomChange, opt: :val}
end
end
attributes do
uuid_primary_key :id
attribute :subject, :string, allow_nil?: false
attribute :description, :string
attribute :status, :atom do
constraints [one_of: [:open, :closed]]
default :open
allow_nil? false
end
create_timestamp :created_at
update_timestamp :updated_at
end
end
defmodule Tutorial.Support do
use Ash.Domain
resources do
resource Tutorial.Support.Ticket
end
end
```
### Enter your solution
```elixir
defmodule Tutorial.Support.Ticket do
use Ash.Resource,
domain: Tutorial.Support,
data_layer: Ash.DataLayer.Ets
actions do
defaults [:read]
# <-- Add the :open and :close action
end
attributes do
uuid_primary_key(:id)
attribute :subject, :string, allow_nil?: false
attribute :description, :string
attribute :status, :atom do
constraints one_of: [:open, :closed]
default :open
allow_nil? false
end
create_timestamp :created_at
update_timestamp :updated_at
end
end
defmodule Tutorial.Support do
use Ash.Domain
resources do
resource Tutorial.Support.Ticket
end
end
```
## Open a Ticket
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`.
Show Solution
```elixir
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My Subject"})
|> Ash.create!()
```
Try setting the `:status` to `:closed` and see if it works.
Show Solution
```elixir
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**
```elixir
```
Create a Ticket and store it in the `ticket` variable.
Show Solution
```elixir
ticket =
Tutorial.Support.Ticket
|> Ash.Changeset.for_create(:open, %{subject: "My Subject"})
|> Ash.create!()
```
**Enter your solution**
```elixir
```
## 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
```elixir
ticket
|> Ash.Changeset.for_update(:close)
|> Ash.update!()
```
```elixir
```