api-v2/deps/phoenix_ecto/lib/phoenix_ecto/check_repo_status.ex
2025-04-16 10:03:13 -03:00

98 lines
2.7 KiB
Elixir

defmodule Phoenix.Ecto.CheckRepoStatus do
@moduledoc """
A plug that does some checks on your application repos.
Checks if the storage is up (database is created) or if there are any pending migrations.
Both checks can raise an error if the conditions are not met.
## Plug options
* `:otp_app` - name of the application which the repos are fetched from
* `:migration_paths` - a function that accepts a repo and returns a migration directory, or a list of migration directories, that is used to check for pending migrations
* `:migration_lock` - the locking strategy used by the Ecto Adapter when checking for pending migrations. Set to `false` to disable migration locks.
* `:prefix` - the prefix used to check for pending migrations.
"""
@behaviour Plug
alias Plug.Conn
@migration_opts [:migration_lock, :prefix]
@compile {:no_warn_undefined, Ecto.Migrator}
def init(opts) do
Keyword.fetch!(opts, :otp_app)
opts
end
def call(%Conn{} = conn, opts) do
repos = Application.get_env(opts[:otp_app], :ecto_repos, [])
for repo <- repos, Process.whereis(repo) do
check_pending_migrations!(repo, opts) || check_storage_up!(repo)
end
conn
end
defp check_storage_up!(repo) do
try do
adapter = repo.__adapter__()
if Code.ensure_loaded?(adapter) && function_exported?(adapter, :storage_status, 1) do
adapter.storage_status(repo.config())
end
rescue
_ -> true
else
:down -> raise Phoenix.Ecto.StorageNotCreatedError, repo: repo
_ -> true
end
end
defp check_pending_migrations!(repo, opts) do
dirs = migration_directories(repo, opts)
migrations_fun =
Keyword.get_lazy(opts, :mock_migrations_fn, fn ->
if Code.ensure_loaded?(Ecto.Migrator),
do: &Ecto.Migrator.migrations/3,
else: fn _repo, _paths, _opts -> raise "to be rescued" end
end)
true = is_function(migrations_fun, 3)
migration_opts = Keyword.take(opts, @migration_opts)
try do
repo
|> migrations_fun.(dirs, migration_opts)
|> Enum.any?(fn {status, _version, _migration} -> status == :down end)
rescue
_ -> false
else
true ->
raise Phoenix.Ecto.PendingMigrationError,
repo: repo,
directories: dirs,
migration_opts: migration_opts
false ->
true
end
end
defp migration_directories(repo, opts) do
case Keyword.fetch(opts, :migration_paths) do
{:ok, migration_directories_fn} ->
List.wrap(migration_directories_fn.(repo))
:error ->
try do
[Ecto.Migrator.migrations_path(repo)]
rescue
_ -> []
end
end
end
end