diff --git a/lib/ash/seed.ex b/lib/ash/seed.ex index c304d743..f4f99b45 100644 --- a/lib/ash/seed.ex +++ b/lib/ash/seed.ex @@ -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 diff --git a/test/seed_test.exs b/test/seed_test.exs index f5ab9e07..676e9607 100644 --- a/test/seed_test.exs +++ b/test/seed_test.exs @@ -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