Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 70 additions & 65 deletions lib/supavisor/tenants.ex
Original file line number Diff line number Diff line change
Expand Up @@ -84,60 +84,6 @@ defmodule Supavisor.Tenants do

def get_tenant(_, _), do: nil

@spec get_user_cache(:single | :cluster, String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user_cache(type, user, external_id, sni_hostname) do
cache_key = {:user_cache, type, user, external_id, sni_hostname}

case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
{:commit, {:cached, get_user(type, user, external_id, sni_hostname)},
ttl: :timer.hours(24)}
end) do
{_, {:cached, value}} -> value
{_, {:cached, value}, _} -> value
end
end

@spec get_user(atom(), String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user(_, _, nil, nil) do
{:error, "Either external_id or sni_hostname must be provided"}
end

def get_user(:cluster, user, external_id, sni_hostname) do
query =
from(ct in ClusterTenants,
where: ct.cluster_alias == ^external_id and ct.active == true,
limit: 1
)

case Repo.all(query) do
[%ClusterTenants{} = ct] ->
get_user(:single, user, ct.tenant_external_id, sni_hostname)

[_ | _] ->
{:error, :multiple_results}

_ ->
{:error, :not_found}
end
end

def get_user(:single, user, external_id, sni_hostname) do
query = build_user_query(user, external_id, sni_hostname)

case Repo.all(query) do
[{%User{}, %Tenant{}} = {user, tenant}] ->
{:ok, %{user: user, tenant: tenant}}

[_ | _] ->
{:error, :multiple_results}

_ ->
{:error, :not_found}
end
end

def get_pool_config(external_id, user) do
query =
from(a in User,
Expand Down Expand Up @@ -306,6 +252,76 @@ defmodule Supavisor.Tenants do
Repo.all(User)
end

@doc """
Gets a single user.

Raises `Ecto.NoResultsError` if the User does not exist.

## Examples

iex> get_user!(123)
%User{}

iex> get_user!(456)
** (Ecto.NoResultsError)

"""
def get_user!(id), do: Repo.get!(User, id)

@spec get_user_cache(:single | :cluster, String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user_cache(type, user, external_id, sni_hostname) do
cache_key = {:user_cache, type, user, external_id, sni_hostname}

case Cachex.fetch(Supavisor.Cache, cache_key, fn _key ->
{:commit, {:cached, get_user(type, user, external_id, sni_hostname)},
ttl: :timer.hours(24)}
end) do
{_, {:cached, value}} -> value
{_, {:cached, value}, _} -> value
end
end

@spec get_user(atom(), String.t(), String.t() | nil, String.t() | nil) ::
{:ok, map()} | {:error, any()}
def get_user(_, _, nil, nil) do
{:error, "Either external_id or sni_hostname must be provided"}
end

def get_user(:cluster, user, external_id, sni_hostname) do
query =
from(ct in ClusterTenants,
where: ct.cluster_alias == ^external_id and ct.active == true,
limit: 1
)

case Repo.all(query) do
[%ClusterTenants{} = ct] ->
get_user(:single, user, ct.tenant_external_id, sni_hostname)

[_ | _] ->
{:error, :multiple_results}

_ ->
{:error, :not_found}
end
end

def get_user(:single, user, external_id, sni_hostname) do
query = build_user_query(user, external_id, sni_hostname)

case Repo.all(query) do
[{%User{}, %Tenant{}} = {user, tenant}] ->
{:ok, %{user: user, tenant: tenant}}

[_ | _] ->
{:error, :multiple_results}

_ ->
{:error, :not_found}
end
end

@doc """
Creates a user.

Expand Down Expand Up @@ -424,17 +440,6 @@ defmodule Supavisor.Tenants do
"""
def get_cluster!(id), do: Repo.get!(Cluster, id)

@spec get_cluster_with_rel(String.t()) :: {:ok, Cluster.t()} | {:error, any()}
def get_cluster_with_rel(id) do
case Repo.get(Cluster, id) do
nil ->
{:error, :not_found}

cluster ->
{:ok, Repo.preload(cluster, :cluster_tenants)}
end
end

@doc """
Creates a cluster.

Expand Down
8 changes: 4 additions & 4 deletions lib/supavisor/tenants/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ defmodule Supavisor.Tenants.User do
@doc false
def changeset(user, attrs) do
attrs =
if attrs["db_user_alias"] do
attrs
else
Map.put(attrs, "db_user_alias", attrs["db_user"])
case attrs do
%{"db_user_alias" => _} -> attrs
%{"db_user" => db_user} -> Map.put(attrs, "db_user_alias", db_user)
_ -> attrs
end

user
Expand Down
151 changes: 143 additions & 8 deletions test/supavisor/tenants_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,6 @@ defmodule Supavisor.TenantsTest do
assert %Ecto.Changeset{} = Tenants.change_tenant(tenant)
end

test "get_user/4" do
_tenant = tenant_fixture()
assert {:error, :not_found} = Tenants.get_user(:single, "no_user", "no_tenant", "")

assert {:ok, %{tenant: _, user: _}} =
Tenants.get_user(:single, "postgres", "dev_tenant", "")
end

test "update_tenant_ps/2 updates the tenant's default_parameter_status" do
_tenant = tenant_fixture()
default_parameter_status = %{"server_version" => "17.0"}
Expand Down Expand Up @@ -174,6 +166,77 @@ defmodule Supavisor.TenantsTest do
end
end

describe "users" do
alias Supavisor.Tenants.User

import Supavisor.TenantsFixtures

@invalid_attrs %{
"db_user" => nil,
"db_password" => nil,
"pool_size" => nil,
"mode_type" => nil
}

test "list_users/0 returns all users" do
user = user_fixture()
assert user in Tenants.list_users()
end

test "get_user!/1 returns the user with given id" do
user = user_fixture()
assert Tenants.get_user!(user.id) == user
end

test "get_user/4" do
_tenant = tenant_fixture()
assert {:error, :not_found} = Tenants.get_user(:single, "no_user", "no_tenant", "")

assert {:ok, %{tenant: _, user: _}} =
Tenants.get_user(:single, "postgres", "dev_tenant", "")
end

test "create_user/1 with valid data creates a user" do
valid_attrs = %{
"db_user" => "some_user",
"db_password" => "some_password",
"pool_size" => 3,
"mode_type" => "transaction"
}

assert {:ok, %User{} = user} = Tenants.create_user(valid_attrs)
assert user.db_user_alias == "some_user"
assert user.db_user == "some_user"
assert user.db_password == "some_password"
assert user.pool_size == 3
assert user.mode_type == :transaction
end

test "create_user/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Tenants.create_user(@invalid_attrs)
end

test "update_user/2 with valid data updates the user" do
user = user_fixture()
update_attrs = %{"db_password" => "some_updated_password", "pool_size" => 4}

assert {:ok, %User{} = user} = Tenants.update_user(user, update_attrs)
assert user.db_password == "some_updated_password"
assert user.pool_size == 4
end

test "delete_user/1 deletes the user" do
user = user_fixture()
assert {:ok, %User{}} = Tenants.delete_user(user)
assert_raise Ecto.NoResultsError, fn -> Tenants.get_user!(user.id) end
end

test "change_user/1 returns a user changeset" do
user = user_fixture()
assert %Ecto.Changeset{} = Tenants.change_user(user)
end
end

describe "clusters" do
alias Supavisor.Tenants.Cluster

Expand Down Expand Up @@ -225,4 +288,76 @@ defmodule Supavisor.TenantsTest do
assert %Ecto.Changeset{} = Tenants.change_cluster(cluster)
end
end

describe "cluster_tenants" do
alias Supavisor.Tenants.ClusterTenants

import Supavisor.TenantsFixtures

@invalid_attrs %{type: nil, cluster_alias: nil, tenant_external_id: nil, active: nil}

test "list_cluster_tenants/0 returns all cluster_tenants" do
cluster_tenants = cluster_tenants_fixture()
assert cluster_tenants in Tenants.list_cluster_tenants()
end

test "get_cluster_tenants!/1 returns the cluster_tenants with given id" do
cluster_tenants = cluster_tenants_fixture()
assert Tenants.get_cluster_tenants!(cluster_tenants.id) == cluster_tenants
end

test "create_cluster_tenants/1 with valid data creates a cluster_tenants" do
tenant = tenant_fixture()
cluster = cluster_fixture()

valid_attrs = %{
type: "write",
cluster_alias: cluster.alias,
tenant_external_id: tenant.external_id,
active: true
}

assert {:ok, %ClusterTenants{} = cluster_tenants} =
Tenants.create_cluster_tenants(valid_attrs)

assert cluster_tenants.type == :write
assert cluster_tenants.cluster_alias == cluster.alias
assert cluster_tenants.tenant_external_id == tenant.external_id
assert cluster_tenants.active == true
end

test "create_cluster_tenants/1 with invalid data returns error changeset" do
assert {:error, %Ecto.Changeset{}} = Tenants.create_cluster_tenants(@invalid_attrs)
end

test "update_cluster_tenants/2 with valid data updates the cluster_tenants" do
valid_attrs = %{type: "read"}
cluster_tenants = cluster_tenants_fixture()

assert {:ok, %ClusterTenants{} = cluster_tenants} =
Tenants.update_cluster_tenants(cluster_tenants, valid_attrs)

assert cluster_tenants.active == true
end

test "update_cluster_tenants/2 with invalid data returns error changeset" do
cluster_tenants = cluster_tenants_fixture()

assert {:error, %Ecto.Changeset{}} =
Tenants.update_cluster_tenants(cluster_tenants, @invalid_attrs)

assert cluster_tenants == Tenants.get_cluster_tenants!(cluster_tenants.id)
end

test "delete_cluster_tenants/1 deletes the cluster_tenants" do
cluster_tenants = cluster_tenants_fixture()
assert {:ok, %ClusterTenants{}} = Tenants.delete_cluster_tenants(cluster_tenants)
assert_raise Ecto.NoResultsError, fn -> Tenants.get_cluster_tenants!(cluster_tenants.id) end
end

test "change_cluster_tenants/1 returns a cluster_tenants changeset" do
cluster_tenants = cluster_tenants_fixture()
assert %Ecto.Changeset{} = Tenants.change_cluster_tenants(cluster_tenants)
end
end
end
37 changes: 37 additions & 0 deletions test/support/fixtures/tenants_fixtures.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ defmodule Supavisor.TenantsFixtures do
tenant
end

@doc """
Generate a user.
"""
def user_fixture(attrs \\ %{}) do
{:ok, user} =
attrs
|> Enum.into(%{
"db_user" => "postgres",
"db_password" => "postgres",
"pool_size" => 3,
"mode_type" => "transaction"
})
|> Supavisor.Tenants.create_user()

user
end

# @doc """
# Generate a unique cluster tenant_external_id.
# """
Expand Down Expand Up @@ -64,4 +81,24 @@ defmodule Supavisor.TenantsFixtures do

cluster
end

@doc """
Generate a cluster tenants.
"""
def cluster_tenants_fixture(attrs \\ %{}) do
tenant = tenant_fixture()
cluster = cluster_fixture(%{cluster_tenants: []})

{:ok, cluster_tenants} =
attrs
|> Enum.into(%{
type: "write",
cluster_alias: cluster.alias,
tenant_external_id: tenant.external_id,
active: true
})
|> Supavisor.Tenants.create_cluster_tenants()

cluster_tenants
end
end
Loading