From b515bb258b9950d36fcd7eb76473e8d38ed5110f Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Thu, 22 Dec 2022 01:03:13 -0500 Subject: [PATCH] fix: fix broken default behavior around managing relationships. Read more: https://github.com/ash-project/ash/issues/469 --- config/config.exs | 2 + lib/ash/actions/managed_relationships.ex | 29 ++++++++++-- lib/ash/api/dsl.ex | 2 +- lib/ash/api/info.ex | 60 ++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/config/config.exs b/config/config.exs index 54a53725..91cf755f 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,5 +1,7 @@ import Config +config :ash, :use_all_identities_in_manage_relationship?, false + if Mix.env() == :dev do config :git_ops, mix_project: Ash.MixProject, diff --git a/lib/ash/actions/managed_relationships.ex b/lib/ash/actions/managed_relationships.ex index 3b2e7109..4fed1f4e 100644 --- a/lib/ash/actions/managed_relationships.ex +++ b/lib/ash/actions/managed_relationships.ex @@ -6,6 +6,25 @@ defmodule Ash.Actions.ManagedRelationships do 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 == %{}, do: {:ok, created} @@ -531,7 +550,7 @@ defmodule Ash.Actions.ManagedRelationships do end def pkeys(relationship, opts) do - identity_priority = opts[:identity_priority] || [] + identity_priority = opts[:identity_priority] || opts[:use_identities] use_identities = opts[:use_identities] identity_priority = @@ -549,8 +568,12 @@ defmodule Ash.Actions.ManagedRelationships do end) unprioritized_identities = - relationship.destination - |> Ash.Resource.Info.identities() + if @use_all_identities_in_manage_relationship do + relationship.destination + |> Ash.Resource.Info.identities() + else + [] + end |> then(fn identities -> if use_identities do Enum.filter(identities, &(&1.name in use_identities)) diff --git a/lib/ash/api/dsl.ex b/lib/ash/api/dsl.ex index ba1a8595..4d9c38eb 100644 --- a/lib/ash/api/dsl.ex +++ b/lib/ash/api/dsl.ex @@ -78,7 +78,7 @@ defmodule Ash.Api.Dsl do """ resources do registry MyApp.Registry - end + en """ ], links: [], diff --git a/lib/ash/api/info.ex b/lib/ash/api/info.ex index 85e95c6c..3747f67a 100644 --- a/lib/ash/api/info.ex +++ b/lib/ash/api/info.ex @@ -19,6 +19,66 @@ defmodule Ash.Api.Info do 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 """ Gets the resources of an Api module. Can be used at compile time.