Current section
Files
Jump to
Current section
Files
lib/diffo/provider/components/relationship.ex
# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
#
# SPDX-License-Identifier: MIT
defmodule Diffo.Provider.Relationship do
@moduledoc """
Diffo - TMF Service and Resource Management with a difference
Relationship - Ash Resource for a TMF Service or Resource Relationship
"""
use Ash.Resource,
otp_app: :diffo,
domain: Diffo.Provider,
data_layer: AshNeo4j.DataLayer,
extensions: [AshOutstanding.Resource, AshJason.Resource]
neo4j do
relate [
{:source, :RELATES, :incoming, :Instance},
{:target, :RELATES, :outgoing, :Instance},
{:characteristics, :HAS, :outgoing, :Characteristic}
]
guard [
{:HAS, :outgoing, :Characteristic}
]
end
jason do
pick [:alias, :type, :characteristics]
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.suppress_rename(:characteristics, list_name)
|> Diffo.Util.suppress(:alias)
end
order [
:alias,
:type,
:service,
:resource,
:serviceRelationshipCharacteristic,
:resourceRelationshipCharacteristic
]
end
outstanding do
expect [:alias, :type, :target, :characteristics]
end
actions do
defaults [:read, :destroy]
create :create do
description "creates a relationship between a source and target instance"
accept [:source_id, :target_id, :type, :alias]
argument :source_id, :uuid
argument :target_id, :string
argument :characteristics, {:array, :uuid}
change manage_relationship(:source_id, :source, type: :append)
change manage_relationship(:target_id, :target, type: :append)
change manage_relationship(:characteristics, type: :append)
change Diffo.Changes.DetailRelationship
change load [:characteristics]
end
read :list do
description "lists all relationships"
end
read :list_service_relationships_from do
description "lists service relationships from the instance"
argument :instance_id, :uuid
filter expr(source_id == ^arg(:instance_id) and target.type == :service)
end
read :list_resource_relationships_from do
description "lists resource relationships from the instance"
argument :instance_id, :uuid
filter expr(source_id == ^arg(:instance_id) and target.type == :resource)
end
update :update do
description "updates the relationship type and/or alias"
accept [:alias, :type]
end
update :relate_characteristics do
description "relates characteristics to the relationship"
argument :characteristics, {:array, :uuid}
change manage_relationship(:characteristics, type: :append)
end
update :unrelate_characteristics do
description "unrelates characteristic from the relationship"
argument :characteristics, {:array, :uuid}
change manage_relationship(:characteristics, type: :remove)
end
end
attributes do
uuid_primary_key :id do
description "a uuid4, unique to this instance, generated by default"
public? true
end
attribute :alias, :atom do
description "the alias of this relationship, used for supporting service or resource"
allow_nil? true
public? true
end
attribute :type, :atom do
description "the type of the relationship from the source to the target"
allow_nil? false
public? true
end
attribute :target_href, :string do
description "the target href"
allow_nil? true
writable? false
public? true
end
attribute :target_type, :atom do
description "the target type"
allow_nil? true
writable? false
public? true
end
create_timestamp :inserted_at
update_timestamp :updated_at
end
relationships do
belongs_to :source, Diffo.Provider.Instance do
description "the source instance which relates to the target instance via this relationship"
allow_nil? false
public? true
end
belongs_to :target, Diffo.Provider.Instance do
description "the target instance which is related from the source instance via this relationship"
allow_nil? false
public? true
end
has_many :characteristics, Diffo.Provider.Characteristic do
description "the relationship's collection of defining characteristics"
public? true
end
end
identities do
identity :unique_source_and_target, [:source_id, :target_id]
end
validations do
validate {Diffo.Validations.IsUuid4OrNil, attribute: :source_id}, on: :create
validate {Diffo.Validations.IsUuid4OrNil, attribute: :target_id}, on: :create
# validate present(:alias) do
# on [:create, :update]
# where [one_of(:source_type, [:resource]), one_of(:target_type, [:service])]
# message "a resource cannot have a supporting service"
# end
# validate {Diffo.Validations.RelatedResourcesDifferent,
# relationship: :characteristic, attribute: :name},
# on: :update
end
preparations do
prepare build(
load: [:characteristics],
sort: [alias: :asc, type: :asc, inserted_at: :asc]
)
end
@doc """
Derives the instance relationship name from the instance type
## Examples
iex> Diffo.Provider.Relationship.derive_relationship_name(:service)
:serviceRelationship
iex> Diffo.Provider.Relationship.derive_relationship_name(:resource)
:resourceRelationship
"""
def derive_relationship_name(instance_type) do
case instance_type do
:service -> :serviceRelationship
:resource -> :resourceRelationship
_ -> nil
end
end
@doc """
Derives the instance relationship characteristic list name from the instance type
## Examples
iex> Diffo.Provider.Relationship.derive_relationship_characteristic_list_name(:service)
:serviceRelationshipCharacteristic
iex> Diffo.Provider.Relationship.derive_relationship_characteristic_list_name(:resource)
:resourceRelationshipCharacteristic
"""
def derive_relationship_characteristic_list_name(instance_type) do
case instance_type do
:service -> :serviceRelationshipCharacteristic
:resource -> :resourceRelationshipCharacteristic
_ -> nil
end
end
end