# 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
Attributes
Home
Relationships
### 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 ```
Attributes
Home
Relationships