Best way to skip tenant check on a query

In my app I have users and accounts. The accounts are tenants. Users have and belong to many accounts through memberships. I've made the memberships resource multi-tenant, but now the users can't load their memberships. Memberships are really co-owned. Should I remove the multi-tenancy? Or is there a way for a query to ignore the tenant check?
13 Replies
ZachDaniel
ZachDaniel3y ago
There are a few ways you could do it: 1. You could add global? true to the multitenancy config of memberships. That says "I can be queried with a tenant or without it" This won't really make sense if using schema-based multitenancy, but for attribute multitenancy it does. 2. this one depends, are the users not multi-tenant? If they were multitenant, and you specified a tenant when fetching them, then you should be able to load the memberships I think there might be some more options, but lets explore those first
Robert Graff
Robert GraffOP3y ago
Users are not multi-tenant since they can belong to multiple tenants at the same time Let me explore the global options. The documentation was a little vague on exactly the implications of the global option.
Whether or not the data also exists outside of each tenant. Defaults to false .
ZachDaniel
ZachDaniel3y ago
Are you using schema or attribute multitenancy
Robert Graff
Robert GraffOP3y ago
I'm using attribute
ZachDaniel
ZachDaniel3y ago
I'm thinking we may need to come up with an option just for this specifically, you have a non-tenant resource and you want tenanted relationships You could write a manual relationship potentially which even allows you to describe the join
Robert Graff
Robert GraffOP3y ago
Right now, global is working for me but it doesn't feel right.
ZachDaniel
ZachDaniel3y ago
Yeah, I think you need a manual relationship for now. Its unfortunate, and in the future we can provide like a tenant_setter option that will take a record and figure out what tenant it should get its multitenant related things from
jeroen11dijk
jeroen11dijk4mo ago
Im currently running into the same issue. I can indeed set global? true but that means that you can query it without tenant everywhere if I read the docs correctly. I want the tenant to always be required but being able to override/bypass it this one time. Are there currently any methods of doing this?
ZachDaniel
ZachDaniel4mo ago
There is a way to do this IIRC its something like multitenancy :bypass this was added to solve for these problems
jeroen11dijk
jeroen11dijk4mo ago
That works for a read acion. I think I can work with that, thanks! My problem now is that I have a read
read :read do
primary? true
prepare build(load: [:partner_depot])

multitenancy :bypass
end
read :read do
primary? true
prepare build(load: [:partner_depot])

multitenancy :bypass
end
And then I get
** (Ash.Error.Invalid)
Bread Crumbs:
> Error returned from: Zelo.Company.Depot.read
> Error returned from: Zelo.Delegations.Partner.read

Invalid Error

* Queries against the Zelo.Company.Depot resource require a tenant to be specified
at partner_depot
** (Ash.Error.Invalid)
Bread Crumbs:
> Error returned from: Zelo.Company.Depot.read
> Error returned from: Zelo.Delegations.Partner.read

Invalid Error

* Queries against the Zelo.Company.Depot resource require a tenant to be specified
at partner_depot
Which I want to disable. I dont want to add the bypass to the depot read since its used elsewhere so can I overwrite this or set a custom read of the depots for this action
ZachDaniel
ZachDaniel4mo ago
Yeah, TBH I think this is "working as intended" The primary read action of the partner_depot resource is preventing you from running it without a tenant There are two ways you can approach this one: you can use a different read action for that relationship that allows you to bypass multitenancy you could add multitenancy :allow_global to the primary read action of :partern_depot, and then you could add a preparation that validates the conditions by which it allows this You can use the new shared context to do this if you want
read :read do
multitenancy :allow_global
prepare fn query, _ ->
if query.context[:bypass_depot_multitenancy] do
query
else
if query.tenant do
query
else
Ash.Query.add_error(query, "Must provide a tenant")
end
end
end
end
read :read do
multitenancy :allow_global
prepare fn query, _ ->
if query.context[:bypass_depot_multitenancy] do
query
else
if query.tenant do
query
else
Ash.Query.add_error(query, "Must provide a tenant")
end
end
end
end
Then
read :read do
primary? true
prepare build(load: [:partner_depot])
prepare set_context(%{shared: %{bypass_depot_multitenancy: true}})

multitenancy :bypass
end
read :read do
primary? true
prepare build(load: [:partner_depot])
prepare set_context(%{shared: %{bypass_depot_multitenancy: true}})

multitenancy :bypass
end
jeroen11dijk
jeroen11dijk4mo ago
I agree that it worked as intended but the workaround is really nice! I like the passing of the context, but now have to figure out how to update ash xD
ZachDaniel
ZachDaniel4mo ago
You can with this too
prepare fn query, _ ->
Ash.Query.load(query, partner_depot: Ash.Query.set_context(Depot, %{...}))
end
prepare fn query, _ ->
Ash.Query.load(query, partner_depot: Ash.Query.set_context(Depot, %{...}))
end
since its only one level deep

Did you find this page helpful?