Multiple Tenant Resources

In my app, I have two tenant resources: Organization and Establishment. I'm using the new scope feature and the value for current_tenant is a map containing both tenants. I need to provide an Ash.ToTenant implementation that extracts the correct tenant depending on the resource being queried. I've tried this:
# organization.ex
defimpl Ash.ToTenant do
def to_tenant(%{organization: %{id: id}}, _resource), do: id
def to_tenant(%{id: id}, _resource), do: id
def to_tenant(id, _resource) when is_binary(id), do: id
end
# organization.ex
defimpl Ash.ToTenant do
def to_tenant(%{organization: %{id: id}}, _resource), do: id
def to_tenant(%{id: id}, _resource), do: id
def to_tenant(id, _resource) when is_binary(id), do: id
end
# establishment.ex
defimpl Ash.ToTenant do
def to_tenant(%{establishment: %{id: id}}, _resource), do: id
def to_tenant(%{id: id}, _resource), do: id
def to_tenant(id, _resource) when is_binary(id), do: id
end
# establishment.ex
defimpl Ash.ToTenant do
def to_tenant(%{establishment: %{id: id}}, _resource), do: id
def to_tenant(%{id: id}, _resource), do: id
def to_tenant(id, _resource) when is_binary(id), do: id
end
But I'm getting the following error:
[error] ** (Protocol.UndefinedError) protocol Ash.ToTenant not implemented for type Map
[error] ** (Protocol.UndefinedError) protocol Ash.ToTenant not implemented for type Map
And when I provide implementations for the Map type:
# organization.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{organization: %{id: id}}, _resource), do: id
end

# establishment.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{establishment: %{id: id}}, _resource), do: id
end
# organization.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{organization: %{id: id}}, _resource), do: id
end

# establishment.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{establishment: %{id: id}}, _resource), do: id
end
It works but, I get the following warning:
warning: redefining module Ash.ToTenant.Map (current version loaded from _build/dev/lib/lamashka/ebin/Elixir.Ash.ToTenant.Map.beam)

94 │ defimpl Ash.ToTenant, for: Map do
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

└─ lib/lamashka/establishments/establishment.ex:94: Ash.ToTenant.Map (module)
warning: redefining module Ash.ToTenant.Map (current version loaded from _build/dev/lib/lamashka/ebin/Elixir.Ash.ToTenant.Map.beam)

94 │ defimpl Ash.ToTenant, for: Map do
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

└─ lib/lamashka/establishments/establishment.ex:94: Ash.ToTenant.Map (module)
Should I just ignore it, or is there a better approach to this?
4 Replies
ZachDaniel
ZachDaniel3mo ago
Are you potentially passing a map around as the tenant instead of the struct? You shouldn't need to do that. To define the protocol for map I mean
Joan Gavelán
Joan GavelánOP3mo ago
Yeah, so, given that I have two tenant resources, my current_tenant value in my Ash.Scope is a map containing both tenants. So my goal is to define an implementation that allows Ash to extract the corresponding tenant depending on the resource. I'm trying this, what do you think?
# to_tenant.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{organization: org, establishment: est} = tenant_map, resource) do
case Ash.Resource.Info.multitenancy_attribute(resource) do
:organization_id ->
org.id

:establishment_id ->
est.id

_ ->
tenant_map
end
end
end
# to_tenant.ex
defimpl Ash.ToTenant, for: Map do
def to_tenant(%{organization: org, establishment: est} = tenant_map, resource) do
case Ash.Resource.Info.multitenancy_attribute(resource) do
:organization_id ->
org.id

:establishment_id ->
est.id

_ ->
tenant_map
end
end
end
ZachDaniel
ZachDaniel3mo ago
Ah, interesting. Yeah that makes sense. I think 🙂 I hadn't considered the use case TBH so you'll want to play around with it
Joan Gavelán
Joan GavelánOP3mo ago
Yeah, I'll leave this open until I'm sure this approach works for child resources in both tenants. So far so good, the implementation error is gone. I'll share any updates 🙂

Did you find this page helpful?