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

Depend on information_schema for MyXQL driver #80

Merged
merged 10 commits into from
May 24, 2021
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