Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ash_age.verify.ex
defmodule Mix.Tasks.AshAge.Verify do
@shortdoc "Verifies AGE database configuration"
@moduledoc """
Verifies that Apache AGE is properly configured in your database.
$ mix ash_age.verify
Connects to the database and checks:
1. The AGE extension is installed
2. The search_path includes `ag_catalog`
3. (Optional) A specific graph exists
## Options
- `-r`, `--repo` — The repo to verify against
- `-g`, `--graph` — A graph name to check for existence
## Examples
$ mix ash_age.verify
$ mix ash_age.verify --graph my_graph
$ mix ash_age.verify -r MyApp.Repo -g my_graph
"""
use Mix.Task
alias Ecto.Adapters.SQL
@impl Mix.Task
def run(args) do
{opts, _parsed} = parse_args!(args)
repos = Mix.Ecto.parse_repo(args)
repo = List.first(repos)
Mix.Ecto.ensure_repo(repo, args)
Mix.shell().info("Verifying AGE configuration for #{inspect(repo)}...\n")
{:ok, _, _} =
Ecto.Migrator.with_repo(repo, fn repo ->
results = [
check_age_extension(repo),
check_search_path(repo),
check_graph(repo, opts[:graph])
]
failures = Enum.count(results, &(&1 == :error))
Mix.shell().info("")
if failures > 0 do
Mix.shell().error("#{failures} check(s) failed. See above for details.")
else
Mix.shell().info("All checks passed!")
end
end)
end
@doc false
def parse_args!(args) do
OptionParser.parse!(args,
strict: [repo: :string, graph: :string],
aliases: [r: :repo, g: :graph]
)
end
defp check_age_extension(repo) do
query = """
SELECT EXISTS (
SELECT 1 FROM pg_extension WHERE extname = 'age'
)
"""
case SQL.query(repo, query, []) do
{:ok, %{rows: [[true]]}} ->
Mix.shell().info(" ✓ AGE extension is installed")
:ok
_ ->
Mix.shell().error(" ✗ AGE extension is NOT installed")
Mix.shell().info(" Run: CREATE EXTENSION IF NOT EXISTS age; in your database")
:error
end
end
defp check_search_path(repo) do
case SQL.query(repo, "SHOW search_path", []) do
{:ok, %{rows: [[path]]}} ->
if String.contains?(path, "ag_catalog") do
Mix.shell().info(" ✓ search_path includes ag_catalog (#{path})")
:ok
else
Mix.shell().error(" ✗ search_path does NOT include ag_catalog (#{path})")
Mix.shell().info(
" Add to your Repo config: after_connect: {AshAge.Session, :setup, []}"
)
:error
end
{:error, reason} ->
Mix.shell().error(" ✗ Could not check search_path: #{inspect(reason)}")
:error
end
end
defp check_graph(_repo, nil), do: :ok
defp check_graph(repo, graph_name) do
AshAge.Migration.validate_identifier!(graph_name)
if AshAge.Graph.exists?(repo, graph_name) do
Mix.shell().info(" ✓ Graph #{inspect(graph_name)} exists")
:ok
else
Mix.shell().error(" ✗ Graph #{inspect(graph_name)} does NOT exist")
Mix.shell().info(" Create it via migration: create_age_graph(#{inspect(graph_name)})")
:error
end
end
end