Skip to content

Commit

Permalink
Show create database action when there is no database
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed May 16, 2024
1 parent a893d11 commit bf49906
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 87 deletions.
89 changes: 25 additions & 64 deletions lib/phoenix_ecto/check_repo_status.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ defmodule Phoenix.Ecto.CheckRepoStatus do
repos = Application.get_env(opts[:otp_app], :ecto_repos, [])

for repo <- repos, Process.whereis(repo) do
unless check_pending_migrations!(repo, opts) do
check_storage_up!(repo)
end
check_pending_migrations!(repo, opts) || check_storage_up!(repo)
end

conn
Expand All @@ -44,84 +42,47 @@ defmodule Phoenix.Ecto.CheckRepoStatus do
adapter.storage_status(repo.config())
end
rescue
_ -> :ok
_ -> true
else
:down -> raise Phoenix.Ecto.StorageNotCreatedError, repo: repo
_ -> :ok
_ -> true
end
end

defp check_pending_migrations!(repo, opts) do
repo_status =
with {:ok, migration_directories} <- migration_directories(repo, opts),
{:ok, migrations} <- migrations(repo, migration_directories, opts) do
{:ok, migration_directories, migrations}
end
dirs = migration_directories(repo, opts)

case repo_status do
{:ok, migration_directories, migrations} ->
has_pending =
Enum.any?(migrations, fn {status, _version, _migration} -> status == :down end)
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)

if has_pending do
raise Phoenix.Ecto.PendingMigrationError, repo: repo, directories: migration_directories
else
false
end
true = is_function(migrations_fun, 3)

:error ->
# could not determine migration directories and/or migrations because Ecto.Migrator is not available
false
try do
repo
|> migrations_fun.(dirs, Keyword.take(opts, @migration_opts))
|> Enum.any?(fn {status, _version, _migration} -> status == :down end)
rescue
_ -> false
else
true -> raise Phoenix.Ecto.PendingMigrationError, repo: repo
false -> true
end
end

defp migration_directories(repo, opts) do
case Keyword.fetch(opts, :migration_paths) do
{:ok, migration_directories_fn} ->
migration_directories = migration_directories_fn.(repo)
{:ok, List.wrap(migration_directories)}

:error ->
default_migration_directory(repo, opts)
end
end

def migrations(repo, migration_directories, opts) do
migration_opts = Keyword.take(opts, @migration_opts)

case Keyword.fetch(opts, :mock_migrations_fn) do
{:ok, migration_fn} ->
migrations = get_migrations(migration_fn, repo, migration_directories, migration_opts)
{:ok, migrations}

:error ->
if Code.ensure_loaded?(Ecto.Migrator) do
{:ok, Ecto.Migrator.migrations(repo, migration_directories, migration_opts)}
else
:error
end
end
end

defp get_migrations(fun, repo, directories, _opts) when is_function(fun, 2) do
fun.(repo, directories)
end

defp get_migrations(fun, repo, directories, opts) when is_function(fun, 3) do
fun.(repo, directories, opts)
end

defp default_migration_directory(repo, opts) do
case Keyword.fetch(opts, :mock_default_migration_directory_fn) do
{:ok, migration_directories_fn} ->
migration_directories = migration_directories_fn.(repo)
{:ok, List.wrap(migration_directories)}
List.wrap(migration_directories_fn.(repo))

:error ->
if Code.ensure_loaded?(Ecto.Migrator) do
{:ok, [Ecto.Migrator.migrations_path(repo)]}
else
:error
try do
[Ecto.Migrator.migrations_path(repo)]
rescue
_ -> []
end
end
end
Expand Down
35 changes: 12 additions & 23 deletions test/phoenix_ecto/check_repo_status_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "does not raise an error when the storage is created and there are no pending migrations for a repo" do
Process.register(self(), StorageUpRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo])
mock_migrations_fn = fn _repo, _directories -> [] end
mock_migrations_fn = fn _repo, _directories, _opts -> [] end

conn = conn(:get, "/")

assert conn ==
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
after
Expand All @@ -59,15 +58,14 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "raises an error when there is no storage created for a repo" do
Process.register(self(), StorageDownRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageDownRepo])
mock_migrations_fn = fn _repo, _directories -> [] end
mock_migrations_fn = fn _repo, _directories, _opts -> raise "no migrations" end

conn = conn(:get, "/")

