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

Ash: 10 - Code Interfaces

code_interfaces.livemd

Ash: 10 - Code Interfaces

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)

Code Interfaces

Relationships HomeAggregates

In this tutorial you will add a Code Interface

In previous tutorials you always used Changesets directly to CRUD on resources. While this is a perfectly valid way of handling CRUD operations, Ash provides Code Interfaces to make this more ergonomic for you. The code interface can be defined on the resources or the domain. In this example, we will define it on the domain.

You will add 3 code interfaces for:

  • Creating a representative
  • Opening a ticket
  • Assigning a representative

For each resource you want to add a code interface to, inside that resource definition on the domain, you will place “definitions”. For example:

resource MyApp.Tweet do
  define :create_tweet, args: [:content]
end

Then add the 3 interfaces by defining the following inside the Ticket resource’s block:

  • define :assign_ticket, args: [:representative_id], action: :assign
  • define :open_ticket, args: [:subject, :description], action: :open

And the following inside the Representative resource’s block.

  • define :create_representative, args: [:name], action: :create

    Show Solution

    resource Tutorial.Support.Ticket do
      define :assign_ticket, args: [:representative_id], action: :assign
      define :open_ticket, args: [:subject, :description], action: :open
    end
    
    resource Tutorial.Support.Representative do
      define :create_representative, args: [:name], action: :create
    end

Enter your solution

defmodule Tutorial.Support.Ticket do
  use Ash.Resource,
    domain: Tutorial.Support,
    data_layer: Ash.DataLayer.Ets

  actions do
    defaults [:read]

    create :open do
      accept [:subject, :description]
    end

    update :close do
      change set_attribute(:status, :closed)
    end

    update :assign do
      accept [:representative_id]
    end
  end

  attributes do
    uuid_primary_key :id
    attribute :subject, :string, allow_nil?: false
    attribute :description, :string, allow_nil?: true

    attribute :status, :atom do
      constraints one_of: [:open, :closed]
      default :open
      allow_nil? false
    end

    create_timestamp :created_at
    update_timestamp :updated_at
  end

  relationships do
    belongs_to :representative, Tutorial.Support.Representative
  end
end

defmodule Tutorial.Support.Representative do
  use Ash.Resource,
    domain: Tutorial.Support,
    data_layer: Ash.DataLayer.Ets

  actions do
    defaults [:read]

    create :create do
      accept [:name]
    end
  end

  attributes do
    uuid_primary_key :id
    attribute :name, :string
  end
end

defmodule Tutorial.Support do
  use Ash.Domain

  resources do
    resource Tutorial.Support.Ticket do
      # <-- Add ticket code interface here
    end

    resource Tutorial.Support.Representative do
      # <-- Add representative code interface here
    end
  end
end

Using the Code Interfaces

Create a Representative, but instead of using a changeset, use the code interface you created.

You can use a code interface by calling Tutorial.Support.create_representative!/1 with the desired name.

Set the name to Joe Armstrong and store it in a joe variable.

Show Solution

  joe = Tutorial.Support.create_representative!("Joe Armstrong")

Create a Ticket with the created code interface.

Call Tutorial.Support.open_ticket!/2 with a subject and description and store the result in a ticket variable.

Show Solution

  ticket = Tutorial.Support.open_ticket!("I can't find my hand!", "I think someone stole it.")

Assign the Representative joe to the Ticket using the code Interface.

Use Tutorial.Support.assign_ticket!/2.

Show Solution

  Tutorial.Support.assign_ticket!(ticket, joe.id)
Managing Relationships HomeAggregates