Skip to content

Commit

Permalink
Merge d9071db into bdf19bb
Browse files Browse the repository at this point in the history
  • Loading branch information
HammamSamara committed May 14, 2021
2 parents bdf19bb + d9071db commit 615e098
Show file tree
Hide file tree
Showing 13 changed files with 50 additions and 206 deletions.
13 changes: 0 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ Configure the Repo you will use to execute the database commands with:

config :triplex, repo: ExampleApp.Repo

### Additional configuration for MySQL

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

And then create the table:

mix ecto.migrate


## Usage

Here is a quick overview of what you can do with triplex!
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
67 changes: 0 additions & 67 deletions lib/mix/tasks/triplex.mysql.install.ex

This file was deleted.

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
81 changes: 33 additions & 48 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 @@ -147,18 +150,24 @@ defmodule Triplex do
if reserved_tenant?(tenant) do
{:error, reserved_message(tenant)}
else
charset = config().mysql[:charset]
collate = config().mysql[:collate]

sql =
case repo.__adapter__ do
Ecto.Adapters.MyXQL -> "CREATE DATABASE #{to_prefix(tenant)}"
Ecto.Adapters.Postgres -> "CREATE SCHEMA \"#{to_prefix(tenant)}\""
Ecto.Adapters.MyXQL ->
"CREATE DATABASE `#{to_prefix(tenant)}` DEFAULT CHARSET #{charset} COLLATE #{collate}"

Ecto.Adapters.Postgres ->
"CREATE SCHEMA \"#{to_prefix(tenant)}\""
end

case SQL.query(repo, sql, []) do
{:ok, _} ->
with {:ok, _} <- add_to_tenants_table(tenant, repo),
{:ok, _} <- exec_func(func, tenant, repo) do
{:ok, tenant}
else
case exec_func(func, tenant, repo) do
{:ok, _} ->
{:ok, tenant}

{:error, reason} ->
drop(tenant, repo)
{:error, error_message(reason)}
Expand All @@ -178,27 +187,6 @@ defmodule Triplex do
end
end

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])

Ecto.Adapters.Postgres ->
{:ok, :skipped}
end
end

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])

Ecto.Adapters.Postgres ->
{:ok, :skipped}
end
end

defp exec_func(nil, tenant, _) do
{:ok, tenant}
end
Expand All @@ -223,14 +211,14 @@ 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

with {:ok, _} <- SQL.query(repo, sql, []),
{:ok, _} <- remove_from_tenants_table(tenant, repo) do
{:ok, tenant}
else
case SQL.query(repo, sql, []) do
{:ok, _} ->
{:ok, tenant}

{:error, exception} ->
{:error, error_message(exception)}
end
Expand Down Expand Up @@ -274,17 +262,10 @@ defmodule Triplex do
Returns all the tenants on the given `repo`.
"""
def all(repo \\ config().repo) do
sql =
case repo.__adapter__ do
Ecto.Adapters.MyXQL ->
"SELECT name FROM #{config().tenant_table}"

Ecto.Adapters.Postgres ->
"""
SELECT schema_name
FROM information_schema.schemata
"""
end
sql = """
SELECT schema_name
FROM information_schema.schemata
"""

%{rows: result} = SQL.query!(repo, sql, [])

Expand All @@ -305,7 +286,11 @@ defmodule Triplex do
sql =
case repo.__adapter__ do
Ecto.Adapters.MyXQL ->
"SELECT COUNT(*) FROM #{config().tenant_table} WHERE name = ?"
"""
SELECT COUNT(*)
FROM information_schema.schemata
WHERE schema_name = ?
"""

Ecto.Adapters.Postgres ->
"""
Expand Down
12 changes: 8 additions & 4 deletions lib/triplex/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ 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.
- `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`.
through triplex APIs. The items here can be strings or regexes.
- `mysql`: extra options to supply for the create database query for MySQL driver.
The SQL standard allows a DEFAULT CHARACTER SET clause in CREATE SCHEMA than are presently accepted by PostgreSQL.
supported options are `charset` and `collate`.
"""

defstruct [
Expand All @@ -16,6 +17,9 @@ defmodule Triplex.Config do
migrations_path: "tenant_migrations",
reserved_tenants: [],
tenant_field: :id,
tenant_table: :tenants
mysql: [
charset: "utf8mb4",
collate: "utf8mb4_bin"
]
]
end
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

0 comments on commit 615e098

Please sign in to comment.