assert_raise(Phoenix.Ecto.StorageNotCreatedError, fn ->
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
end)
Expand All @@ -79,15 +77,14 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "raises an error when there are pending migrations for any repo" do
Process.register(self(), StorageUpRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo])
mock_migrations_fn = fn _repo, _directories -> [{:down, 1, "migration"}] end
mock_migrations_fn = fn _repo, _directories, _opts -> [{:down, 1, "migration"}] end

conn = conn(:get, "/")

assert_raise(Phoenix.Ecto.PendingMigrationError, fn ->
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
end)
Expand All @@ -96,7 +93,7 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
Process.unregister(StorageUpRepo)
end

test "supports the `ecto_migration_lock` option" do
test "supports the Ecto's migration_lock option" do
Process.register(self(), StorageUpRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo])
mock_migrations_fn = fn _repo, _directories, _opts -> [{:down, 1, "migration"}] end
Expand All @@ -107,7 +104,6 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn,
migration_lock: false
)
Expand All @@ -124,7 +120,7 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
conn = conn(:get, "/")

# set to a single directory
mock_migrations_fn = fn _repo, ["foo"] -> [{:down, 1, "migration"}] end
mock_migrations_fn = fn _repo, ["foo"], _opts -> [{:down, 1, "migration"}] end

assert_raise(Phoenix.Ecto.PendingMigrationError, fn ->
CheckRepoStatus.call(
Expand All @@ -136,7 +132,7 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
end)

# set to multiple directories
mock_migrations_fn = fn _repo, ["foo", "bar"] -> [{:down, 1, "migration"}] end
mock_migrations_fn = fn _repo, ["foo", "bar"], _opts -> [{:down, 1, "migration"}] end

assert_raise(Phoenix.Ecto.PendingMigrationError, fn ->
CheckRepoStatus.call(
Expand All @@ -154,14 +150,13 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "does not raise an error when the repo's adapter does not implement storage_status/1" do
Process.register(self(), StorageStatusRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [NoStorageStatusRepo])
mock_migrations_fn = fn _repo, _directories -> raise "failed" end
mock_migrations_fn = fn _repo, _directories, _opts -> raise "failed" end

conn = conn(:get, "/")

assert conn ==
CheckRepoStatus.call(conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
after
Expand All @@ -184,14 +179,13 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "does not raise an error when storage_status returns {:error, term()}" do
Process.register(self(), StorageErrorRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageErrorRepo])
mock_migrations_fn = fn _repo, _directories -> [] end
mock_migrations_fn = fn _repo, _directories, _opts -> [] end

conn = conn(:get, "/")

assert conn ==
CheckRepoStatus.call(conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
after
Expand All @@ -202,15 +196,14 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
test "does not raise an error when the storage is created and there are no pending migrations for multiple repos" do
Process.register(self(), StorageUpRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo, StorageUpRepo])
mock_migrations_fn = fn _repo, _directories -> [] end
mock_migrations_fn = fn _repo, _directories, _opts -> [] end

conn = conn(:get, "/")

assert conn ==
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
after
Expand All @@ -222,15 +215,14 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
Process.register(self(), StorageUpRepo)
Process.register(spawn_link(&LongLivedProcess.run/0), StorageDownRepo)
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo, StorageDownRepo])
mock_migrations_fn = fn _repo, _directories -> [] end
mock_migrations_fn = fn _repo, _directories, _opts -> raise "no migrations" end

conn = conn(:get, "/")

assert_raise(Phoenix.Ecto.StorageNotCreatedError, fn ->
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
end)
Expand All @@ -246,8 +238,8 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
Application.put_env(:check_repo_ready, :ecto_repos, [StorageUpRepo, NoStorageStatusRepo])

mock_migrations_fn = fn
StorageUpRepo, _directories -> []
NoStorageStatusRepo, _directories -> [{:down, 1, "migration"}]
StorageUpRepo, _directories, _opts -> []
NoStorageStatusRepo, _directories, _opts -> [{:down, 1, "migration"}]
end

conn = conn(:get, "/")
Expand All @@ -256,7 +248,6 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
CheckRepoStatus.call(
conn,
otp_app: :check_repo_ready,
mock_default_migration_directory_fn: &default_mock_migration_directory_fn/1,
mock_migrations_fn: mock_migrations_fn
)
end)
Expand All @@ -265,6 +256,4 @@ defmodule Phoenix.Ecto.CheckRepoStatusTest do
Process.unregister(StorageUpRepo)
Process.unregister(NoStorageStatusRepo)
end

defp default_mock_migration_directory_fn(_repo), do: "some_migration_directory"
end

0 comments on commit bf49906

Please sign in to comment.