improvement: add tenant option to Ash.Seed.seed! (#1230)

This commit is contained in:
Robert Timis 2024-06-10 13:47:43 +02:00 committed by GitHub
parent 1f34fa9191
commit 6b964b9384
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 129 additions and 10 deletions

View file

@ -49,10 +49,7 @@ defmodule Ash.Seed do
Map.put(acc, key, value)
end)
seed!(
resource,
input
)
seed!(resource, input)
end
def seed!(records) when is_list(records) do
@ -63,20 +60,25 @@ defmodule Ash.Seed do
Performs a direct call to the data layer of a resource with the provided input.
If a list is provided as input, then you will get back that many results.
To set a tenant, use the tenant option.
"""
def seed!(resource, input) when is_list(input) do
Enum.map(input, &seed!(resource, &1))
def seed!(resource, input, opts \\ [])
def seed!(resource, input, opts) when is_list(input) do
Enum.map(input, &seed!(resource, &1, opts))
end
def seed!(resource, %resource{} = input) do
def seed!(resource, %resource{} = input, _opts) do
seed!(input)
end
def seed!(resource, %other{}) do
def seed!(resource, %other{}, _opts) do
raise "Cannot seed #{inspect(resource)} with an input of type #{inspect(other)}"
end
def seed!(%resource{} = record, input) when is_map(input) do
def seed!(%resource{} = record, input, opts) when is_map(input) do
attrs =
resource
|> Ash.Resource.Info.attributes()
@ -100,6 +102,8 @@ defmodule Ash.Seed do
|> change_attributes(input)
|> change_relationships(input)
|> Ash.Changeset.set_defaults(:create, true)
|> Ash.Changeset.set_tenant(opts[:tenant])
|> maybe_set_attribute_tenant()
|> create_via_data_layer()
|> case do
{:ok, result, _, _} ->
@ -110,7 +114,7 @@ defmodule Ash.Seed do
end
end
def seed!(resource, input) when is_map(input) do
def seed!(resource, input, opts) when is_map(input) do
attr_input =
input
|> Map.new(fn {key, value} ->
@ -126,6 +130,8 @@ defmodule Ash.Seed do
|> change_attributes(attr_input)
|> change_relationships(input)
|> Ash.Changeset.set_defaults(:create, true)
|> Ash.Changeset.set_tenant(opts[:tenant])
|> maybe_set_attribute_tenant()
|> create_via_data_layer()
|> case do
{:ok, result, _, _} ->
@ -315,4 +321,17 @@ defmodule Ash.Seed do
Map.put(input, field, field_value)
)
end
defp maybe_set_attribute_tenant(changeset) do
if changeset.tenant &&
Ash.Resource.Info.multitenancy_strategy(changeset.resource) == :attribute do
attribute = Ash.Resource.Info.multitenancy_attribute(changeset.resource)
{m, f, a} = Ash.Resource.Info.multitenancy_parse_attribute(changeset.resource)
attribute_value = apply(m, f, [changeset.to_tenant | a])
Ash.Changeset.force_change_attribute(changeset, attribute, attribute_value)
else
changeset
end
end
end

View file

@ -6,6 +6,79 @@ defmodule Ash.Test.SeedTest do
require Ash.Query
alias Ash.Test.Domain, as: Domain
defmodule Tenant do
use Ash.Resource,
domain: Domain,
data_layer: Ash.DataLayer.Ets
ets do
private?(true)
end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
end
attributes do
uuid_primary_key :id
attribute :name, :string, public?: true, allow_nil?: false
end
end
defmodule ContextUser do
use Ash.Resource,
domain: Domain,
data_layer: Ash.DataLayer.Ets
ets do
private?(true)
end
multitenancy do
strategy :context
end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
end
attributes do
uuid_primary_key :id
attribute :name, :string, public?: true, allow_nil?: false
end
end
defmodule AttributeUser do
use Ash.Resource,
domain: Domain,
data_layer: Ash.DataLayer.Ets
ets do
private?(true)
end
multitenancy do
strategy :attribute
attribute :tenant_id
end
actions do
default_accept :*
defaults [:read, :destroy, create: :*, update: :*]
end
attributes do
uuid_primary_key :id
attribute :name, :string, public?: true, allow_nil?: false
end
relationships do
belongs_to :tenant, Ash.Test.SeedTest.Tenant
end
end
defmodule Author do
@moduledoc false
use Ash.Resource,
@ -269,5 +342,32 @@ defmodule Ash.Test.SeedTest do
assert Enum.count(Ash.read!(Rating)) == 2
assert Enum.count(Ash.read!(Author)) == 1
end
test "context multitenancy works" do
tenant = seed!(%Tenant{name: "Tenant"})
tenant_id = tenant.id
user = seed!(ContextUser, %{name: "User"}, tenant: tenant_id)
user_id = user.id
assert {:ok, %ContextUser{id: ^user_id}} = Ash.get(ContextUser, user_id, tenant: tenant_id)
assert {:error, %{errors: [%Ash.Error.Query.NotFound{}]}} =
Ash.get(ContextUser, user_id, tenant: "random")
end
test "attribute multitenancy works" do
tenant = seed!(%Tenant{name: "Tenant"})
tenant_id = tenant.id
user = seed!(AttributeUser, %{name: "User"}, tenant: tenant_id)
user_id = user.id
assert {:ok, %AttributeUser{id: ^user_id}} =
Ash.get(AttributeUser, user_id, tenant: tenant_id)
assert {:error, %{errors: [%Ash.Error.Query.NotFound{}]}} =
Ash.get(AttributeUser, user_id, tenant: "random")
end
end
end