# ExTenant

## Multi Tenancy Library - based on foreign key relationship

## Installation

- The package can be installed by adding `ex_tenant` to your list of dependencies in `mix.exs`:

def deps do
    {:ex_tenant, "~> 0.2.4"}

- Run: `mix deps.get && mix deps.compile` to retrieve dependencies & compile

## How to use the library

- Call the `use ExTenant` macro to inject all the required behaviour into your
- Application Repo module to enable all multi-tenancy functions.


In the application `Config` file the `repo` and `tenanted_field` settings need to be configured.
ExTenant will default the tenanted_field setting to be `tenant_id`.


  config :ex_tenant,
    tenant_repo: MyAppRepo,
    tenanted_field: "tenant_id",
    get_tenant_from_subdomain_func: &your_application_get_tenant_from_subdomain_function/1

- If the `repo` is not configured ExTenant will raise an exception.
- the `get_tenant_from_subdomain_func` is optional if you decide to use the Plug - see example below


- In your application `Repo` file call the `use` macro as per this example

  def YourApplication.Repo do
    use ExTenant,
      config: [
        otp_app: :name_of_your_elixir_phoenix_app_as_atom,
        adapter: Ecto.Adapters.Postgres,
        tenanted_field: "tenant_id"

In order to get the `tenant_id` into the progress dictionary in Phoenix
we recommend to use a plug - there you should retrieve the `tenant name`
from something like the `sub domain` in the url. Then using the `tenant name`
retrieve the `tenant`, and call:

- Repo.put_tenant_id(tenant_id)

> To insert the `tenant_id` into the process dictionary.


- An example of how a Plug can be used to insert the tenant_id into the process dictionary

- Take a look at the module `ExTenant.SubdomainPlug`

From here all your Repo callbacks (Repo.get/ etc) will
have a where clause applied to them with the `tenant_id` injected into the clause.

In order to set the `tenant_id` on `insert` and `update` functions the tenant_id
needs to be inserted into the attributes to be inserted. The `tenanted_schema`
macro & the `tenanted()` function inserts the correct `belongs_to` tenanted foreign
key based relationship.

Further the `cast_tenant` method overloads the standard `Ecto.Changeset.cast` function
by injecting the `tenant_id` into the params and allowed keys. This function raises
exceptions when the `Repo` was not configured correctly in `config.exs` and also
if the `tenant_id` value is not set in the process_dictionary.

Ecto Schema/Changeset

  defmodule Post do
    use ExTenant.Schema
    use ExTenant.Changeset

    tenanted_schema "posts" do
      field(:name, :string)
      field(:body, :string)


    defp changeset(attrs) do
      |> cast_tenanted(params, [:name, :body])

NB: If the `tenant_id` is not set in the changeset, Repo.insert/update callbacks will raise a `Postgrex.Error` (not_null_violation)


- documentation to be added.

## Features for Querying the database

- Overrides the Ecto callback `default_options` to inject the `tenant_id`
- Overrides the Ecto callback `prepare_query` to inject the `tenant_id` into a where clause