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 orgschema 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.

  1. 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"
  2. 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 😁