mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 21:13:10 +12:00
WIP
This commit is contained in:
parent
de9a790626
commit
a6ca37f537
6 changed files with 306 additions and 222 deletions
|
@ -7,7 +7,7 @@ defmodule Ash.Actions.Create do
|
|||
def run(api, resource, action, params) do
|
||||
if Keyword.get(params, :side_load, []) in [[], nil] do
|
||||
Ash.DataLayer.transact(resource, fn ->
|
||||
with %{valid?: true} = changeset <- changeset(resource, params),
|
||||
with %{valid?: true} = changeset <- changeset(api, resource, params),
|
||||
{:ok, %{data: created}} <-
|
||||
do_authorized(changeset, params, action, resource, api) do
|
||||
{:ok, created}
|
||||
|
@ -26,91 +26,34 @@ defmodule Ash.Actions.Create do
|
|||
|
||||
# TODO: Rewrite attributes that reference foreign keys to the primary relationship
|
||||
# for that foriegn key. Also require a "primary relationship" for a foreign key
|
||||
def changeset(resource, params) do
|
||||
# if the foreign key doesn't reference the
|
||||
def changeset(api, resource, params) do
|
||||
attributes = Keyword.get(params, :attributes, %{})
|
||||
relationships = Keyword.get(params, :relationships, %{})
|
||||
|
||||
case prepare_create_attributes(resource, attributes) do
|
||||
%{valid?: true} = changeset ->
|
||||
relationships
|
||||
|> Enum.reduce(changeset, fn {key, value}, changeset ->
|
||||
case Ash.relationship(resource, key) do
|
||||
%{cardinality: :many, destination: destination, name: name} ->
|
||||
case Ash.Actions.PrimaryKeyHelpers.values_to_primary_key_filters(
|
||||
destination,
|
||||
value
|
||||
) do
|
||||
{:ok, values} ->
|
||||
Map.update!(changeset, :__ash_relationships__, fn ash_relationships ->
|
||||
Map.put(ash_relationships, key, %{add: values})
|
||||
end)
|
||||
|
||||
{:error, _error} ->
|
||||
Ecto.Changeset.add_error(changeset, name, "Invalid Identifiers")
|
||||
end
|
||||
|
||||
%{type: :has_one, destination: destination, name: name} ->
|
||||
case Ash.Actions.PrimaryKeyHelpers.value_to_primary_key_filter(
|
||||
destination,
|
||||
value
|
||||
) do
|
||||
{:ok, value} ->
|
||||
Map.update!(changeset, :__ash_relationships__, fn ash_relationships ->
|
||||
Map.put(ash_relationships, key, %{add: value})
|
||||
end)
|
||||
|
||||
{:error, _error} ->
|
||||
Ecto.Changeset.add_error(changeset, name, "Invalid Identifier")
|
||||
end
|
||||
|
||||
%{
|
||||
type: :belongs_to,
|
||||
destination: destination,
|
||||
name: name,
|
||||
source_field: source_field,
|
||||
destination_field: destination_field
|
||||
} ->
|
||||
case Ash.Actions.PrimaryKeyHelpers.value_to_primary_key_filter(destination, value) do
|
||||
{:ok, value} ->
|
||||
changeset
|
||||
|> Map.update!(:__ash_relationships__, fn ash_relationships ->
|
||||
Map.put(ash_relationships, key, %{add: value})
|
||||
end)
|
||||
# Does this assumption hold?
|
||||
|> Ecto.Changeset.put_change(
|
||||
source_field,
|
||||
Keyword.fetch!(value, destination_field)
|
||||
)
|
||||
|> Map.put_new(:__ash_skip_authorization_fields__, [])
|
||||
|> Map.update!(:__ash_skip_authorization_fields__, fn fields ->
|
||||
[source_field | fields]
|
||||
end)
|
||||
|
||||
{:error, _error} ->
|
||||
Ecto.Changeset.add_error(changeset, name, "Invalid Identifier(s)")
|
||||
end
|
||||
|
||||
_ ->
|
||||
Ecto.Changeset.add_error(changeset, key, "No such relationship")
|
||||
end
|
||||
end)
|
||||
|
||||
changeset ->
|
||||
changeset
|
||||
end
|
||||
resource
|
||||
|> prepare_create_attributes(attributes)
|
||||
|> Ash.Actions.Relationships.handle_create_relationships(api, relationships)
|
||||
end
|
||||
|
||||
defp do_authorized(changeset, params, action, resource, api) do
|
||||
relationships = Keyword.get(params, :relationships, %{})
|
||||
|
||||
create_authorization_request =
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: action.authorization_steps,
|
||||
resource: resource,
|
||||
changeset: changeset,
|
||||
changeset:
|
||||
Ash.Actions.Relationships.authorization_changeset_with_foreign_keys(
|
||||
changeset,
|
||||
relationships
|
||||
),
|
||||
action_type: action.type,
|
||||
fetcher: fn ->
|
||||
do_create(resource, changeset)
|
||||
end,
|
||||
dependencies: Map.get(changeset, :__changes_depend_on__) || [],
|
||||
state_key: :data,
|
||||
must_fetch?: true,
|
||||
relationship: [],
|
||||
|
@ -144,14 +87,13 @@ defmodule Ash.Actions.Create do
|
|||
)
|
||||
end)
|
||||
|
||||
relationship_auths =
|
||||
Ash.Actions.Relationships.relationship_change_authorizations(api, resource, changeset)
|
||||
relationship_auths = Map.get(changeset, :__authorizations__, [])
|
||||
|
||||
if params[:authorization] do
|
||||
strict_access? =
|
||||
case Keyword.fetch(params[:authorization], :strict_access?) do
|
||||
{:ok, value} -> value
|
||||
:error -> true
|
||||
:error -> false
|
||||
end
|
||||
|
||||
Authorizer.authorize(
|
||||
|
|
|
@ -1,19 +1,98 @@
|
|||
defmodule Ash.Actions.Relationships do
|
||||
def relationship_change_authorizations(api, resource, changeset) do
|
||||
resource
|
||||
|> Ash.relationships()
|
||||
|> Enum.filter(fn relationship ->
|
||||
Map.has_key?(changeset.__ash_relationships__, relationship.name)
|
||||
end)
|
||||
|> Enum.flat_map(fn relationship ->
|
||||
add_related_authorizations(api, relationship, changeset)
|
||||
def handle_create_relationships(changeset, api, relationships_input) do
|
||||
Enum.reduce(relationships_input, changeset, fn {name, data}, changeset ->
|
||||
case Ash.relationship(changeset.data.__struct__, name) do
|
||||
nil ->
|
||||
Ecto.Changeset.add_error(changeset, name, "Invalid relationship")
|
||||
|
||||
relationship ->
|
||||
cond do
|
||||
!Keyword.keyword?(data) ->
|
||||
add_create_authorizations(api, relationship, data, changeset)
|
||||
|
||||
Keyword.keys(data) == [:add] ->
|
||||
add_create_authorizations(api, relationship, data[:add], changeset)
|
||||
|
||||
Keyword.keys(data) == [:replace] ->
|
||||
add_create_authorizations(api, relationship, data[:replace], changeset)
|
||||
|
||||
Keyword.keys(data) == [:add, :replace] ->
|
||||
Ecto.Changeset.add_error(
|
||||
changeset,
|
||||
relationship.name,
|
||||
"Cannot add to a relationship and replace it at the same time."
|
||||
)
|
||||
|
||||
Keyword.has_key?(data, :remove) ->
|
||||
Ecto.Changeset.add_error(
|
||||
changeset,
|
||||
relationship.name,
|
||||
"Cannot remove from a relationship on create."
|
||||
)
|
||||
|
||||
true ->
|
||||
Ecto.Changeset.add_error(
|
||||
changeset,
|
||||
relationship.name,
|
||||
"Invalid relationship data provided"
|
||||
)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp add_related_authorizations(
|
||||
def authorization_changeset_with_foreign_keys(changeset, relationships) do
|
||||
relationships
|
||||
|> Enum.reduce_while({:ok, []}, fn {name, data}, {:ok, relationships} ->
|
||||
case Ash.relationship(changeset.data.__struct__, name) do
|
||||
nil ->
|
||||
{:halt, {:error, name}}
|
||||
|
||||
%{type: :belongs_to, destination_field: destination_field} = relationship ->
|
||||
case identifier_or_identifiers(relationship, data) do
|
||||
{:ok, identifier} ->
|
||||
if Keyword.has_key?(identifier, destination_field) do
|
||||
{:cont, {:ok, relationships}}
|
||||
else
|
||||
{:cont, {:ok, [relationship | relationships]}}
|
||||
end
|
||||
|
||||
{:error, _} ->
|
||||
{:halt, {:error, name}}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:cont, {:ok, relationships}}
|
||||
end
|
||||
end)
|
||||
|> case do
|
||||
{:error, name} ->
|
||||
Ecto.Changeset.add_error(changeset, name, "Invalid relationship")
|
||||
|
||||
{:ok, []} ->
|
||||
changeset
|
||||
|
||||
{:ok, relationships_needing_fetch} ->
|
||||
fn data ->
|
||||
Enum.reduce(relationships_needing_fetch, changeset, fn relationship, changeset ->
|
||||
related = get_in(data, [:relationships, relationship.name, :to_add])
|
||||
|
||||
if related do
|
||||
value = Map.get(related, relationship.destination_field)
|
||||
Ecto.Changeset.cast(changeset, relationship.source_field, value)
|
||||
else
|
||||
Ecto.Changeset.add_error(changeset, relationship.name, "Invalid relationship data")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp read_related_authorization(
|
||||
api,
|
||||
%{destination: destination} = relationship,
|
||||
changeset
|
||||
value,
|
||||
key
|
||||
) do
|
||||
default_read =
|
||||
Ash.primary_action(destination, :read) ||
|
||||
|
@ -21,11 +100,6 @@ defmodule Ash.Actions.Relationships do
|
|||
|
||||
relationship_name = relationship.name
|
||||
|
||||
value =
|
||||
changeset.__ash_relationships__
|
||||
|> Map.get(relationship_name)
|
||||
|> Map.get(:add, [])
|
||||
|
||||
filter =
|
||||
case relationship.cardinality do
|
||||
:many ->
|
||||
|
@ -35,31 +109,192 @@ defmodule Ash.Actions.Relationships do
|
|||
value
|
||||
end
|
||||
|
||||
read_request =
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: default_read.authorization_steps,
|
||||
resource: relationship.destination,
|
||||
action_type: :read,
|
||||
filter: filter,
|
||||
must_fetch?: true,
|
||||
state_key: [:relationships, relationship_name, :to_add],
|
||||
fetcher: fn ->
|
||||
case api.read(destination, filter: filter, paginate: false) do
|
||||
{:ok, %{results: results}} -> {:ok, results}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end,
|
||||
relationship: [relationship.name],
|
||||
source: "read prior to write related #{relationship.name}"
|
||||
)
|
||||
|
||||
related_requests = related_add_authorization_requests(api, value, relationship, changeset)
|
||||
|
||||
[read_request | related_requests]
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: default_read.authorization_steps,
|
||||
resource: relationship.destination,
|
||||
action_type: :read,
|
||||
filter: filter,
|
||||
must_fetch?: true,
|
||||
state_key: [:relationships, relationship_name, key],
|
||||
fetcher: fn ->
|
||||
case api.read(destination, filter: filter, paginate: false) do
|
||||
{:ok, %{results: results}} -> {:ok, results}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end,
|
||||
relationship: [relationship.name],
|
||||
source: "read prior to write related #{relationship.name}"
|
||||
)
|
||||
end
|
||||
|
||||
defp related_add_authorization_requests(
|
||||
defp identifier_or_identifiers(relationship, data) do
|
||||
case relationship.cardinality do
|
||||
:many ->
|
||||
Ash.Actions.PrimaryKeyHelpers.values_to_primary_key_filters(
|
||||
relationship.destination,
|
||||
data
|
||||
)
|
||||
|
||||
:one ->
|
||||
Ash.Actions.PrimaryKeyHelpers.value_to_primary_key_filter(
|
||||
relationship.destination,
|
||||
data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp add_create_authorizations(api, relationship, data, changeset) do
|
||||
case identifier_or_identifiers(relationship, data) do
|
||||
{:ok, identifiers} ->
|
||||
changeset =
|
||||
add_to_relationship_change_metadata(changeset, relationship, :add, identifiers)
|
||||
|
||||
read_authorization = read_related_authorization(api, relationship, identifiers, :to_add)
|
||||
|
||||
add_authorization =
|
||||
add_to_relationship_authorization(api, identifiers, relationship, changeset)
|
||||
|
||||
changeset
|
||||
|> add_authorizations(read_authorization)
|
||||
|> add_authorizations(add_authorization)
|
||||
|> set_belongs_to_change(relationship, identifiers)
|
||||
|
||||
{:error, error} ->
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp add_to_relationship_change_metadata(changeset, relationship, key, identifiers) do
|
||||
changeset
|
||||
|> Map.put_new(:__ash_relationships__, %{})
|
||||
|> Map.update!(:__ash_relationships__, fn ash_relationships ->
|
||||
ash_relationships
|
||||
|> Map.put_new(relationship.name, %{})
|
||||
|> Map.update!(relationship.name, fn changes -> Map.put(changes, key, identifiers) end)
|
||||
end)
|
||||
end
|
||||
|
||||
defp set_belongs_to_change(
|
||||
changeset,
|
||||
%{
|
||||
type: :belongs_to,
|
||||
source_field: source_field,
|
||||
name: name,
|
||||
destination_field: destination_field
|
||||
},
|
||||
value
|
||||
) do
|
||||
if Keyword.has_key?(value, destination_field) do
|
||||
changeset
|
||||
|> Ecto.Changeset.put_change(
|
||||
source_field,
|
||||
Keyword.fetch!(value, destination_field)
|
||||
)
|
||||
|> Map.put_new(:__ash_skip_authorization_fields__, [])
|
||||
|> Map.update!(:__ash_skip_authorization_fields__, fn fields ->
|
||||
[source_field | fields]
|
||||
end)
|
||||
else
|
||||
Map.put_new(changeset, :__changes_depend_on__, [:relationships, name, :to_add])
|
||||
end
|
||||
end
|
||||
|
||||
defp set_belongs_to_change(changeset, _, _) do
|
||||
changeset
|
||||
end
|
||||
|
||||
defp add_authorizations(changeset, authorizations) do
|
||||
authorizations = List.wrap(authorizations)
|
||||
Map.update(changeset, :__authorizations__, authorizations, &Kernel.++(&1, authorizations))
|
||||
end
|
||||
|
||||
defp add_to_relationship_authorization(
|
||||
api,
|
||||
identifier,
|
||||
%{type: :has_one, name: name, destination: destination} = relationship,
|
||||
changeset
|
||||
) do
|
||||
pkey = Ash.primary_key(destination)
|
||||
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: relationship.authorization_steps,
|
||||
resource: relationship.source,
|
||||
changeset: changeset,
|
||||
action_type: :create,
|
||||
state_key: :data,
|
||||
must_fetch?: true,
|
||||
dependencies: [:data, [:relationships, name, :to_add]],
|
||||
is_fetched: fn data ->
|
||||
case data do
|
||||
%{^name => %Ecto.Association.NotLoaded{}} -> false
|
||||
_ -> true
|
||||
end
|
||||
end,
|
||||
fetcher: fn %{data: data, relationships: %{^name => %{:to_add => to_add}}} ->
|
||||
pkey_value = Keyword.take(identifier, pkey)
|
||||
|
||||
related =
|
||||
Enum.find(to_add, fn to_relate ->
|
||||
to_relate
|
||||
|> Map.take(pkey)
|
||||
|> Map.to_list()
|
||||
|> Kernel.==(pkey_value)
|
||||
end)
|
||||
|
||||
updated =
|
||||
api.update(related,
|
||||
attributes: %{
|
||||
relationship.destination_field => Map.get(data, relationship.source_field)
|
||||
}
|
||||
)
|
||||
|
||||
case updated do
|
||||
{:ok, updated} -> {:ok, Map.put(data, relationship.name, updated)}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end,
|
||||
relationship: [],
|
||||
bypass_strict_access?: true,
|
||||
source: "Update relationship #{name}"
|
||||
)
|
||||
end
|
||||
|
||||
defp add_to_relationship_authorization(
|
||||
api,
|
||||
_identifier,
|
||||
%{type: :belongs_to, name: name} = relationship,
|
||||
changeset
|
||||
) do
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: relationship.authorization_steps,
|
||||
resource: relationship.source,
|
||||
action_type: :update,
|
||||
state_key: :data,
|
||||
is_fetched: fn data ->
|
||||
case data do
|
||||
%{^name => %Ecto.Association.NotLoaded{}} -> false
|
||||
_ -> true
|
||||
end
|
||||
end,
|
||||
must_fetch?: true,
|
||||
dependencies: [:data, [:relationships, name, :to_add]],
|
||||
bypass_strict_access?: true,
|
||||
changeset: changeset,
|
||||
fetcher: fn %{data: data, relationships: %{^name => %{:to_add => to_add}}} ->
|
||||
case to_add do
|
||||
[item] -> {:ok, Map.put(data, name, item)}
|
||||
_ -> raise "Internal relationship assumption failed."
|
||||
end
|
||||
end,
|
||||
relationship: [],
|
||||
source: "Set relationship #{relationship.name}"
|
||||
)
|
||||
end
|
||||
|
||||
defp add_to_relationship_authorization(
|
||||
api,
|
||||
identifiers,
|
||||
%{destination: destination, name: name, type: :many_to_many} = relationship,
|
||||
|
@ -95,7 +330,7 @@ defmodule Ash.Actions.Relationships do
|
|||
Map.fetch!(data, relationship.source_field_on_join_table)
|
||||
}
|
||||
|
||||
changeset = Ash.Actions.Create.changeset(relationship.through, attributes)
|
||||
changeset = Ash.Actions.Create.changeset(api, relationship.through, attributes)
|
||||
|
||||
if changeset.valid? do
|
||||
{:ok, changeset}
|
||||
|
@ -184,7 +419,7 @@ defmodule Ash.Actions.Relationships do
|
|||
end)
|
||||
end
|
||||
|
||||
defp related_add_authorization_requests(
|
||||
defp add_to_relationship_authorization(
|
||||
api,
|
||||
identifiers,
|
||||
%{destination: destination, name: name, type: :has_many} = relationship,
|
||||
|
@ -232,10 +467,7 @@ defmodule Ash.Actions.Relationships do
|
|||
api.update(related,
|
||||
attributes: %{
|
||||
relationship.destination_field => Map.get(data, relationship.source_field)
|
||||
},
|
||||
# TODO: This does nothing, but is intended for use when we disallow writing to fields that point to
|
||||
# relationships
|
||||
system?: true
|
||||
}
|
||||
)
|
||||
|
||||
case updated do
|
||||
|
@ -262,96 +494,4 @@ defmodule Ash.Actions.Relationships do
|
|||
]
|
||||
end)
|
||||
end
|
||||
|
||||
defp related_add_authorization_requests(
|
||||
api,
|
||||
_identifier,
|
||||
%{type: :belongs_to, name: name} = relationship,
|
||||
changeset
|
||||
) do
|
||||
[
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: relationship.authorization_steps,
|
||||
resource: relationship.source,
|
||||
action_type: :update,
|
||||
state_key: :data,
|
||||
is_fetched: fn data ->
|
||||
case data do
|
||||
%{^name => %Ecto.Association.NotLoaded{}} -> false
|
||||
_ -> true
|
||||
end
|
||||
end,
|
||||
must_fetch?: true,
|
||||
dependencies: [:data, [:relationships, name, :to_add]],
|
||||
bypass_strict_access?: true,
|
||||
changeset: changeset,
|
||||
fetcher: fn %{data: data, relationships: %{^name => %{:to_add => to_add}}} ->
|
||||
case to_add do
|
||||
[item] -> {:ok, Map.put(data, name, item)}
|
||||
_ -> raise "Internal relationship assumption failed."
|
||||
end
|
||||
end,
|
||||
relationship: [],
|
||||
source: "Set relationship #{relationship.name}"
|
||||
)
|
||||
]
|
||||
end
|
||||
|
||||
defp related_add_authorization_requests(
|
||||
api,
|
||||
identifier,
|
||||
%{type: :has_one, name: name, destination: destination} = relationship,
|
||||
changeset
|
||||
) do
|
||||
pkey = Ash.primary_key(destination)
|
||||
|
||||
[
|
||||
Ash.Authorization.Request.new(
|
||||
api: api,
|
||||
authorization_steps: relationship.authorization_steps,
|
||||
resource: relationship.source,
|
||||
changeset: changeset,
|
||||
action_type: :create,
|
||||
state_key: :data,
|
||||
must_fetch?: true,
|
||||
dependencies: [:data, [:relationships, name, :to_add]],
|
||||
is_fetched: fn data ->
|
||||
case data do
|
||||
%{^name => %Ecto.Association.NotLoaded{}} -> false
|
||||
_ -> true
|
||||
end
|
||||
end,
|
||||
fetcher: fn %{data: data, relationships: %{^name => %{:to_add => to_add}}} ->
|
||||
pkey_value = Keyword.take(identifier, pkey)
|
||||
|
||||
related =
|
||||
Enum.find(to_add, fn to_relate ->
|
||||
to_relate
|
||||
|> Map.take(pkey)
|
||||
|> Map.to_list()
|
||||
|> Kernel.==(pkey_value)
|
||||
end)
|
||||
|
||||
updated =
|
||||
api.update(related,
|
||||
attributes: %{
|
||||
relationship.destination_field => Map.get(data, relationship.source_field)
|
||||
},
|
||||
# TODO: This does nothing, but is intended for use when we disallow writing to fields that point to
|
||||
# relationships
|
||||
system?: true
|
||||
)
|
||||
|
||||
case updated do
|
||||
{:ok, updated} -> {:ok, Map.put(data, relationship.name, updated)}
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end,
|
||||
relationship: [],
|
||||
bypass_strict_access?: true,
|
||||
source: "Update relationship #{name}"
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,6 @@ defmodule Ash.Authorization.Check do
|
|||
@type options :: Keyword.t()
|
||||
|
||||
@callback strict_check(Ash.user(), Ash.Authorization.request(), options) :: boolean | :unknown
|
||||
|
||||
@callback prepare(options) ::
|
||||
list(Ash.Authorization.prepare_instruction()) | {:error, Ash.error()}
|
||||
@callback check(Ash.user(), list(Ash.record()), map, options) ::
|
||||
|
|
|
@ -54,7 +54,13 @@ defmodule Ash.Authorization.Checker do
|
|||
{[], _clauses_requiring_fetch} ->
|
||||
case fetch_requests(requests, state, strict_access?) do
|
||||
{:ok, {new_requests, new_state}} ->
|
||||
run_checks(scenarios, user, new_requests, facts, new_state, strict_access?)
|
||||
{new_requests, new_facts} =
|
||||
Enum.reduce(new_requests, {[], facts}, fn request, {requests, facts} ->
|
||||
{request, new_facts} = strict_check(user, request, facts, strict_access?)
|
||||
{[request | requests], new_facts}
|
||||
end)
|
||||
|
||||
run_checks(scenarios, user, new_requests, new_facts, new_state, strict_access?)
|
||||
|
||||
:all_scenarios_known ->
|
||||
:all_scenarios_known
|
||||
|
@ -106,10 +112,11 @@ defmodule Ash.Authorization.Checker do
|
|||
{Enum.count(request.relationship), not request.bypass_strict_access?, request.relationship}
|
||||
end)
|
||||
|> case do
|
||||
[request | _] = requests ->
|
||||
[request | rest] = requests ->
|
||||
case Request.fetch(state, request) do
|
||||
{:ok, new_state} ->
|
||||
{:ok, {requests ++ other_requests, new_state}}
|
||||
new_requests = [%{request | is_fetched: true} | rest] ++ other_requests
|
||||
{:ok, {new_requests, new_state}}
|
||||
|
||||
:error ->
|
||||
{:ok, {requests ++ other_requests, state}}
|
||||
|
|
|
@ -75,6 +75,10 @@ defmodule Ash.Authorization.Request do
|
|||
end)
|
||||
end
|
||||
|
||||
def fetched?(_, %{is_fetched: boolean}) when is_boolean(boolean) do
|
||||
boolean
|
||||
end
|
||||
|
||||
def fetched?(state, request) do
|
||||
case fetch_request_state(state, request) do
|
||||
{:ok, value} ->
|
||||
|
|
|
@ -59,16 +59,8 @@ defmodule Ash.Test.Authorization.CreateAuthorizationTest do
|
|||
authorization_steps: false
|
||||
|
||||
attribute :self_manager, :boolean, authorization_steps: false
|
||||
# [
|
||||
# authorize_if: user_attribute(:admin, true)
|
||||
# ]
|
||||
|
||||
attribute :fired, :boolean, authorization_steps: false
|
||||
# [
|
||||
# authorize_if: user_attribute(:admin, true),
|
||||
# forbid_if: attribute_equals(:self_manager, true),
|
||||
# authorize_if: always()
|
||||
# ]
|
||||
end
|
||||
|
||||
relationships do
|
||||
|
|
Loading…
Reference in a new issue