diff --git a/README.md b/README.md index 43330b2..e97d1ce 100644 --- a/README.md +++ b/README.md @@ -46,19 +46,22 @@ In MySQL, each tenant will have its own MySQL database. Triplex uses a table called `tenants` in the main Repo to keep track of the different tenants. Generate the migration that will create the table by running: - mix triplex.mysql.install + mix triplex.mysql.install And then create the table: - mix ecto.migrate + mix ecto.migrate +Otherwise, if you wish to skip this behavior, configure Triplex to use the default `information_schema.schemata` table: + + config :triplex, tenant_table: :"information_schema.schemata" ## Usage Here is a quick overview of what you can do with triplex! -### Creating, renaming and droping tenants +### Creating, renaming and dropping tenants #### To create a new tenant: diff --git a/config/test.exs b/config/test.exs index 0c6daa7..60053d9 100644 --- a/config/test.exs +++ b/config/test.exs @@ -9,6 +9,8 @@ config :triplex, "security", "app", "staging", + "triplex_test", + "travis", ~r/^db\d+$/ ] diff --git a/lib/mix/tasks/triplex.gen.migration.ex b/lib/mix/tasks/triplex.gen.migration.ex index db64670..0710d2e 100644 --- a/lib/mix/tasks/triplex.gen.migration.ex +++ b/lib/mix/tasks/triplex.gen.migration.ex @@ -3,8 +3,8 @@ defmodule Mix.Tasks.Triplex.Gen.Migration do require Mix.Generator - alias Mix.Project alias Mix.Generator + alias Mix.Project @shortdoc "Generates a new tenant migration for the repo" diff --git a/lib/mix/tasks/triplex.migrate.ex b/lib/mix/tasks/triplex.migrate.ex index a9c7347..8a3ffa2 100644 --- a/lib/mix/tasks/triplex.migrate.ex +++ b/lib/mix/tasks/triplex.migrate.ex @@ -61,7 +61,7 @@ defmodule Mix.Tasks.Triplex.Migrate do * `--log-sql` - log the raw sql migrations are running * `--strict-version-order` - abort when applying a migration with old timestamp * `--no-compile` - does not compile applications before migrating - * `--no-deps-check` - does not check depedendencies before migrating + * `--no-deps-check` - does not check dependencies before migrating ## PS diff --git a/lib/mix/tasks/triplex.rollback.ex b/lib/mix/tasks/triplex.rollback.ex index 7fc4cff..a4442e7 100644 --- a/lib/mix/tasks/triplex.rollback.ex +++ b/lib/mix/tasks/triplex.rollback.ex @@ -57,7 +57,7 @@ defmodule Mix.Tasks.Triplex.Rollback do * `--pool-size` - the pool size if the repository is started only for the task (defaults to 1) * `--log-sql` - log the raw sql migrations are running * `--no-compile` - does not compile applications before rolling back - * `--no-deps-check` - does not check depedendencies before rolling back + * `--no-deps-check` - does not check dependencies before rolling back ## PS diff --git a/lib/triplex.ex b/lib/triplex.ex index bc132b2..9c9cd24 100644 --- a/lib/triplex.ex +++ b/lib/triplex.ex @@ -2,7 +2,7 @@ defmodule Triplex do @moduledoc """ This is the main module of Triplex. - The main objetive of it is to make a little bit easier to manage tenants + The main objective of it is to make a little bit easier to manage tenants through postgres db schemas or equivalents, executing queries and commands inside and outside the tenant without much boilerplate code. @@ -15,7 +15,7 @@ defmodule Triplex do Repo.all(User, prefix: Triplex.to_prefix("my_tenant")) - It's a good idea to call `Triplex.to_prefix` on your tenant name, altough is + It's a good idea to call `Triplex.to_prefix` on your tenant name, although is not required. Because, if you configured a `tenant_prefix`, this function will return the prefixed one. """ @@ -30,7 +30,7 @@ defmodule Triplex do def config, do: struct(Triplex.Config, Application.get_all_env(:triplex)) @doc """ - Returns the list of reserverd tenants. + Returns the list of reserved tenants. By default, there are some limitations for the name of a tenant depending on the database, like "public" or anything that start with "pg_". @@ -47,6 +47,9 @@ defmodule Triplex do nil, "public", "information_schema", + "performance_schema", + "sys", + "mysql", ~r/^pg_/ | config().reserved_tenants ] @@ -137,7 +140,7 @@ defmodule Triplex do After creating it successfully, the given `func` callback is called with the `tenant` and the `repo` as arguments. The `func` must return - `{:ok, any}` if successfull or `{:error, reason}` otherwise. In the case + `{:ok, any}` if successful or `{:error, reason}` otherwise. In the case the `func` fails, this func will rollback the created schema and fail with the same `reason`. @@ -149,7 +152,7 @@ defmodule Triplex do else sql = case repo.__adapter__ do - Ecto.Adapters.MyXQL -> "CREATE DATABASE #{to_prefix(tenant)}" + Ecto.Adapters.MyXQL -> "CREATE DATABASE `#{to_prefix(tenant)}`" Ecto.Adapters.Postgres -> "CREATE SCHEMA \"#{to_prefix(tenant)}\"" end @@ -181,8 +184,12 @@ defmodule Triplex do defp add_to_tenants_table(tenant, repo) do case repo.__adapter__ do Ecto.Adapters.MyXQL -> - sql = "INSERT INTO #{Triplex.config().tenant_table} (name) VALUES (?)" - SQL.query(repo, sql, [tenant]) + if Triplex.config().tenant_table == :"information_schema.schemata" do + {:ok, :skipped} + else + sql = "INSERT INTO #{Triplex.config().tenant_table} (name) VALUES (?)" + SQL.query(repo, sql, [tenant]) + end Ecto.Adapters.Postgres -> {:ok, :skipped} @@ -192,7 +199,11 @@ defmodule Triplex do defp remove_from_tenants_table(tenant, repo) do case repo.__adapter__ do Ecto.Adapters.MyXQL -> - SQL.query(repo, "DELETE FROM #{Triplex.config().tenant_table} WHERE NAME = ?", [tenant]) + if Triplex.config().tenant_table == :"information_schema.schemata" do + {:ok, :skipped} + else + SQL.query(repo, "DELETE FROM #{Triplex.config().tenant_table} WHERE NAME = ?", [tenant]) + end Ecto.Adapters.Postgres -> {:ok, :skipped} @@ -223,7 +234,7 @@ defmodule Triplex do else sql = case repo.__adapter__ do - Ecto.Adapters.MyXQL -> "DROP DATABASE #{to_prefix(tenant)}" + Ecto.Adapters.MyXQL -> "DROP DATABASE `#{to_prefix(tenant)}`" Ecto.Adapters.Postgres -> "DROP SCHEMA \"#{to_prefix(tenant)}\" CASCADE" end @@ -277,7 +288,12 @@ defmodule Triplex do sql = case repo.__adapter__ do Ecto.Adapters.MyXQL -> - "SELECT name FROM #{config().tenant_table}" + column_name = + if Triplex.config().tenant_table == :"information_schema.schemata", + do: "schema_name", + else: "name" + + "SELECT #{column_name} FROM `#{config().tenant_table}`" Ecto.Adapters.Postgres -> """ @@ -305,7 +321,7 @@ defmodule Triplex do sql = case repo.__adapter__ do Ecto.Adapters.MyXQL -> - "SELECT COUNT(*) FROM #{config().tenant_table} WHERE name = ?" + "SELECT COUNT(*) FROM `#{config().tenant_table}` WHERE name = ?" Ecto.Adapters.Postgres -> """ diff --git a/lib/triplex/config.ex b/lib/triplex/config.ex index 7b781ac..12f21e1 100644 --- a/lib/triplex/config.ex +++ b/lib/triplex/config.ex @@ -5,7 +5,7 @@ defmodule Triplex.Config do - `repo`: the ecto repo that will be used to execute the schema operations. - `tenant_prefix`: a prefix for all tenants. - `reserved_tenants`: a list of reserved tenants, which cannot be created - thourhg triplex APIs. The items here can be strings or regexes. + through triplex APIs. The items here can be strings or regexes. - `tenant_field`: an atom with the name of the field to get the tenant name if the given tenant is a struct. By default it's `:id`. """ diff --git a/lib/triplex/plugs/session_plug.ex b/lib/triplex/plugs/session_plug.ex index 1e7efd1..7baccb5 100644 --- a/lib/triplex/plugs/session_plug.ex +++ b/lib/triplex/plugs/session_plug.ex @@ -15,8 +15,8 @@ if Code.ensure_loaded?(Plug) do alias Plug.Conn - alias Triplex.SessionPlugConfig alias Triplex.Plug + alias Triplex.SessionPlugConfig @doc false def init(opts), do: struct(SessionPlugConfig, opts) diff --git a/lib/triplex/plugs/subdomain_plug.ex b/lib/triplex/plugs/subdomain_plug.ex index 249b83b..7edc225 100644 --- a/lib/triplex/plugs/subdomain_plug.ex +++ b/lib/triplex/plugs/subdomain_plug.ex @@ -15,8 +15,8 @@ if Code.ensure_loaded?(Plug) do alias Plug.Conn - alias Triplex.SubdomainPlugConfig alias Triplex.Plug + alias Triplex.SubdomainPlugConfig @doc false def init(opts), do: struct(SubdomainPlugConfig, opts) diff --git a/lib/triplex/plugs/subdomain_plug_config.ex b/lib/triplex/plugs/subdomain_plug_config.ex index 08784c5..2c6a43e 100644 --- a/lib/triplex/plugs/subdomain_plug_config.ex +++ b/lib/triplex/plugs/subdomain_plug_config.ex @@ -7,7 +7,7 @@ defmodule Triplex.SubdomainPlugConfig do - `tenant_handler`: function to handle the tenant param. Its return will be used as the tenant. - `assign`: the name of the assign where we must save the tenant. - - `endpoint`: the Phoenix.Endpoint to get the host name to dicover the + - `endpoint`: the Phoenix.Endpoint to get the host name to discover the subdomain. """