Tenex (Tenex v1.0.4)

View Source

This is the main module of Tenex.

The main objective of it is to make a little bit easier to manage tenants through postgres db schemas, executing queries and commands inside and outside the tenant without much boilerplate code.

Using the tenant

This module has useful functions to manage your tenants, like create/1, rename/2 and drop/1, but if you're trying to apply the tenant to a query, changeset or schema, stay with your application Repo, sending the prefix. Like this:

Repo.all(User, prefix: Tenex.to_prefix("my_tenant"))

It's a good idea to call Tenex.to_prefix on your tenant name, although is not required. Because, if you configured a tenant_prefix, this function will return the prefixed one.

Summary

Functions

Returns all the tenants on the given repo.

Returns a %Tenex.Config{} struct with all the args loaded from the app configuration.

Creates the given tenant on the given repo.

Creates the tenant schema/database on the given repo.

Drops the given tenant on the given repo.

Returns if the given tenant exists or not on the given repo.

Migrates the given tenant on your repo.

Returns the path for the tenant migrations on your repo.

Renames the old_tenant to the new_tenant on the given repo.

Returns if the given tenant is reserved or not.

Returns the list of reserved tenants.

Returns the value of the configured tenant field on the given map.

Returns the tenant name with the given prefix.

Functions

all(repo \\ config().repo, opts \\ [])

Returns all the tenants on the given repo.

config()

Returns a %Tenex.Config{} struct with all the args loaded from the app configuration.

create(tenant, repo \\ config().repo)

Creates the given tenant on the given repo.

Returns {:ok, tenant} if successful or {:error, reason} otherwise.

Besides creating the database itself, this function also loads their structure executing all migrations from inside priv/YOUR_REPO/tenant_migrations folder. By calling create_schema/3 sending migrate/2 as the func callback.

See migrate/2 for more details about the migration running.

Ecto 3 migrations, tenex and transactions

So on Ecto 3, migrations were changed to run on async tasks. Because of that it's not possible anymore to run Tenex.create/2 inside of a transaction anymore.

But there is a way to achieve the same results using create_schema/3 and migrate/2. Here is an example using transaction:

Repo.transaction(fn ->
  {:ok, _} = Tenex.create("tenant")
  User.insert!(%{name: "Demo user 1"})
  User.insert!(%{name: "Demo user 2"})
end)

And here is how you could achieve the same results on success or fail:

Tenex.create_schema("tenant", Repo, fn(tenant, repo) ->
  Repo.transaction(fn ->
    {:ok, _} = Tenex.migrate(tenant, repo)
    User.insert!(%{name: "Demo user 1"})
    User.insert!(%{name: "Demo user 2"})

    # the `create_schema/3` function must return `{:ok, "tenant"}`
    # if succeeded, and `Repo.transaction` transforms the function results
    # on this tuple
    tenant
  end)
end)

So, if the function given to create_schema/3 returns an error tuple, it will rollback the created schema and return that tuple to you. Check out create_schema/3 docs for more details.

create_schema(tenant, repo \\ config().repo, func \\ nil)

Creates the tenant schema/database on the given repo.

Returns {:ok, tenant} if successful or {:error, reason} otherwise.

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 successful or {:error, reason} otherwise. In the case the func fails, this func will rollback the created schema and fail with the same reason.

The function to_prefix/1 will be applied to the tenant.

drop(tenant, repo \\ config().repo)

Drops the given tenant on the given repo.

Returns {:ok, tenant} if successful or {:error, reason} otherwise.

The function to_prefix/1 will be applied to the tenant.

exists?(tenant, repo \\ config().repo)

Returns if the given tenant exists or not on the given repo.

The function to_prefix/1 will be applied to the tenant.

migrate(tenant, repo \\ config().repo)

Migrates the given tenant on your repo.

Returns {:ok, migrated_versions} if successful or {:error, reason} otherwise.

The function to_prefix/1 will be applied to the tenant.

migrations_path(repo \\ config().repo)

Returns the path for the tenant migrations on your repo.

rename(old_tenant, new_tenant, repo \\ config().repo)

Renames the old_tenant to the new_tenant on the given repo.

Returns {:ok, new_tenant} if successful or {:error, reason} otherwise.

The function to_prefix/1 will be applied to the old_tenant and new_tenant.

reserved_tenant?(tenant)

Returns if the given tenant is reserved or not.

The function to_prefix/1 will be applied to the tenant.

reserved_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_".

You also can configure your own reserved tenant names if you want with:

config :tenex, reserved_tenants: ["www", "api", ~r/^db+$/]

Notice that you can use regexes, and they will be applied to the tenant names.

tenant_field(map)

Returns the value of the configured tenant field on the given map.

to_prefix(tenant, prefix \\ config().tenant_prefix)

Returns the tenant name with the given prefix.

If the prefix is omitted, the tenant_prefix configuration from Tenex.Config will be used.

The tenant can be a string, a map or a struct. For a string it will be used as the tenant name to concat the prefix. For a map or a struct, it will get the tenant_field/0 from it to concat the prefix.