fix: fix broken default behavior around managing relationships.

Read more: https://github.com/ash-project/ash/issues/469
This commit is contained in:
Zach Daniel 2022-12-22 01:03:13 -05:00
parent 05e4f2f2de
commit b515bb258b
4 changed files with 89 additions and 4 deletions

View file

@ -1,5 +1,7 @@
import Config import Config
config :ash, :use_all_identities_in_manage_relationship?, false
if Mix.env() == :dev do if Mix.env() == :dev do
config :git_ops, config :git_ops,
mix_project: Ash.MixProject, mix_project: Ash.MixProject,

View file

@ -6,6 +6,25 @@ defmodule Ash.Actions.ManagedRelationships do
require Ash.Query require Ash.Query
@use_all_identities_in_manage_relationship Application.compile_env(
:ash,
:use_all_identities_in_manage_relationship?
)
if is_nil(@use_all_identities_in_manage_relationship) do
IO.warn("""
* IMPORTANT *
The configuration `use_all_identities_in_manage_relationship` was not set. It is defaulting to `true`.
This configuration must now be manually set, and it should be set to `false`.
If you are currently using `manage_relationship`, please read https://github.com/ash-project/ash/issues/469
""")
@use_all_identities_in_manage_relationship true
end
def load(_api, created, %{relationships: rels}, _) when rels == %{}, def load(_api, created, %{relationships: rels}, _) when rels == %{},
do: {:ok, created} do: {:ok, created}
@ -531,7 +550,7 @@ defmodule Ash.Actions.ManagedRelationships do
end end
def pkeys(relationship, opts) do def pkeys(relationship, opts) do
identity_priority = opts[:identity_priority] || [] identity_priority = opts[:identity_priority] || opts[:use_identities]
use_identities = opts[:use_identities] use_identities = opts[:use_identities]
identity_priority = identity_priority =
@ -549,8 +568,12 @@ defmodule Ash.Actions.ManagedRelationships do
end) end)
unprioritized_identities = unprioritized_identities =
relationship.destination if @use_all_identities_in_manage_relationship do
|> Ash.Resource.Info.identities() relationship.destination
|> Ash.Resource.Info.identities()
else
[]
end
|> then(fn identities -> |> then(fn identities ->
if use_identities do if use_identities do
Enum.filter(identities, &(&1.name in use_identities)) Enum.filter(identities, &(&1.name in use_identities))

View file

@ -78,7 +78,7 @@ defmodule Ash.Api.Dsl do
""" """
resources do resources do
registry MyApp.Registry registry MyApp.Registry
end en
""" """
], ],
links: [], links: [],

View file

@ -19,6 +19,66 @@ defmodule Ash.Api.Info do
end end
end end
def find_manage_relationships_with_identity_not_configured(otp_app) do
otp_app
|> Application.get_env(:ash_apis, [])
|> Enum.flat_map(&Ash.Api.Info.resources/1)
|> Enum.flat_map(fn resource ->
resource
|> Ash.Resource.Info.actions()
|> Enum.flat_map(fn action ->
action
|> Map.get(:changes, [])
|> Enum.flat_map(fn
%{change: {Ash.Resource.Change.ManageRelationship, opts}} ->
related = Ash.Resource.Info.related(resource, opts[:relationship])
identities = Ash.Resource.Info.identities(related)
argument = Enum.find(action.arguments, &(&1.name == opts[:argument]))
if argument && map_type?(argument.type) && !Enum.empty?(identities) do
[{resource, action.name, opts[:argument]}]
else
[]
end
_ ->
[]
end)
end)
end)
|> Enum.group_by(
fn {resource, action, _} ->
{resource, action}
end,
&elem(&1, 2)
)
|> Enum.map_join("\n\n", fn {{resource, action}, args} ->
"#{inspect(resource)}.#{action}:\n" <>
Enum.map_join(args, "\n", fn arg ->
"* #{arg}"
end)
end)
end
defp map_type?({:array, type}) do
map_type?(type)
end
defp map_type?(:map), do: true
defp map_type?(Ash.Type.Map), do: true
defp map_type?(type) do
if Ash.Type.embedded_type?(type) do
if is_atom(type) && :erlang.function_exported(type, :admin_map_type?, 0) do
type.admin_map_type?()
else
false
end
else
false
end
end
@doc """ @doc """
Gets the resources of an Api module. Can be used at compile time. Gets the resources of an Api module. Can be used at compile time.