Skip to content

Latest commit

 

History

History
277 lines (199 loc) · 6.83 KB

customizing_actions.livemd

File metadata and controls

277 lines (199 loc) · 6.83 KB

Ash: 7 - 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

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:

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
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

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
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
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!()