Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Ash: 5 - Customizing Actions

customizing_actions.livemd

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 HomeRelationships

In 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