mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 05:23:03 +12:00
improvement: optimize for relates_to_actor_via
This commit is contained in:
parent
a4b3d36ca9
commit
95a4aa3708
4 changed files with 64 additions and 78 deletions
|
@ -3,6 +3,7 @@ defmodule Ash.Policy.Check.RelatesToActorVia do
|
|||
use Ash.Policy.FilterCheck
|
||||
|
||||
require Ash.Expr
|
||||
import Ash.Filter.TemplateHelpers
|
||||
|
||||
@impl true
|
||||
def describe(opts) do
|
||||
|
@ -12,80 +13,60 @@ defmodule Ash.Policy.Check.RelatesToActorVia do
|
|||
|
||||
@impl true
|
||||
def filter(opts) do
|
||||
opts = Keyword.update!(opts, :relationship_path, &List.wrap/1)
|
||||
{last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
||||
|
||||
pkey =
|
||||
opts[:resource]
|
||||
|> Ash.Resource.Info.related(opts[:relationship_path])
|
||||
|> Kernel.||(raise "Must be able to determine related resource for `relates_to_actor_via`")
|
||||
last_relationship.destination
|
||||
|> Ash.Resource.Info.primary_key()
|
||||
|
||||
if to_many? do
|
||||
Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
|
||||
else
|
||||
Enum.reduce(pkey, nil, fn field, expr ->
|
||||
if expr do
|
||||
Ash.Expr.expr(^expr and ^ref(opts[:relationship_path], field) == ^actor(field))
|
||||
else
|
||||
Ash.Expr.expr(^ref(opts[:relationship_path], field) == ^actor(field))
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def reject(opts) do
|
||||
opts = Keyword.update!(opts, :relationship_path, &List.wrap/1)
|
||||
{last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
||||
|
||||
pkey =
|
||||
last_relationship.destination
|
||||
|> Ash.Resource.Info.primary_key()
|
||||
|
||||
if to_many? do
|
||||
Ash.Expr.expr(not (^filter(opts)))
|
||||
else
|
||||
expr =
|
||||
Enum.reduce(pkey, nil, fn field, expr ->
|
||||
if expr do
|
||||
Ash.Expr.expr(^expr and is_nil(^ref(opts[:relationship_path], field)))
|
||||
else
|
||||
Ash.Expr.expr(is_nil(^ref(opts[:relationship_path], field)))
|
||||
end
|
||||
end)
|
||||
|
||||
Ash.Expr.expr(not (^filter(opts)) or ^expr)
|
||||
end
|
||||
end
|
||||
|
||||
defp relationship_info(resource, path, to_many? \\ false)
|
||||
|
||||
defp relationship_info(resource, [rel], to_many?) do
|
||||
rel = Ash.Resource.Info.relationship(resource, rel)
|
||||
{rel, to_many? || rel.cardinality == :many}
|
||||
end
|
||||
|
||||
defp relationship_info(resource, [rel | rest], to_many?) do
|
||||
rel = Ash.Resource.Info.relationship(resource, rel)
|
||||
relationship_info(rel.destination, rest, to_many? || rel.cardinality == :many)
|
||||
end
|
||||
end
|
||||
|
||||
# defmodule Ash.Policy.Check.RelatesToActorVia do
|
||||
# @moduledoc false
|
||||
# use Ash.Policy.FilterCheck
|
||||
|
||||
# require Ash.Expr
|
||||
|
||||
# @impl true
|
||||
# def describe(opts) do
|
||||
# path = Enum.join(opts[:relationship_path], ".")
|
||||
# "record.#{path} == actor"
|
||||
# end
|
||||
|
||||
# @impl true
|
||||
# def filter(opts) do
|
||||
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
||||
|
||||
# pkey =
|
||||
# last_relationship.destination
|
||||
# |> Ash.Resource.Info.primary_key()
|
||||
|
||||
# if to_many? do
|
||||
# Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
|
||||
# else
|
||||
# put_in_path(opts[:relationship_path], Enum.map(pkey, &{&1, {:_actor, &1}}))
|
||||
# end
|
||||
# end
|
||||
|
||||
# @impl true
|
||||
# def reject(opts) do
|
||||
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
||||
|
||||
# pkey =
|
||||
# last_relationship.destination
|
||||
# |> Ash.Resource.Info.primary_key()
|
||||
|
||||
# if to_many? do
|
||||
# Ash.Expr.expr(not exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
|
||||
# else
|
||||
# [
|
||||
# or: [
|
||||
# [not: filter(opts)],
|
||||
# [put_in_path(opts[:relationship_path], Enum.map(pkey, &{:is_nil, &1}))]
|
||||
# ]
|
||||
# ]
|
||||
# end
|
||||
# end
|
||||
|
||||
# defp relationship_info(resource, path, to_many? \\ false)
|
||||
|
||||
# defp relationship_info(resource, [rel], to_many?) do
|
||||
# rel = Ash.Resource.Info.relationship(resource, rel)
|
||||
# {rel, to_many? || rel.cardinality == :many}
|
||||
# end
|
||||
|
||||
# defp relationship_info(resource, [rel | rest], to_many?) do
|
||||
# rel = Ash.Resource.Info.relationship(resource, rel)
|
||||
# relationship_info(rel.destination, rest, to_many? || rel.cardinality == :many)
|
||||
# end
|
||||
|
||||
# defp put_in_path([], value) do
|
||||
# value
|
||||
# end
|
||||
|
||||
# defp put_in_path([key | rest], value) do
|
||||
# [{key, put_in_path(rest, value)}]
|
||||
# end
|
||||
# end
|
||||
|
|
|
@ -10,9 +10,13 @@ defmodule Ash.Test.Support.PolicyComplex.Comment do
|
|||
# you can't see comments on your post by people who aren't your friend
|
||||
# for testing purposes :)
|
||||
policy action_type(:read) do
|
||||
authorize_if expr(author == ^actor(:id))
|
||||
authorize_if expr(post.author == ^actor(:id))
|
||||
authorize_if expr(exists(author.friends, id == ^actor(:id)))
|
||||
# authorize_if expr(author == ^actor(:id))
|
||||
# authorize_if expr(post.author == ^actor(:id))
|
||||
# authorize_if expr(exists(author.friends, id == ^actor(:id)))
|
||||
|
||||
authorize_if relates_to_actor_via(:author)
|
||||
authorize_if relates_to_actor_via([:post, :author])
|
||||
authorize_if relates_to_actor_via([:author, :friends])
|
||||
end
|
||||
|
||||
policy action_type(:create) do
|
||||
|
|
|
@ -16,7 +16,8 @@ defmodule Ash.Test.Support.PolicyComplex.FriendLink do
|
|||
|
||||
# note to reader: in real life you'd want some kind of invite/approval system
|
||||
policy action_type(:read) do
|
||||
authorize_if expr(source_id == ^actor(:id) or destination_id == ^actor(:id))
|
||||
authorize_if relates_to_actor_via(:source)
|
||||
authorize_if relates_to_actor_via(:destination)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ defmodule Ash.Test.Support.PolicyComplex.Post do
|
|||
|
||||
policies do
|
||||
policy action_type(:read) do
|
||||
authorize_if expr(author == ^actor(:id))
|
||||
authorize_if expr(exists(author.friends, id == ^actor(:id)))
|
||||
authorize_if relates_to_actor_via(:author)
|
||||
authorize_if relates_to_actor_via([:author, :friends])
|
||||
end
|
||||
|
||||
policy action_type(:create) do
|
||||
|
|
Loading…
Reference in a new issue