Guidance on using Ash and Flop together?

Hi there,

As the title suggests, I’m trying to use Ash and Flop together for some lovely table-magic. Hopefully this is a reasonable question to ask in this topic.

My resource is set up with:

  • Attribute based multi-tenancy
  • Policies to enforce read-permissions

I’d like to use Flop / Flop Phoenix to give the user an interactive grid they can use to sift through the dataset, but I want to ensure that:
a) Data isn’t exposed across tenants
b) Users can only see records that they are allowed to see (based on the Ash Policy)
c) In the future I can make use of Ash calculated fields, etc.

What I’d like to be able to say is something like the following, but that’s obviously mixing Ash.Query and Ecto.Query types.

Customers.Customer
|> Ash.Query.for_read(:read, %{}, actor: socket.assigns.current_user)
|> Ash.Query.set_tenant(socket.assigns.current_tenant)
|> Flop.validate_and_run(params, for: Customers.Customer)

The best I’ve been able to do is the following, but I don’t like that I could easily miss the multi-tenancy filter, and my read-level policies aren’t being followed:

{:ok, query} = Customers.Customer
# specifying actor and authorize?=true doesn't do anything here
|> Ash.Query.for_read(:read)
# have to manually add tenant filter here.  set_tenant has no effect.
|> Ash.Query.filter(tenant_id == ^socket.assigns.current_tenant.id)
# Get an Ecto.Query which includes at least some of my filters
|> Ash.Query.data_layer_query()

customers = Flop.validate_and_run(query, params, for: Customers.Customer)

I assume I’m missing something obvious here and would appreciate a nudge in the right direction.

Thank you so much!

Unfortunately I don’t think that flop and Ash will ever play fully nicely together. They are at different levels of abstraction.

To answer your question, though, you will want to look at Ash.can and the accompanying options to take a query and apply policy filters for a given action.

With that said, not all policies or action invariants can be simulated this way. Some policies(if you currently have any of these then you probably already know) can only be enforced after running the query. Some actions have after action hooks that expect to be able to modify the return, not all calculated fields can be lowered to the query, etc.

You may be able to get what you want with flop in the short term but in the long term we will need our own equivalent tooling.

Flop and Ecto exist at a “data” level of abstraction whereas Ash models “domain actions”, only one component of which is the data that powers them.

It’s also worth pointing out that a lot of what Flop offers is already handled in Ash. Ash supports filtering, sorting and pagination out of the box, and AshPhoenix has some tooling for parsing filters/sorts/pagination from params.

1 Like

What’s missing that Flop provides?

Thanks @Nefcairon, @zachdaniel, @frankdugan3

I was stuck on “how do I make Flop” work, and it hadn’t occurred to me to look into how much of that I could replace with native Ash (most of it, as you say).

Thank you very much!