Current section

Files

Jump to
diffo lib diffo provider components base_instance.ex
Raw

lib/diffo/provider/components/base_instance.ex

# SPDX-FileCopyrightText: 2025 diffo contributors <https://github.com/diffo-dev/diffo/graphs.contributors>
#
# SPDX-License-Identifier: MIT
defmodule Diffo.Provider.BaseInstance do
@moduledoc """
Diffo - TMF Service and Resource Management with a difference
BaseInstance - Ash Resource Fragment of a TMF Service or Resource Instance
"""
use Spark.Dsl.Fragment,
of: Ash.Resource,
otp_app: :diffo,
domain: Diffo.Provider,
data_layer: AshNeo4j.DataLayer,
extensions: [
AshOutstanding.Resource,
AshJason.Resource,
AshStateMachine,
Diffo.Provider.Instance.Extension
]
alias Diffo.Util, as: Util
alias Diffo.Provider.Instance.Util, as: Instance
neo4j do
relate [
{:external_identifiers, :REFERENCES, :outgoing, :ExternalIdentifier},
{:specification, :SPECIFIED_BY, :outgoing, :Specification},
{:process_statuses, :STATUSES, :incoming, :ProcessStatus},
{:forward_relationships, :RELATES, :outgoing, :Relationship},
{:reverse_relationships, :RELATES, :incoming, :Relationship},
{:features, :HAS, :outgoing, :Feature},
{:characteristics, :HAS, :outgoing, :Characteristic},
{:entities, :RELATES, :outgoing, :EntityRef},
{:notes, :ANNOTATES, :incoming, :Note},
{:event, :FIRED, :outgoing, :Event},
{:places, :RELATES, :outgoing, :PlaceRef},
{:parties, :RELATES, :outgoing, :PartyRef}
]
label :Instance
end
jason do
pick [
:id,
:href,
:name,
:external_identifiers,
:specification,
:process_statuses,
:forward_relationships,
:features,
:characteristics,
:entities,
:places,
:parties,
:type
]
compact true
customize fn result, record ->
result
|> Instance.category(record)
|> Instance.description(record)
|> Util.suppress_rename(:external_identifiers, :externalIdentifier)
|> Instance.dates(record)
|> Instance.states(record)
|> Instance.relationships()
|> Util.rename(:specification, record.specification.type)
|> Util.suppress_rename(:process_statuses, :processStatus)
|> Util.suppress_rename(
:features,
Instance.derive_feature_list_name(record.type)
)
|> Util.suppress_rename(
:characteristics,
Instance.derive_characteristic_list_name(record.type)
)
|> Util.suppress_rename(:entities, :relatedEntity)
|> Util.suppress_rename(:notes, :note)
|> Util.suppress_rename(:places, :place)
|> Util.suppress_rename(:parties, :relatedParty)
end
order [
:id,
:href,
:category,
:description,
:name,
:externalIdentifier,
:serviceSpecification,
:resourceSpecification,
:serviceDate,
:startDate,
:startOperatingDate,
:endDate,
:endOperatingDate,
:state,
:operatingStatus,
:administrativeState,
:operationalState,
:resourceStatus,
:usageState,
:processStatus,
:serviceRelationship,
:resourceRelationship,
:supportingService,
:supportingResource,
:feature,
:activationFeature,
:serviceCharacteristic,
:resourceCharacteristic,
:relatedEntity,
:notes,
:place,
:relatedParty
]
end
outstanding do
expect [:specification, :type, :service_state, :service_operating_status]
# expect [:type, :name, :external_identifiers, :specification, :service_state, :service_operating_status, :forward_relationships, :reverse_relationships, :features, :characteristics, :entities, :process_statuses, :places, :parties]
end
state_machine do
initial_states [:initial]
default_initial_state :initial
state_attribute :service_state
transitions do
transition action: :cancel,
from: [:initial, :feasibilityChecked, :reserved],
to: :cancelled
transition action: :feasibilityCheck, from: :initial, to: :feasibilityChecked
transition action: :reserve, from: [:initial, :feasibilityChecked], to: :reserved
transition action: :deactivate, from: [:active, :reserved], to: [:inactive]
transition action: :activate,
from: [
:initial,
:feasibilityChecked,
:reserved,
:inactive,
:suspended,
:terminated
],
to: :active
transition action: :suspend, from: :active, to: :suspended
transition action: :terminate, from: [:active, :inactive, :suspended], to: :terminated
end
end
attributes do
attribute :id, :uuid do
description "a uuid4, unique to this instance, generated by default"
primary_key? true
allow_nil? false
public? true
default &Diffo.Uuid.uuid4/0
source :uuid
end
attribute :which, :atom do
description "the which of the instance, either expected or actual"
allow_nil? false
default :actual
public? true
constraints one_of: [:expected, :actual]
end
attribute :type, :atom do
description "the type of the instance, either service or resource"
allow_nil? false
default :service
public? true
constraints one_of: [:service, :resource]
end
attribute :name, :string do
description "the name of this service or resource instance"
allow_nil? true
public? true
constraints match: ~r/^[a-zA-Z0-9\s._-]+$/
end
attribute :service_state, :atom do
allow_nil? false
default Diffo.Provider.Service.default_service_state()
public? true
constraints one_of: Diffo.Provider.Service.service_states()
end
attribute :service_operating_status, :atom do
description "the service operating status, if this instance is a service"
allow_nil? true
public? true
default nil
constraints one_of: Diffo.Provider.Service.service_operating_statuses()
end
create_timestamp :created_at
update_timestamp :updated_at
attribute :started_at, :utc_datetime_usec do
allow_nil? true
end
attribute :stopped_at, :utc_datetime_usec do
allow_nil? true
end
end
relationships do
has_many :external_identifiers, Diffo.Provider.ExternalIdentifier do
description "the instance's list of external identifiers"
public? true
destination_attribute :instance_id
end
belongs_to :specification, Diffo.Provider.Specification do
description "the specification which specifies this instance"
public? true
end
has_many :process_statuses, Diffo.Provider.ProcessStatus do
description "the instance's process status collection"
public? true
destination_attribute :instance_id
end
has_many :forward_relationships, Diffo.Provider.Relationship do
description "the instance's outgoing relationships to other instances"
destination_attribute :source_id
public? true
end
has_many :reverse_relationships, Diffo.Provider.Relationship do
description "the instance's incoming relationships from other instances"
destination_attribute :target_id
public? true
end
has_many :features, Diffo.Provider.Feature do
description "the instance's collection of defining features"
public? true
destination_attribute :instance_id
end
has_many :characteristics, Diffo.Provider.Characteristic do
description "the instance's collection of defining characteristics"
public? true
destination_attribute :instance_id
end
has_many :entities, Diffo.Provider.EntityRef do
description "the instance's collection of related entities"
public? true
destination_attribute :instance_id
end
has_many :notes, Diffo.Provider.Note do
description "the instance's collection of annotating notes"
public? true
destination_attribute :instance_id
end
has_one :event, Diffo.Provider.Event do
description "the most recently fired event"
public? true
destination_attribute :instance_id
end
has_many :places, Diffo.Provider.PlaceRef do
description "the instance's collection of related places"
public? true
destination_attribute :instance_id
end
has_many :parties, Diffo.Provider.PartyRef do
description "the instance's collection of related parties"
public? true
destination_attribute :instance_id
end
end
actions do
defaults [:destroy]
create :create do
description "creates a new instance of a service or resource according by specification id"
accept [:id, :name, :type, :which]
argument :specified_by, :uuid
argument :features, {:array, :uuid}
argument :characteristics, {:array, :uuid}
change manage_relationship(:specified_by, :specification, type: :append)
change manage_relationship(:features, type: :append)
change manage_relationship(:characteristics, type: :append)
change load [:href]
upsert? true
end
read :read do
description "read a service or resource instance"
primary? true
end
read :list do
description "lists all service and resource instances"
end
read :find_by_name do
description "finds service and resource instances by name"
get? false
argument :query, :ci_string do
description "Return only instances with names including the given value."
end
filter expr(contains(name, ^arg(:query)))
end
read :find_by_specification_id do
description "list service or resource instances by specification id"
get? false
argument :query, :string do
description "Return only instances specified by the given specification id."
end
# prepare build(sort: [name: :asc])
filter expr(specification_id == ^arg(:query))
end
update :name do
description "updates the name"
require_atomic? false
accept [:name]
end
update :specify do
description "specifies the instance by specification id"
require_atomic? false
argument :specified_by, :uuid
change manage_relationship(:specified_by, :specification, type: :append_and_remove)
# todo validate that the new specification has same name (will have different major version)
end
update :cancel do
description "cancels a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:cancelled)
change set_attribute(:service_operating_status, :unknown)
change set_attribute(:stopped_at, &DateTime.utc_now/0)
end
update :feasibilityCheck do
description "feasibilityChecks a service instance"
require_atomic? false
accept [:service_operating_status]
validate attribute_equals(:type, :service)
change transition_state(:feasibilityChecked)
validate argument_in(:service_operating_status, [
nil,
:initial,
:pending,
:unknown,
:feasible,
:not_feasible
])
end
update :reserve do
description "reserves a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:reserved)
change set_attribute(:service_operating_status, :pending)
end
update :deactivate do
description "deactivates a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:inactive)
change set_attribute(:service_operating_status, :configured)
end
update :activate do
description "activates a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:active)
change set_attribute(:service_operating_status, :starting)
change set_attribute(:started_at, &DateTime.utc_now/0)
end
update :suspend do
description "suspends a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:suspended)
change set_attribute(:service_operating_status, :limited)
end
update :terminate do
description "terminates a service instance"
require_atomic? false
validate attribute_equals(:type, :service)
change transition_state(:terminated)
change set_attribute(:service_operating_status, :stopping)
change set_attribute(:stopped_at, &DateTime.utc_now/0)
end
update :status do
description "updates the status of an instance"
require_atomic? false
validate attribute_equals(:type, :service)
accept [:service_operating_status]
end
update :relate_features do
description "relates features to the instance"
argument :features, {:array, :uuid}
change manage_relationship(:features, type: :append)
end
update :unrelate_features do
description "unrelates features from the instance"
argument :features, {:array, :uuid}
change manage_relationship(:features, type: :remove)
end
update :relate_characteristics do
description "relates characteristics to the instance"
argument :characteristics, {:array, :uuid}
change manage_relationship(:characteristics, type: :append)
end
update :unrelate_characteristics do
description "unrelates characteristics from the instance"
argument :characteristics, {:array, :uuid}
change manage_relationship(:characteristics, type: :remove)
end
update :annotate do
description "annotates the instance with a note"
argument :note, :uuid
change manage_relationship(:note, :notes, type: :append)
end
update :fire_event do
description "fires an event, maintaining the event chain"
argument :event, :map do
allow_nil? false
end
change Diffo.Changes.DetailEvent
change manage_relationship(:event, type: :create)
change load [:event]
end
end
code_interface do
define :read
end
identities do
identity :unique_name_per_type, [:name] do
message "instance name must be unique"
pre_check? true
end
end
preparations do
prepare build(
load: [
:href,
:external_identifiers,
:specification,
:process_statuses,
:forward_relationships,
:entities,
:notes,
:features,
:characteristics,
:places,
:parties
],
sort: [created_at: :desc]
)
end
calculations do
calculate :href,
:string,
expr(
type <>
"InventoryManagement/v" <>
specification.tmf_version <>
"/" <>
type <>
"/" <>
specification.name <>
"/" <> id
) do
description "the inventory href of the service or resource instance"
end
end
end