-
-
Notifications
You must be signed in to change notification settings - Fork 341
feat(aggregates): add multitenancy bypass option for aggregates #2427
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(aggregates): add multitenancy bypass option for aggregates #2427
Conversation
Refactors and extends the multitenancy aggregate tests to cover all aggregate types (count, exists, first, sum, list, max, min, avg) with and without multitenancy bypass. Consolidates resource module definitions, adds more comprehensive test data, and verifies correct aggregate behavior across tenants and aggregate/action combinations, including error handling for missing tenant context.
Aggregates are now grouped by their multitenancy setting and executed in separate queries for bypass and tenant-specific aggregates. This ensures correct handling of multitenancy when running aggregate queries.
|
This does look generally good, but I think we're going to have to test this in |
Eliminated the `multitenancy: :bypass_all` key from the context map in Ash.Query.Aggregate, as it is now only set under the `shared` key. This simplifies the context structure.
…ich aggregations are bypassed
Sure 🙏🏻 please tell me what must do, i will try it I did some changes you told me please re-check it 🙏🏻 For exampleAnd Thanks! |
|
@shahryarjb there wouldn't be any failing tests yet, because |
|
Hi dear @zachdaniel i think my PR in the Ash Postgres needs so much attention, its required-database knowledge is more than me 🥲 PR: ash-project/ash_postgres#649 Thank you in advance |
|
@shahryarjb that PR makes me think that we should just make it so that we only allow this for attribute multitenancy, and that is a change we can make here. WDYT? |
|
Hi dear @zachdaniel i put a comment in the ash postgres, I agree with you as well. |
|
Excellent. So with that in mind, what we need to do then is validate when building an aggregate that all resources along the relationship path either have no multitenancy, or have attribute multitenancy. If there is context multitenancy anywhere then we can just raise an error. |
@zachdaniel this condtion should be in this pr or should be ash postgres? Hamm it is an easy condition or some think you have in your mind? |
|
@shahryarjb yeah it should be here in this PR 😄 can go somewhere around here: https://github.com/ash-project/ash/pull/2427/files#diff-b7b7a5528d3683c2b41b27b34431108ab641369372ec60ac1c908fa81beb04ffR2792 You should be able to look at each resource along the |
Adds validation to prevent use of `multitenancy: :bypass` aggregates on resources or relationship paths that use the `:context` multitenancy strategy, as bypass is only supported with the `:attribute` strategy. Includes comprehensive tests to ensure errors are raised in invalid scenarios and that valid configurations continue to work as expected.
|
Hi dear @zachdaniel , could you please re-check my last 2 updates, if is there any consideration i should care please let me know to update! |
lib/ash/actions/read/read.ex
Outdated
| shared: %{multitenancy: :bypass_all} | ||
| }) | ||
| else | ||
| Ash.Query.set_tenant(aggregate.query, aggregate.query.tenant || query.tenant) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should set the tenant in both cases, but just set the shared context only in the case we want to bypass multitenancy for.
Also, lets use shared: %{private: %{multitenancy: :bypass_all}} perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zachdaniel
Old behavior: When bypassing multitenancy, the tenant value was lost completely.
New behavior: The tenant is always preserved. Bypassing just tells the system "don't filter by tenant" but still
keeps the tenant value available for logging, auditing, or other uses.
Based on this: da6907d
Hope it is as you told me
lib/ash/actions/read/read.ex
Outdated
| if Ash.Resource.Info.multitenancy_strategy(resource) == :context do | ||
| location = relationship_name && " in relationship `#{relationship_name}`" | ||
|
|
||
| raise Ash.Error.Query.InvalidQuery, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to raise, lets just add this error to the query.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zachdaniel I refactored some lines of the code to support error tupple based on cf19f84
And i tested and all tests were okey
By the way I wish there were some coding-style guidelines, like how I should push code to the repo. There are many coding styles I prefer, but I think they might not be well-received. I'm not talking about performance, just coding style. but i tried to follow the repo style of coding
Old behavior: When bypassing multitenancy, the tenant value was lost completely. New behavior: The tenant is always preserved. Bypassing just tells the system "don't filter by tenant" but still keeps the tenant value available for logging, auditing, or other uses.
lib/ash/actions/aggregate.ex
Outdated
| Ash.Tracer.set_metadata(opts[:tracer], :action, metadata) | ||
|
|
||
| # Preserve the original tenant from opts before handle_multitenancy might clear it | ||
| original_tenant = opts[:tenant] || query.tenant |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we updated Ash.Actions.Read.handle_multitenancy to not clear the tenant, so we shouldn't need to do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I re-checked and as you said it works, but it did not work before and force me to keep original and use it inside the code
By the way thank you
Done ✅
lib/ash/actions/read/read.ex
Outdated
| # Validate context multitenancy and its relationships are not used with bypass | ||
| # Ref: https://github.com/ash-project/ash_postgres/pull/649#issuecomment-3536654583 | ||
| defp handle_aggregate_multitenancy(query) do | ||
| validate_context_strategy = fn resource, relationship_name, aggregate_name -> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should just be a private function, no need to create an anonymous function here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ✅
lib/ash/actions/read/read.ex
Outdated
| aggregate.query | ||
| |> Ash.Query.set_tenant(aggregate.query.tenant || query.tenant) | ||
| |> then( | ||
| &if(aggregate.multitenancy == :bypass, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big fan of then(&if, I think we should clean this up by defining a private function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ✅
lib/ash/actions/read/read.ex
Outdated
| ) | ||
| ) | ||
|
|
||
| with :ok <- validation, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets put the logic for this in a private fun, so its with :ok <- validate_multitenancy(....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ✅
lib/ash/query/aggregate.ex
Outdated
| build_opts -> build_query(target_resource, resource, build_opts) | ||
| end | ||
|
|
||
| # Set bypass context IMMEDIATELY if multitenancy bypass is requested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for the comment. I'd look at each comment to see if its truly necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done ✅
zachdaniel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comments. I think we're almost there 🥳
Extracted aggregate multitenancy validation into separate helper functions for improved readability and maintainability. The new functions `validate_aggregate_multitenancy/1` and `validate_context_multitenancy_strategy/3` replace the previous inline validation logic in `handle_aggregate_multitenancy/1`.
Extracted aggregate query preparation logic into a new private function `prepare_aggregate_query/3` to improve readability and maintainability. This change centralizes the logic for setting tenant and context based on the aggregate's multitenancy strategy.
|
Hi dear @zachdaniel, I did all changes you told me! and hope they will be okey 🧙♂️ |
Based on #2372, it covers the aggregates for know, I will try for calculations
I covered these test cases
This is the limit of bypass actions; it should still error)Contributor checklist
Leave anything that you believe does not apply unchecked.