Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mix task to generate sql dumps of tenant templates #40

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/_build
/cover
/deps
/tmp
erl_crash.dump
*.ez
.DS_Store
14 changes: 13 additions & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@ use Mix.Config
config :apartmentex, Apartmentex.TestPostgresRepo,
hostname: "localhost",
database: "apartmentex_test",
username: "postgres",
password: "postgres",
adapter: Ecto.Adapters.Postgres,
pool: Ecto.Adapters.SQL.Sandbox
pool: Ecto.Adapters.SQL.Sandbox,
priv: "priv/repo"

config :apartmentex, Apartmentex.TestMySqlRepo,
hostname: "localhost",
database: "apartmentex_test",
username: "mysql",
password: "mysql",
adapter: Ecto.Adapters.MySql,
pool: Ecto.Adapters.SQL.Sandbox,
priv: "priv/repo"

config :logger, level: :warn
120 changes: 120 additions & 0 deletions lib/mix/tasks/apartmentex.dump.template.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
defmodule Mix.Tasks.Apartmentex.Dump.Template do
use Mix.Task

import Mix.Ecto
import Apartmentex.TenantActions
import Apartmentex.PrefixBuilder

@default_subpath "apartmentex_template"

def run(args) do
no_umbrella!("apartmentex.dump.template")
{opts, _, _} =
OptionParser.parse args, switches: [dump_path: :string], aliases: [d: :dump_path]

repo = parse_repo(args) |> List.first
path = opts[:dump_path] || default_path(repo)
config = Keyword.merge(repo.config, opts)

new_tenant(repo, "template")

dump_template(repo, path, config)

drop_tenant(repo, "template")
end

defp dump_template(repo, path, config) do
case repo.__adapter__ do
Ecto.Adapters.Postgres -> pg_dump(path, config)
Ecto.Adapters.MySQL -> mysql_dump(path, config)
end
end

defp default_path(repo) do
Path.join([source_repo_priv(repo), @default_subpath, "structure.sql"])
end

defp pg_dump(path, config) do
config = Keyword.put(config, :pg, true)
|> Keyword.put(:schema, build_prefix("template"))

File.mkdir_p!(Path.dirname(path))
{_output, 0} =
run_with_cmd("pg_dump", config, ["--file", path,
"--schema-only",
"--no-acl", "--no-owner",
config[:database]])
end

defp mysql_dump(path, config) do
config = Keyword.put(config, :mysql, true)
File.mkdir_p!(Path.dirname(path))

{output, 0} =
run_with_cmd("mysqldump", config, ["--no-data",
"--routines",
"--protocol=tcp",
"--databases", build_prefix("template")])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could get away with using ~w() here.

File.write!(path, output)
end

# this should be in a PR for Ecto to add schema specification to dump
defp run_with_cmd(cmd, opts, opt_args) do
unless System.find_executable(cmd) do
raise "could not find executable `#{cmd}` in path, " <>
"please guarantee it is available before running ecto commands"
end

env =
if opts[:pg], do: [{"PGCONNECT_TIMEOUT", "10"}], else: []

env =
cond do
opts[:pg] && opts[:password] ->
[{"PGPASSWORD", opts[:password]}|env]
opts[:mysql] && opts[:password] ->
[{"MYSQL_PWD", opts[:password]}|env]
true ->
env
end

args =
[]
args =
cond do
opts[:pg] && opts[:username] ->
["-U", opts[:username]|args]
opts[:mysql] && opts[:username] ->
["--user", opts[:username]|args]
true ->
args
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like these should be thrown in private methods. It was a bit hard for me to reason about the purpose of these.

args =
cond do
opts[:mysql] ->
port = opts[:port] || System.get_env("MYSQL_TCP_PORT") || "3306"
["--port", to_string(port)|args]
opts[:pg] && opts[:port] ->
["-p", to_string(opts[:port])|args]
true ->
args
end
args =
cond do
opts[:pg] && opts[:schema] ->
["-n", opts[:schema]|args]
true ->
args
end
args =
cond do
opts[:mysql] ->
["--host", (opts[:hostname] || System.get_env("MYSQL_HOST") || "localhost")|args]
opts[:pg] ->
["--host", (opts[:hostname] || System.get_env("PGHOST") || "localhost")|args]
end

