Current section
Files
Jump to
Current section
Files
lib/diffo/provider/components/instance/extension/party.ex
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
#
# SPDX-License-Identifier: MIT
defmodule Diffo.Provider.Instance.Party do
@moduledoc false
alias Diffo.Provider
@doc """
Struct for a Party
"""
defstruct [:id, :role]
@doc false
def validate_parties(changeset, declarations) do
if declarations == [] do
changeset
else
parties = Ash.Changeset.get_argument(changeset, :parties) || []
changeset
|> validate_roles(parties, declarations)
|> validate_constraints(parties, declarations)
end
end
defp validate_roles(changeset, parties, declarations) do
declared_roles = MapSet.new(declarations, & &1.role)
Enum.reduce(parties, changeset, fn %{role: role}, cs ->
if MapSet.member?(declared_roles, role) do
cs
else
Ash.Changeset.add_error(cs,
field: :parties,
message: "role #{inspect(role)} is not declared on this resource"
)
end
end)
end
defp validate_constraints(changeset, parties, declarations) do
counts = Enum.frequencies_by(parties, & &1.role)
declarations
|> Enum.filter(&is_struct(&1, Diffo.Provider.Extension.PartyDeclaration))
|> Enum.reject(&(&1.reference || &1.calculate))
|> Enum.reduce(changeset, fn decl, cs ->
count = Map.get(counts, decl.role, 0)
constraints = decl.constraints || []
cs
|> check_min(decl.role, count, Keyword.get(constraints, :min))
|> check_max(decl.role, count, Keyword.get(constraints, :max))
end)
end
defp check_min(cs, _role, _count, nil), do: cs
defp check_min(cs, _role, count, min) when count >= min, do: cs
defp check_min(cs, role, count, min),
do:
Ash.Changeset.add_error(cs,
field: :parties,
message: "role #{inspect(role)} requires at least #{min} (got #{count})"
)
defp check_max(cs, _role, _count, nil), do: cs
defp check_max(cs, _role, count, max) when count <= max, do: cs
defp check_max(cs, role, count, max),
do:
Ash.Changeset.add_error(cs,
field: :parties,
message: "role #{inspect(role)} allows at most #{max} (got #{count})"
)
@doc """
Relates the parties in the changeset with the Extended Instance by creating party_ref
"""
def relate_instance(result, changeset)
when is_struct(result) and is_struct(changeset, Ash.Changeset) do
parties = Ash.Changeset.get_argument(changeset, :parties)
case parties do
nil ->
{:ok, result}
[] ->
{:ok, result}
_ ->
Enum.reduce_while(parties, {:ok, []}, fn %{id: id, role: role}, {:ok, acc} ->
case Provider.create_party_ref(%{instance_id: result.id, party_id: id, role: role}) do
{:ok, party_ref} -> {:cont, {:ok, [party_ref | acc]}}
{:error, error} -> {:halt, {:error, error}}
end
end)
|> case do
{:ok, party_refs} -> {:ok, Map.put(result, :parties, party_refs)}
{:error, error} -> {:error, error}
end
end
end
end