Skip to content
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

Running the can_ functions on a domain triggers N queries if used in a list #1658

Open
StephanH90 opened this issue Dec 13, 2024 · 3 comments
Labels
bug Something isn't working

Comments

@StephanH90
Copy link
Contributor

Describe the bug
When you use the can_:action_name? functions that get generated on a domain in a list, it creates 1 query for every row.

To Reproduce
Let's assume we have a policy like this:

policy action :delete do
    authorize_if relates_to_actor_via(:users)
    # I have also tried other flavours such as:
    # authorize_if expr(exists(users, id == ^actor(:id))) # <- also produces a query for every row
end

A common use-case would be to only show the delete button if this authorization passes. However, if you use the function MyDomain.can_delete?(some_resource, user) you actually get 1 query for every resource.

Expected behavior
I would have assumed that Ash tries to use the preloaded relationship if possible.

@StephanH90 StephanH90 added bug Something isn't working needs review labels Dec 13, 2024
@zachdaniel
Copy link
Contributor

You get 1 query for every record? That shouldn't be the case.

@zachdaniel
Copy link
Contributor

I think? I'll have to double check that implementation. Could you provide a more concrete example of how you're calling it?

@StephanH90
Copy link
Contributor Author

StephanH90 commented Dec 15, 2024

@zachdaniel I prepared a little MR that illustrates the problem: https://github.com/ash-project/ash/pull/1661/files

Otherwise, here is the main bit of my change:

# Post resource
defmodule Post do
  # stuff

  actions do
    # stuff

    update :update_title do
      accept [:title]
    end
  end

  relationships do
    belongs_to :user, User, public?: true
  end

  policies do
    policy action :update_title do
      authorize_if relates_to_actor_via(:user)
    end
  end
end
test "a can check doesn't rerun queries" do
  start_supervised({Ash.Test.Authorizer, {}})

  user =
    User
    |> Ash.Changeset.for_create(:create)
    |> Ash.create!(authorize?: false)

  post =
    Post
    |> Ash.Changeset.for_create(:create, %{title: "test", user_id: user.id})
    |> Ash.create!(authorize?: false)
    |> Ash.load!(:user)

  # ! This causes an extra SQL query (!not when running against ETS)
  Domain.can_update_post_title(user, post)

  # In my actual usecase I have something like this in my liveview:
  # <div :for={item <- @items}>
  #   <button :if={MyDomain.can_delete(@current_user, item)}>Delete</button>
  #   {item.name}
  # </div>

  # This causes 1 query for every row.
end

If you check out my comment at the end of the test, I show an example from my actual use case. In my live view I want to show a delete button for every item. However, the user can only delete their own item.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants