Setting Tenant when using Context Multitenancy
.logicman:
So I have configure my resources to use context multitenancy (Postgres Schemas). Migrations are working fine and creating an
org
creates an
org
schema
as well. My question is how can I set the tenant, assuming am getting the tenant from the subdomain, which can the be used in
Ash
queries? I have seen
Ash.set_tenant/1
which takes a map as argument, what does the map look like? How is it different from
Ash.Query.set_tenant/2
? An also there what’s called
Process Context
is
Ash
, what is it and assuming it’s a data structure stored in the
Process
dictionary, how does it look like? Examples or pseudo code will help. Hopefully my questions are making sense 😁
zachdaniel:
You essentially have two options.
-
Provide the tenant when creating changesets (this is what I typically advise, but its a matter of preference). That looks like this:
Ash.Changeset.for_create(Resource, :create, .., tenant: "tenant_name") Ash.Query.for_read(Resource, :read, .., tenant: "tenant_name") Resource.code_interface_function(tenant: "tenant_name"
-
Set the tenant into process context using
Ash.set_tenant/1
Ash.set_tenant("tenant_name") Ash.Changeset.for_create(Resource, :create, ..) Ash.Query.for_read(Resource, :read, ..) Resource.code_interface_function()
zachdaniel:
The main thing about using the process dictionary is that you’ll have to make sure you call
Ash.set_tenant("tenant_name")
in any processes you start
.logicman:
Thank you <@197905764424089601>
zachdaniel:
My pleasure 🙂
.logicman:
Setting Tenant when using Context Multitenancy
zimt28:
If I remember correctly
set_tenant/1
expects a map, even though it should be a string
zimt28:
It still works but ElixirLS will complain
zachdaniel:
…weird
zimt28:
I think the typespec is wrong, but I cannot check right now
zachdaniel:
fixed 🙂
zimt28:
But it would still be nice to have a callback somewhere that allows passing an actual tenant and converting it to a string in the callback
zachdaniel:
Yeah, agreed that would be nice
zachdaniel:
That would be a good first issue actually
zachdaniel:
I’ll make a ticket for it soon
zachdaniel:
It would really just be a protocol you can implement, i.e
defimpl Ash.ToTenant do
def to_tenant(%Org{short_name: name}), do: name
end
.logicman:
What’s the equivalent of
Triplex.exists?("tenant1")
and
Triplex.to_prefix(tenant)
in Ash?
And also if my resource is configured likes this:
postgres do
...
manage_tenant do
template ["org_", :id]
end
end
it would nice if calling
Ash.set_tenant/1
automatically adds the set prefix, for example
Ash.set_tenant("tenant_1")
results in
{:tenant, org_tenant_1}
instead of
{:tenant, tenant_1}
zachdaniel:
We don’t actually have our own versions of those functions, you’ll have to handroll them
zachdaniel:
But to check if a tenant exists, you could either 1. lookup the org with that id, or 2. check if the schema exists using your ecto repo
zachdaniel:
We could implement the
to_tenant
protocol automatically if you use
manage_tenant
in ash_postgres, which would solve for a lot of this.
zachdaniel:
Just need to write the protocol.
.logicman:
That would be awesome
zachdaniel:
I probably won’t get to it soon though really. its a good first issue for someone else to tackle though, will write it up in a bit
.logicman:
In the meantime will just “handroll” my own, shouldn’t be hard I have done it before 😁