From 45928442534e06c4ef02d927a4958d4d4f673266 Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Mon, 18 May 2026 07:35:21 +0930 Subject: [PATCH] refactored assigner using defined_simple_relationship --- AGENTS.md | 1 - lib/diffo/provider.ex | 6 - .../assigner/assigned_to_relationship.ex | 103 ------------------ lib/diffo/provider/assigner/assigner.ex | 51 +++++---- .../provider/components/base_instance.ex | 4 +- .../calculations/assigned_values.ex | 12 +- .../components/calculations/free_values.ex | 12 +- test/provider/extension/assigner_test.exs | 2 +- 8 files changed, 42 insertions(+), 149 deletions(-) delete mode 100644 lib/diffo/provider/assigner/assigned_to_relationship.ex diff --git a/AGENTS.md b/AGENTS.md index 57adba5..86b2964 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,7 +47,6 @@ lib/diffo/provider/ assigner/ assigner.ex # Diffo.Provider.Assigner — assign/3 (pools do) and assign/4 assignable_characteristic.ex # AssignableCharacteristic — pool bounds + algorithm - assigned_to_relationship.ex # AssignedToRelationship — assignedTo edges (pool/thing/assigned) base_instance.ex # Ash Fragment for Instance resources base_party.ex # Ash Fragment for Party resources base_place.ex # Ash Fragment for Place resources diff --git a/lib/diffo/provider.ex b/lib/diffo/provider.ex index cddd8f8..4ee5324 100644 --- a/lib/diffo/provider.ex +++ b/lib/diffo/provider.ex @@ -83,12 +83,6 @@ defmodule Diffo.Provider do define :delete_defined_simple_relationship, action: :destroy end - resource Diffo.Provider.AssignedToRelationship do - define :create_assigned_to_relationship, action: :create_assignment - define :get_assigned_to_relationship_by_id, action: :read, get_by: :id - define :delete_assigned_to_relationship, action: :destroy - end - resource Diffo.Provider.AssignableCharacteristic do define :create_assignable_characteristic, action: :create define :get_assignable_characteristic_by_id, action: :read, get_by: :id diff --git a/lib/diffo/provider/assigner/assigned_to_relationship.ex b/lib/diffo/provider/assigner/assigned_to_relationship.ex deleted file mode 100644 index 1926d88..0000000 --- a/lib/diffo/provider/assigner/assigned_to_relationship.ex +++ /dev/null @@ -1,103 +0,0 @@ -# SPDX-FileCopyrightText: 2025 diffo contributors -# -# SPDX-License-Identifier: MIT - -defmodule Diffo.Provider.AssignedToRelationship do - @moduledoc """ - Ash Resource for a pool assignment relationship. - - Carries the assignment attributes (`pool`, `thing`, `assigned`) that link a - source instance to an assignee instance. Stored as an `:AssignedToRelationship` - Neo4j node, distinct from the `:Relationship` nodes used for TMF service/resource - relationships. Accessible on an instance via `instance.assignments`. - - Created by `Diffo.Provider.Assigner` via `Diffo.Provider.create_assigned_to_relationship/1`. - """ - use Ash.Resource, - fragments: [Diffo.Provider.BaseRelationship], - otp_app: :diffo, - domain: Diffo.Provider - - resource do - description "An Ash Resource for a pool assignment relationship" - plural_name :assigned_to_relationships - end - - neo4j do - relate [ - {:source, :RELATES, :incoming, :Instance}, - {:target, :RELATES, :outgoing, :Instance} - ] - end - - jason do - pick [:type] - - customize fn result, record -> - target_type = Map.get(record, :target_type) - - reference = %Diffo.Provider.Reference{ - id: record.target_id, - href: Map.get(record, :target_href) - } - - list_name = - Diffo.Provider.Relationship.derive_relationship_characteristic_list_name(target_type) - - result - |> Diffo.Util.set(target_type, reference) - |> Diffo.Util.set(list_name, [%{name: record.thing, value: record.assigned}]) - end - - order [ - :type, - :service, - :resource, - :serviceRelationshipCharacteristic, - :resourceRelationshipCharacteristic - ] - end - - actions do - create :create_assignment do - description "creates an assignedTo relationship with pool/thing/assigned attributes" - accept [:pool, :thing, :assigned] - - argument :source_id, :uuid - argument :target_id, :string - - change set_attribute(:type, :assignedTo) - change manage_relationship(:source_id, :source, type: :append) - change manage_relationship(:target_id, :target, type: :append) - change Diffo.Changes.DetailRelationship - end - end - - attributes do - attribute :pool, :atom do - description "the pool name on the source instance" - allow_nil? true - public? true - end - - attribute :thing, :atom do - description "the kind of thing being assigned within the pool" - allow_nil? true - public? true - end - - attribute :assigned, :integer do - description "the assigned value from the pool" - allow_nil? true - public? true - end - end - - identities do - identity :unique_assignment, [:source_id, :target_id, :pool, :thing, :assigned] - end - - preparations do - prepare build(sort: [created_at: :asc]) - end -end diff --git a/lib/diffo/provider/assigner/assigner.ex b/lib/diffo/provider/assigner/assigner.ex index daf5d39..370c7c8 100644 --- a/lib/diffo/provider/assigner/assigner.ex +++ b/lib/diffo/provider/assigner/assigner.ex @@ -4,13 +4,15 @@ defmodule Diffo.Provider.Assigner do @moduledoc """ - Helper to perform Assignment using `Diffo.Provider.AssignedToRelationship`. + Helper to perform Assignment using `Diffo.Provider.DefinedSimpleRelationship`. - Assignment state is stored on `AssignedToRelationship` nodes (pool, thing, assigned), - distinct from regular TMF `Diffo.Provider.Relationship` nodes. + Each assignment is stored as a `DefinedSimpleRelationship` with `type: :assignedTo` + and a single `NameValuePrimitive` characteristic carrying the thing name and assigned value. """ alias Diffo.Provider.AssignableCharacteristic - alias Diffo.Provider.AssignedToRelationship + alias Diffo.Provider.DefinedSimpleRelationship + alias Diffo.Type.NameValuePrimitive + alias Diffo.Type.Primitive @doc """ Assign a thing using the pool declared via `pools do` on the instance module. @@ -63,13 +65,15 @@ defmodule Diffo.Provider.Assigner do end end - defp relate_is_assigned(result, pool, thing, value, assignee_id) - when is_struct(result) and is_atom(pool) and is_atom(thing) and is_integer(value) and + defp relate_is_assigned(result, _pool, thing, value, assignee_id) + when is_struct(result) and is_atom(thing) and is_integer(value) and is_bitstring(assignee_id) do - case Diffo.Provider.create_assigned_to_relationship(%{ - pool: pool, - thing: thing, - assigned: value, + case Diffo.Provider.create_defined_simple_relationship(%{ + type: :assignedTo, + characteristic: %NameValuePrimitive{ + name: thing, + value: Primitive.wrap("integer", value) + }, source_id: result.id, target_id: assignee_id }) do @@ -102,17 +106,22 @@ defmodule Diffo.Provider.Assigner do end end - defp find_assignment(source_id, target_id, pool, thing, value) do - AssignedToRelationship - |> Ash.Query.new() - |> Ash.Query.filter_input( - source_id: source_id, - target_id: target_id, - pool: pool, - thing: thing, - assigned: value - ) - |> Ash.read_one(domain: Diffo.Provider) + defp find_assignment(source_id, target_id, _pool, thing, value) do + case DefinedSimpleRelationship + |> Ash.Query.new() + |> Ash.Query.filter_input(source_id: source_id, target_id: target_id, type: :assignedTo) + |> Ash.read(domain: Diffo.Provider) do + {:ok, rels} -> + {:ok, + Enum.find(rels, fn rel -> + rel.characteristic && + rel.characteristic.name == thing && + Diffo.Unwrap.unwrap(rel.characteristic.value) == value + end)} + + {:error, error} -> + {:error, error} + end end defp next(instance, pool, thing) diff --git a/lib/diffo/provider/components/base_instance.ex b/lib/diffo/provider/components/base_instance.ex index 13e6b90..996362e 100644 --- a/lib/diffo/provider/components/base_instance.ex +++ b/lib/diffo/provider/components/base_instance.ex @@ -188,7 +188,7 @@ defmodule Diffo.Provider.BaseInstance do {:process_statuses, :STATUSES, :incoming, :ProcessStatus}, {:forward_relationships, :RELATES, :outgoing, :Relationship}, {:reverse_relationships, :RELATES, :incoming, :Relationship}, - {:assignments, :RELATES, :outgoing, :AssignedToRelationship}, + {:assignments, :RELATES, :outgoing, :DefinedSimpleRelationship}, {:features, :HAS, :outgoing, :Feature}, {:characteristics, :HAS, :outgoing, :Characteristic}, {:entities, :RELATES, :outgoing, :EntityRef}, @@ -409,7 +409,7 @@ defmodule Diffo.Provider.BaseInstance do public? true end - has_many :assignments, Diffo.Provider.AssignedToRelationship do + has_many :assignments, Diffo.Provider.DefinedSimpleRelationship do description "the instance's outgoing pool assignment relationships" destination_attribute :source_id public? true diff --git a/lib/diffo/provider/components/calculations/assigned_values.ex b/lib/diffo/provider/components/calculations/assigned_values.ex index 6ac879f..3b8a982 100644 --- a/lib/diffo/provider/components/calculations/assigned_values.ex +++ b/lib/diffo/provider/components/calculations/assigned_values.ex @@ -14,15 +14,11 @@ defmodule Diffo.Provider.Calculations.AssignedValues do thing = context.arguments[:thing] Enum.map(records, fn record -> - Diffo.Provider.AssignedToRelationship - |> Ash.Query.new() - |> Ash.Query.filter_input( - source_id: record.instance_id, - pool: record.name, - thing: thing - ) + Diffo.Provider.DefinedSimpleRelationship + |> Ash.Query.filter_input(source_id: record.instance_id, type: :assignedTo) |> Ash.read!(domain: Diffo.Provider) - |> Enum.map(& &1.assigned) + |> Enum.filter(fn rel -> rel.characteristic && rel.characteristic.name == thing end) + |> Enum.map(fn rel -> Diffo.Unwrap.unwrap(rel.characteristic.value) end) end) end end diff --git a/lib/diffo/provider/components/calculations/free_values.ex b/lib/diffo/provider/components/calculations/free_values.ex index 2ed048d..af0de70 100644 --- a/lib/diffo/provider/components/calculations/free_values.ex +++ b/lib/diffo/provider/components/calculations/free_values.ex @@ -17,14 +17,12 @@ defmodule Diffo.Provider.Calculations.FreeValues do record -> count = - Diffo.Provider.AssignedToRelationship - |> Ash.Query.filter_input( - source_id: record.instance_id, - pool: record.name, - thing: record.thing - ) + Diffo.Provider.DefinedSimpleRelationship + |> Ash.Query.filter_input(source_id: record.instance_id, type: :assignedTo) |> Ash.read!(domain: Diffo.Provider) - |> length() + |> Enum.count(fn rel -> + rel.characteristic && rel.characteristic.name == record.thing + end) record.last - record.first + 1 - count end) diff --git a/test/provider/extension/assigner_test.exs b/test/provider/extension/assigner_test.exs index a24c229..cd5fdbc 100644 --- a/test/provider/extension/assigner_test.exs +++ b/test/provider/extension/assigner_test.exs @@ -190,7 +190,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do assert length(card.assignments) == 1 - assigned_port = hd(card.assignments).assigned + assigned_port = Diffo.Unwrap.unwrap(hd(card.assignments).characteristic.value) {:ok, card} = Servo.assign_port(card, %{