Skip to content
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ config :triplex,
"security",
"app",
"staging",
"triplex_test",
"travis",
~r/^db\d+$/
]

Expand Down
2 changes: 1 addition & 1 deletion lib/mix/tasks/triplex.gen.migration.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
2 changes: 1 addition & 1 deletion lib/mix/tasks/triplex.migrate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/mix/tasks/triplex.rollback.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
38 changes: 27 additions & 11 deletions lib/triplex.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.
"""
Expand All @@ -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_".
Expand All @@ -47,6 +47,9 @@ defmodule Triplex do
nil,
"public",
"information_schema",
"performance_schema",
"sys",
"mysql",
~r/^pg_/
| config().reserved_tenants
]
Expand Down Expand Up @@ -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`.

Expand All @@ -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

Expand Down Expand Up @@ -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}
Expand All @@ -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}
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 ->
"""
Expand Down Expand Up @@ -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 ->
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/triplex/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
"""
Expand Down
2 changes: 1 addition & 1 deletion lib/triplex/plugs/session_plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/triplex/plugs/subdomain_plug.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion lib/triplex/plugs/subdomain_plug_config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
"""

Expand Down