Skip to content
Merged
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
1 change: 1 addition & 0 deletions lib/multitenancy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule AshPostgres.MultiTenancy do

@tenant_name_regex ~r/^[a-zA-Z0-9_-]+$/
def create_tenant!(tenant_name, repo) do
validate_tenant_name!(tenant_name)
Ecto.Adapters.SQL.query!(repo, "CREATE SCHEMA IF NOT EXISTS \"#{tenant_name}\"", [])

migrate_tenant(tenant_name, repo)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "name",
"type": "text"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "63124167427BA3C61197814348217EFC967CDAA398102552836E26BD93E198C8",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "multitenant_named_orgs"
}
19 changes: 19 additions & 0 deletions priv/test_repo/migrations/20250519103535_migrate_resources53.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule AshPostgres.TestRepo.Migrations.MigrateResources53 do
@moduledoc """
Updates resources based on their most recent snapshots.

This file was autogenerated with `mix ash_postgres.generate_migrations`
"""

use Ecto.Migration

def up do
create table(:multitenant_named_orgs, primary_key: false) do
add(:name, :text, null: false, primary_key: true)
end
end

def down do
drop(table(:multitenant_named_orgs))
end
end
31 changes: 30 additions & 1 deletion test/multitenancy_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule AshPostgres.Test.MultitenancyTest do
use AshPostgres.RepoCase, async: false

require Ash.Query
alias AshPostgres.MultitenancyTest.{CompositeKeyPost, Org, Post, User}
alias AshPostgres.MultitenancyTest.{CompositeKeyPost, NamedOrg, Org, Post, User}
alias AshPostgres.Test.Post, as: GlobalPost

setup do
Expand Down Expand Up @@ -292,4 +292,33 @@ defmodule AshPostgres.Test.MultitenancyTest do
|> Ash.create!()
end
end

test "rejects characters other than alphanumericals, - and _ on tenant creation" do
assert_raise(
Ash.Error.Unknown,
~r/Tenant name must match ~r\/\^\[a-zA-Z0-9_-]\+\$\/, got:/,
fn ->
NamedOrg
|> Ash.Changeset.for_create(:create, %{name: "🚫"})
|> Ash.create!()
end
)
end

test "rejects characters other than alphanumericals, - and _ when renaming tenant" do
org =
NamedOrg
|> Ash.Changeset.for_create(:create, %{name: "toto"})
|> Ash.create!()

assert_raise(
Ash.Error.Unknown,
~r/Tenant name must match ~r\/\^\[a-zA-Z0-9_-]\+\$\/, got:/,
fn ->
org
|> Ash.Changeset.for_update(:update, %{name: "🚫"})
|> Ash.update!()
end
)
end
end
1 change: 1 addition & 0 deletions test/support/multitenancy/domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule AshPostgres.MultitenancyTest.Domain do

resources do
resource(AshPostgres.MultitenancyTest.Org)
resource(AshPostgres.MultitenancyTest.NamedOrg)
resource(AshPostgres.MultitenancyTest.User)
resource(AshPostgres.MultitenancyTest.Post)
resource(AshPostgres.MultitenancyTest.PostLink)
Expand Down
41 changes: 41 additions & 0 deletions test/support/multitenancy/resources/named_org.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule AshPostgres.MultitenancyTest.NamedOrg do
@moduledoc false
use Ash.Resource,
domain: AshPostgres.MultitenancyTest.Domain,
data_layer: AshPostgres.DataLayer

defimpl Ash.ToTenant do
def to_tenant(%{name: name}, resource) do
if Ash.Resource.Info.data_layer(resource) == AshPostgres.DataLayer &&
Ash.Resource.Info.multitenancy_strategy(resource) == :context do
"org_#{name}"
else
name
end
end
end

attributes do
attribute(:name, :string,
primary_key?: true,
allow_nil?: false,
public?: true,
writable?: true
)
end

actions do
default_accept(:*)

defaults([:create, :read, :update, :destroy])
end

postgres do
table "multitenant_named_orgs"
repo(AshPostgres.TestRepo)

manage_tenant do
template(["org_", :name])
end
end
end
Loading