mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +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
|
use Ash.Policy.FilterCheck
|
||||||
|
|
||||||
require Ash.Expr
|
require Ash.Expr
|
||||||
|
import Ash.Filter.TemplateHelpers
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def describe(opts) do
|
def describe(opts) do
|
||||||
|
@ -12,80 +13,60 @@ defmodule Ash.Policy.Check.RelatesToActorVia do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(opts) do
|
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 =
|
pkey =
|
||||||
opts[:resource]
|
last_relationship.destination
|
||||||
|> Ash.Resource.Info.related(opts[:relationship_path])
|
|
||||||
|> Kernel.||(raise "Must be able to determine related resource for `relates_to_actor_via`")
|
|
||||||
|> Ash.Resource.Info.primary_key()
|
|> Ash.Resource.Info.primary_key()
|
||||||
|
|
||||||
|
if to_many? do
|
||||||
Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
# defmodule Ash.Policy.Check.RelatesToActorVia do
|
@impl true
|
||||||
# @moduledoc false
|
def reject(opts) do
|
||||||
# use Ash.Policy.FilterCheck
|
opts = Keyword.update!(opts, :relationship_path, &List.wrap/1)
|
||||||
|
{last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
||||||
|
|
||||||
# require Ash.Expr
|
pkey =
|
||||||
|
last_relationship.destination
|
||||||
|
|> Ash.Resource.Info.primary_key()
|
||||||
|
|
||||||
# @impl true
|
if to_many? do
|
||||||
# def describe(opts) do
|
Ash.Expr.expr(not (^filter(opts)))
|
||||||
# path = Enum.join(opts[:relationship_path], ".")
|
else
|
||||||
# "record.#{path} == actor"
|
expr =
|
||||||
# end
|
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)
|
||||||
|
|
||||||
# @impl true
|
Ash.Expr.expr(not (^filter(opts)) or ^expr)
|
||||||
# def filter(opts) do
|
end
|
||||||
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
end
|
||||||
|
|
||||||
# pkey =
|
defp relationship_info(resource, path, to_many? \\ false)
|
||||||
# last_relationship.destination
|
|
||||||
# |> Ash.Resource.Info.primary_key()
|
|
||||||
|
|
||||||
# if to_many? do
|
defp relationship_info(resource, [rel], to_many?) do
|
||||||
# Ash.Expr.expr(exists(^opts[:relationship_path], ^Enum.map(pkey, &{&1, {:_actor, &1}})))
|
rel = Ash.Resource.Info.relationship(resource, rel)
|
||||||
# else
|
{rel, to_many? || rel.cardinality == :many}
|
||||||
# put_in_path(opts[:relationship_path], Enum.map(pkey, &{&1, {:_actor, &1}}))
|
end
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# @impl true
|
defp relationship_info(resource, [rel | rest], to_many?) do
|
||||||
# def reject(opts) do
|
rel = Ash.Resource.Info.relationship(resource, rel)
|
||||||
# {last_relationship, to_many?} = relationship_info(opts[:resource], opts[:relationship_path])
|
relationship_info(rel.destination, rest, to_many? || rel.cardinality == :many)
|
||||||
|
end
|
||||||
# pkey =
|
end
|
||||||
# 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
|
# you can't see comments on your post by people who aren't your friend
|
||||||
# for testing purposes :)
|
# for testing purposes :)
|
||||||
policy action_type(:read) do
|
policy action_type(:read) do
|
||||||
authorize_if expr(author == ^actor(:id))
|
# authorize_if expr(author == ^actor(:id))
|
||||||
authorize_if expr(post.author == ^actor(:id))
|
# authorize_if expr(post.author == ^actor(:id))
|
||||||
authorize_if expr(exists(author.friends, id == ^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
|
end
|
||||||
|
|
||||||
policy action_type(:create) do
|
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
|
# note to reader: in real life you'd want some kind of invite/approval system
|
||||||
policy action_type(:read) do
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ defmodule Ash.Test.Support.PolicyComplex.Post do
|
||||||
|
|
||||||
policies do
|
policies do
|
||||||
policy action_type(:read) do
|
policy action_type(:read) do
|
||||||
authorize_if expr(author == ^actor(:id))
|
authorize_if relates_to_actor_via(:author)
|
||||||
authorize_if expr(exists(author.friends, id == ^actor(:id)))
|
authorize_if relates_to_actor_via([:author, :friends])
|
||||||
end
|
end
|
||||||
|
|
||||||
policy action_type(:create) do
|
policy action_type(:create) do
|
||||||
|
|
Loading…
Reference in a new issue