Arc.LiveView (cratis_arc v0.1.0)

Copy Markdown View Source

The Arc commands and queries, called in-process from a LiveView.

LiveView does not use the HTTP contract or generated TypeScript: it calls the same pipelines the HTTP host does, so authorization, validation and recording behave identically on both.

defmodule MyAppWeb.AuthorsLive do
  use MyAppWeb, :live_view
  use Arc.LiveView, application: MyApp.Arc

  def mount(_params, _session, socket) do
    {:ok,
     socket
     |> assign_observable(:authors, MyApp.Authors.Listing.Authors, :live_authors)
     |> assign_form(:form, MyApp.Authors.Registration.RegisterAuthor)}
  end

  def handle_event("validate", %{"command" => params}, socket),
    do: {:noreply, validate_form(socket, :form, params)}

  def handle_event("save", %{"command" => params}, socket) do
    case submit_form(socket, :form, params) do
      {:ok, _result, socket} -> {:noreply, put_flash(socket, :info, "Registered")}
      {:error, socket} -> {:noreply, socket}
    end
  end
end

Who is the caller

The principal is read from the socket's assigns each time a command or query runs, not once at mount, so a signed-out or demoted user is caught on the next call. By default Arc reads assigns.arc_principal, then assigns.current_scope.user, then assigns.current_user. Pass principal: {Module, :function} to use Arc.LiveView to resolve it your own way; the function receives the socket and returns an Arc.Principal.

Observable queries

assign_observable/5 keeps an assign current. The subscription is a process that ends with the LiveView, and the application must be started (MyApp.Arc in your supervision tree).

Summary

Functions

Assigns an empty Phoenix.HTML.Form for a command under key. Pass params to prefill it.

Assigns the caller's Arc.Identity.Result (the same data GET /.cratis/me serves) under key.

Keeps key current: assigns the data now (so the connected render never flashes empty) and again whenever the query's watched read models change or its topics are notified. Before the socket connects, it is a one-shot read.

Runs a query once and assigns its data to key, and the full Arc.QueryResult to :<key>_result. A query that fails assigns an empty list or nil.

Builds the Arc context for the current caller.

Executes a command through the full pipeline as the current caller.

Executes the command with params and updates the form under key.

Validates params against the command (authorization, declarative rules, validate/2) without recording anything, and assigns the form with its errors under key.

Functions

assign_form(socket, key, command, params \\ %{})

Assigns an empty Phoenix.HTML.Form for a command under key. Pass params to prefill it.

assign_identity(socket, key \\ :identity)

@spec assign_identity(Phoenix.LiveView.Socket.t(), atom()) ::
  Phoenix.LiveView.Socket.t()

Assigns the caller's Arc.Identity.Result (the same data GET /.cratis/me serves) under key.

An anonymous caller gets a result with authenticated?: false. An application without an identity provider gets empty details. Identity is presentation data, never an authorization decision; commands and queries authorize themselves.

assign_observable(socket, key, read_model, query, args \\ %{}, opts \\ [])

@spec assign_observable(
  Phoenix.LiveView.Socket.t(),
  atom(),
  module(),
  atom(),
  map(),
  keyword()
) ::
  Phoenix.LiveView.Socket.t()

Keeps key current: assigns the data now (so the connected render never flashes empty) and again whenever the query's watched read models change or its topics are notified. Before the socket connects, it is a one-shot read.

assign_query(socket, key, read_model, query, args \\ %{}, opts \\ [])

@spec assign_query(
  Phoenix.LiveView.Socket.t(),
  atom(),
  module(),
  atom(),
  map(),
  keyword()
) ::
  Phoenix.LiveView.Socket.t()

Runs a query once and assigns its data to key, and the full Arc.QueryResult to :<key>_result. A query that fails assigns an empty list or nil.

context(socket, opts \\ [])

Builds the Arc context for the current caller.

execute(socket, command, opts \\ [])

@spec execute(Phoenix.LiveView.Socket.t(), struct() | {module(), map()}, keyword()) ::
  {:ok, Arc.CommandResult.t()} | {:error, Arc.CommandResult.t()}

Executes a command through the full pipeline as the current caller.

Returns {:ok, result} when the command succeeded, otherwise {:error, result}.

identity(socket)

The caller's Arc.Identity.Result.

submit_form(socket, key, params)

Executes the command with params and updates the form under key.

Returns {:ok, result, socket} on success, or {:error, socket} with the errors on the form.

validate_form(socket, key, params)

@spec validate_form(Phoenix.LiveView.Socket.t(), atom(), map()) ::
  Phoenix.LiveView.Socket.t()

Validates params against the command (authorization, declarative rules, validate/2) without recording anything, and assigns the form with its errors under key.