Current section
Files
Jump to
Current section
Files
lib/squid_mesh.ex
defmodule SquidMesh do
@moduledoc """
Public entrypoint for the Squid Mesh runtime.
The API exposed here stays focused on declarative workflow operations. Host
applications start, inspect, and later control runs through this surface
without needing to work directly with persistence internals.
"""
alias SquidMesh.Config
alias SquidMesh.Run
alias SquidMesh.RunStore
alias SquidMesh.Runtime.Dispatcher
@doc """
Loads Squid Mesh configuration from the application environment with optional
runtime overrides.
"""
@spec config(keyword()) :: {:ok, Config.t()} | {:error, {:missing_config, [atom()]}}
defdelegate config(overrides \\ []), to: Config, as: :load
@doc """
Loads Squid Mesh configuration and raises if required keys are missing.
"""
@spec config!(keyword()) :: Config.t()
defdelegate config!(overrides \\ []), to: Config, as: :load!
@doc """
Starts a new workflow run with the given payload through the workflow's
default trigger.
"""
@spec start_run(module(), map()) ::
{:ok, Run.t()}
| {:error, {:missing_config, [atom()]}}
| {:error, RunStore.create_error()}
| {:error, {:dispatch_failed, term()}}
def start_run(workflow, payload) when is_map(payload) do
start_run(workflow, payload, [])
end
@spec start_run(module(), map(), keyword()) ::
{:ok, Run.t()}
| {:error, {:missing_config, [atom()]}}
| {:error, RunStore.create_error()}
| {:error, {:dispatch_failed, term()}}
def start_run(workflow, payload, overrides) when is_map(payload) and is_list(overrides) do
with {:ok, config} <- Config.load(overrides),
{:ok, run} <-
RunStore.create_and_dispatch_run(config.repo, workflow, payload, fn run ->
Dispatcher.dispatch_run(config, run)
end) do
SquidMesh.Observability.emit_run_created(run)
{:ok, run}
else
{:error, reason} when is_tuple(reason) and elem(reason, 0) == :invalid_run ->
{:error, reason}
{:error, reason} = error when reason in [:not_found] ->
error
{:error, %_{} = reason} ->
{:error, {:dispatch_failed, reason}}
{:error, reason} when is_tuple(reason) ->
{:error, reason}
{:error, reason} ->
{:error, {:dispatch_failed, reason}}
end
end
def start_run(_workflow, _payload, overrides) when is_list(overrides) do
{:error, {:invalid_payload, :expected_map}}
end
@doc """
Starts a new workflow run through a named trigger with the given payload.
"""
@spec start_run(module(), atom(), map()) ::
{:ok, Run.t()}
| {:error, {:missing_config, [atom()]}}
| {:error, RunStore.create_error()}
| {:error, {:dispatch_failed, term()}}
def start_run(workflow, trigger_name, payload)
when is_atom(trigger_name) and is_map(payload) do
start_run(workflow, trigger_name, payload, [])
end
@spec start_run(module(), atom(), map(), keyword()) ::
{:ok, Run.t()}
| {:error, {:missing_config, [atom()]}}
| {:error, RunStore.create_error()}
| {:error, {:dispatch_failed, term()}}
def start_run(workflow, trigger_name, payload, overrides)
when is_atom(trigger_name) and is_map(payload) and is_list(overrides) do
with {:ok, config} <- Config.load(overrides),
{:ok, run} <-
RunStore.create_and_dispatch_run(
config.repo,
workflow,
trigger_name,
payload,
fn run -> Dispatcher.dispatch_run(config, run) end
) do
SquidMesh.Observability.emit_run_created(run)
{:ok, run}
else
{:error, reason} when is_tuple(reason) and elem(reason, 0) == :invalid_run ->
{:error, reason}
{:error, reason} = error when reason in [:not_found] ->
error
{:error, %_{} = reason} ->
{:error, {:dispatch_failed, reason}}
{:error, reason} when is_tuple(reason) ->
{:error, reason}
{:error, reason} ->
{:error, {:dispatch_failed, reason}}
end
end
@doc """
Fetches one workflow run by id.
"""
@spec inspect_run(Ecto.UUID.t(), keyword()) ::
{:ok, Run.t()} | {:error, :not_found | {:missing_config, [atom()]}}
def inspect_run(run_id, overrides \\ []) do
{inspect_opts, config_overrides} = Keyword.split(overrides, [:include_history])
with {:ok, config} <- Config.load(config_overrides) do
RunStore.get_run(config.repo, run_id, inspect_opts)
end
end
@doc """
Lists workflow runs with optional filters.
"""
@spec list_runs(RunStore.list_filters(), keyword()) ::
{:ok, [Run.t()]} | {:error, {:missing_config, [atom()]}}
def list_runs(filters \\ [], overrides \\ []) do
with {:ok, config} <- Config.load(overrides) do
RunStore.list_runs(config.repo, filters)
end
end
@doc """
Requests cancellation for an eligible workflow run.
"""
@spec cancel_run(Ecto.UUID.t(), keyword()) ::
{:ok, Run.t()}
| {:error, :not_found | {:missing_config, [atom()]} | RunStore.transition_error()}
def cancel_run(run_id, overrides \\ []) do
with {:ok, config} <- Config.load(overrides) do
RunStore.cancel_run(config.repo, run_id)
end
end
@doc """
Creates a new run from a prior run and links it to the original run.
"""
@spec replay_run(Ecto.UUID.t(), keyword()) ::
{:ok, Run.t()}
| {:error, :not_found | {:missing_config, [atom()]} | RunStore.replay_error()}
| {:error, {:dispatch_failed, term()}}
def replay_run(run_id, overrides \\ []) do
with {:ok, config} <- Config.load(overrides),
{:ok, run} <-
RunStore.replay_and_dispatch_run(config.repo, run_id, fn run ->
Dispatcher.dispatch_run(config, run)
end) do
SquidMesh.Observability.emit_run_replayed(run)
{:ok, run}
else
{:error, %_{} = reason} ->
{:error, {:dispatch_failed, reason}}
{:error, reason} ->
{:error, reason}
end
end
end