args = args ++ opt_args
System.cmd(cmd, args, env: env, stderr_to_stdout: true)
end
end
6 changes: 5 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ defmodule Apartmentex.Mixfile do
#
# Type "mix help compile.app" for more information
def application do
[applications: [:ecto, :logger]]
[applications: applications(Mix.env)]
end

defp applications(:test), do: [:ecto, :logger, :postgrex, :mariaex]

defp applications(_), do: [:ecto, :logger]

# Dependencies can be Hex packages:
#
# {:mydep, "~> 0.3.0"}
Expand Down
6 changes: 3 additions & 3 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], []},
%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
"db_connection": {:hex, :db_connection, "1.1.0", "b2b88db6d7d12f99997b584d09fad98e560b817a20dab6a526830e339f54cdb3", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
"decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [], []},
"decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [:mix], []},
"ecto": {:hex, :ecto, "2.1.1", "fa8bdb14be9992b777036e20f183b8c4300cc012a0fae748529ff89b5423f2dd", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
"mariaex": {:hex, :mariaex, "0.8.1", "b5e359596628861ab2a277e642beed4d88455a0a02b134ede6eae2391098922a", [:mix], [{:db_connection, "~> 1.1", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [], []},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []},
"postgrex": {:hex, :postgrex, "0.13.0", "e101ab47d0725955c5c8830ae8812412992e02e4bd9db09e17abb0a5d82d09c7", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.1", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]}}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Apartmentex.TestPostgresRepo.Migrations.CreateTenantUser do
defmodule Apartmentex.Repo.Migrations.CreateTenantNotes do
use Ecto.Migration

def change do
Expand Down
30 changes: 30 additions & 0 deletions test/lib/mix/tasks/apartmentex.dump.template_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule Mix.Tasks.Apartmentex.Dump.TemplateTest do
use ExUnit.Case, async: false

import Support.FileHelpers
import Mix.Tasks.Apartmentex.Dump.Template, only: [run: 1]

alias Apartmentex.TestPostgresRepo
alias Apartmentex.TestMySqlRepo

@output Path.join(tmp_path(), "apartmentex_templates")

setup_all do
File.rm_rf!(@output)
:ok
end

test "it generates a dump file for postgres" do
run ["-r", to_string(TestPostgresRepo), "-d", Path.join(@output, "pg.sql")]
assert_file Path.join(@output, "pg.sql"), fn file ->
assert file =~ "CREATE TABLE notes"
end
end

test "it generates a dump file for mysql" do
run ["-r", to_string(TestMySqlRepo), "-d", Path.join(@output, "mysql.sql")]
assert_file Path.join(@output, "mysql.sql"), fn file ->
assert file =~ "CREATE TABLE `notes`"
end
end
end
11 changes: 10 additions & 1 deletion test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ defmodule Apartmentex.TestPostgresRepo do
use Ecto.Repo, otp_app: :apartmentex, adapter: Ecto.Adapters.Postgres, pool: Ecto.Adapters.SQL.Sandbox
end

defmodule Apartmentex.TestMySqlRepo do
use Ecto.Repo, otp_app: :apartmentex, adapter: Ecto.Adapters.MySQL, pool: Ecto.Adapters.SQL.Sandbox
end

Code.compiler_options(ignore_module_conflict: true)

Mix.Task.run "ecto.drop", ["quiet", "-r", "Apartmentex.TestMySqlRepo"]
Mix.Task.run "ecto.drop", ["quiet", "-r", "Apartmentex.TestPostgresRepo"]
Mix.Task.run "ecto.create", ["quiet", "-r", "Apartmentex.TestMySqlRepo"]
Mix.Task.run "ecto.create", ["quiet", "-r", "Apartmentex.TestPostgresRepo"]

Apartmentex.TestPostgresRepo.start_link

Apartmentex.TestMySqlRepo.start_link

ExUnit.start()

Ecto.Adapters.SQL.Sandbox.mode(Apartmentex.TestPostgresRepo, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Apartmentex.TestPostgresRepo, :auto)
Ecto.Adapters.SQL.Sandbox.mode(Apartmentex.TestMySqlRepo, :auto)