From 9295448d20fa8953230fa2e871096120ba43a078 Mon Sep 17 00:00:00 2001 From: dania02525 Date: Sun, 12 Mar 2017 17:23:21 -0600 Subject: [PATCH] add mix task to generate sql dumps of tenant templates --- .gitignore | 1 + config/test.exs | 14 +- lib/mix/tasks/apartmentex.dump.template.ex | 120 ++++++++++++++++++ mix.exs | 6 +- mix.lock | 6 +- ...0160711125401_test_create_tenant_notes.exs | 2 +- .../tasks/apartmentex.dump.template_test.exs | 30 +++++ test/test_helper.exs | 11 +- 8 files changed, 183 insertions(+), 7 deletions(-) create mode 100644 lib/mix/tasks/apartmentex.dump.template.ex rename priv/{test_postgres_repo => repo}/tenant_migrations/20160711125401_test_create_tenant_notes.exs (60%) create mode 100644 test/lib/mix/tasks/apartmentex.dump.template_test.exs diff --git a/.gitignore b/.gitignore index a7d0bcf..d4601b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /_build /cover /deps +/tmp erl_crash.dump *.ez .DS_Store diff --git a/config/test.exs b/config/test.exs index d8a27da..709c805 100644 --- a/config/test.exs +++ b/config/test.exs @@ -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 diff --git a/lib/mix/tasks/apartmentex.dump.template.ex b/lib/mix/tasks/apartmentex.dump.template.ex new file mode 100644 index 0000000..f7c9f1d --- /dev/null +++ b/lib/mix/tasks/apartmentex.dump.template.ex @@ -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")]) + 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 + 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 diff --git a/mix.exs b/mix.exs index bf2c781..f21fe90 100644 --- a/mix.exs +++ b/mix.exs @@ -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"} diff --git a/mix.lock b/mix.lock index 3afb1de..9d500b4 100644 --- a/mix.lock +++ b/mix.lock @@ -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]}]}} diff --git a/priv/test_postgres_repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs b/priv/repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs similarity index 60% rename from priv/test_postgres_repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs rename to priv/repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs index d6034b3..41df153 100644 --- a/priv/test_postgres_repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs +++ b/priv/repo/tenant_migrations/20160711125401_test_create_tenant_notes.exs @@ -1,4 +1,4 @@ -defmodule Apartmentex.TestPostgresRepo.Migrations.CreateTenantUser do +defmodule Apartmentex.Repo.Migrations.CreateTenantNotes do use Ecto.Migration def change do diff --git a/test/lib/mix/tasks/apartmentex.dump.template_test.exs b/test/lib/mix/tasks/apartmentex.dump.template_test.exs new file mode 100644 index 0000000..f549d29 --- /dev/null +++ b/test/lib/mix/tasks/apartmentex.dump.template_test.exs @@ -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 diff --git a/test/test_helper.exs b/test/test_helper.exs index d357cb2..ba08b70 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -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)