From 4ab8f95b0f7a3250510333be9646bace4391062f Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Thu, 19 Mar 2026 23:44:53 +1030 Subject: [PATCH 1/6] initial vibe --- config/config.exs | 2 +- lib/nbn/nbn.ex | 82 ++++++ lib/nbn/resources/avc.ex | 88 +++++++ .../characteristic_values/avc_value.ex | 33 +++ .../characteristic_values/cvc_value.ex | 33 +++ .../nbn_ethernet_value.ex | 36 +++ .../characteristic_values/nni_group_value.ex | 33 +++ .../characteristic_values/nni_value.ex | 35 +++ .../characteristic_values/ntd_value.ex | 36 +++ .../characteristic_values/uni_value.ex | 36 +++ lib/nbn/resources/cvc.ex | 102 ++++++++ lib/nbn/resources/nbn_ethernet.ex | 89 +++++++ lib/nbn/resources/nni.ex | 89 +++++++ lib/nbn/resources/nni_group.ex | 102 ++++++++ lib/nbn/resources/ntd.ex | 88 +++++++ lib/nbn/resources/uni.ex | 89 +++++++ mix.exs | 2 +- mix.lock | 4 +- test/nbn/nbn_ethernet_test.exs | 240 ++++++++++++++++++ 19 files changed, 1215 insertions(+), 4 deletions(-) create mode 100644 lib/nbn/nbn.ex create mode 100644 lib/nbn/resources/avc.ex create mode 100644 lib/nbn/resources/characteristic_values/avc_value.ex create mode 100644 lib/nbn/resources/characteristic_values/cvc_value.ex create mode 100644 lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex create mode 100644 lib/nbn/resources/characteristic_values/nni_group_value.ex create mode 100644 lib/nbn/resources/characteristic_values/nni_value.ex create mode 100644 lib/nbn/resources/characteristic_values/ntd_value.ex create mode 100644 lib/nbn/resources/characteristic_values/uni_value.ex create mode 100644 lib/nbn/resources/cvc.ex create mode 100644 lib/nbn/resources/nbn_ethernet.ex create mode 100644 lib/nbn/resources/nni.ex create mode 100644 lib/nbn/resources/nni_group.ex create mode 100644 lib/nbn/resources/ntd.ex create mode 100644 lib/nbn/resources/uni.ex create mode 100644 test/nbn/nbn_ethernet_test.exs diff --git a/config/config.exs b/config/config.exs index 053ae6b..ff4d1b4 100644 --- a/config/config.exs +++ b/config/config.exs @@ -37,5 +37,5 @@ config :spark, ] config :diffo, ash_domains: [Diffo.Provider] -config :diffo_example, ash_domains: [DiffoExample.Access] +config :diffo_example, ash_domains: [DiffoExample.Access, DiffoExample.Nbn] import_config "#{config_env()}.exs" diff --git a/lib/nbn/nbn.ex b/lib/nbn/nbn.ex new file mode 100644 index 0000000..8290ac6 --- /dev/null +++ b/lib/nbn/nbn.ex @@ -0,0 +1,82 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Nbn - example NBN domain + + Models NBN network resources including the Ethernet circuit (NbnEthernet) + and its constituent resources: UNI (dedicated), AVC (dedicated), NTD, + CVC (aggregates AVCs, terminates at NNI Group), NNI Group, and NNI. + """ + use Ash.Domain, + otp_app: :diffo + + alias DiffoExample.Nbn.NbnEthernet + alias DiffoExample.Nbn.Uni + alias DiffoExample.Nbn.Avc + alias DiffoExample.Nbn.Ntd + alias DiffoExample.Nbn.Cvc + alias DiffoExample.Nbn.NniGroup + alias DiffoExample.Nbn.Nni + + domain do + description "An example showing how TMF Resources for a fictional NBN domain can be extended from the Provider domain" + end + + resources do + resource NbnEthernet do + define :get_nbn_ethernet_by_id, action: :read, get_by: :id + define :build_nbn_ethernet, action: :build + define :define_nbn_ethernet, action: :define + define :relate_nbn_ethernet, action: :relate + end + + resource Uni do + define :get_uni_by_id, action: :read, get_by: :id + define :build_uni, action: :build + define :define_uni, action: :define + define :relate_uni, action: :relate + end + + resource Avc do + define :get_avc_by_id, action: :read, get_by: :id + define :build_avc, action: :build + define :define_avc, action: :define + define :relate_avc, action: :relate + end + + resource Ntd do + define :get_ntd_by_id, action: :read, get_by: :id + define :build_ntd, action: :build + define :define_ntd, action: :define + define :relate_ntd, action: :relate + end + + resource Cvc do + define :get_cvc_by_id, action: :read, get_by: :id + define :build_cvc, action: :build + define :define_cvc, action: :define + define :assign_cvlan, action: :assign_cvlan + define :relate_cvc, action: :relate + end + + resource NniGroup do + define :get_nni_group_by_id, action: :read, get_by: :id + define :build_nni_group, action: :build + define :define_nni_group, action: :define + define :assign_svlan, action: :assign_svlan + define :relate_nni_group, action: :relate + end + + resource Nni do + define :get_nni_by_id, action: :read, get_by: :id + define :build_nni, action: :build + define :define_nni, action: :define + define :relate_nni, action: :relate + end + end +end diff --git a/lib/nbn/resources/avc.ex b/lib/nbn/resources/avc.ex new file mode 100644 index 0000000..8213d4b --- /dev/null +++ b/lib/nbn/resources/avc.ex @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Avc do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Avc - Access Virtual Circuit Resource Instance + + An AVC is the virtual circuit dedicated to an NBN Ethernet circuit, + carrying traffic between its related UNI and the CVC that aggregates it. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing an Access Virtual Circuit (AVC)" + plural_name :Avcs + end + + specification do + id "b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e" + name "avc" + type :resourceSpecification + description "An AVC Resource Instance dedicated to an NBN Ethernet circuit" + category "Network Resource" + end + + characteristics do + characteristic :avc, DiffoExample.Nbn.AvcValue + end + + actions do + create :build do + description "creates a new AVC resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_avc_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the AVC" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_avc_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the AVC with other instances" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_avc_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/avc_value.ex b/lib/nbn/resources/characteristic_values/avc_value.ex new file mode 100644 index 0000000..f24266a --- /dev/null +++ b/lib/nbn/resources/characteristic_values/avc_value.ex @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.AvcValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + AvcValue - AshTyped Struct for AVC Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:cir, :pir] + compact(true) + end + + outstanding do + expect [:cir, :pir] + end + + typed_struct do + field :cir, :integer, description: "Committed Information Rate in Mbps" + + field :pir, :integer, description: "Peak Information Rate in Mbps" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/cvc_value.ex b/lib/nbn/resources/characteristic_values/cvc_value.ex new file mode 100644 index 0000000..dc68a2e --- /dev/null +++ b/lib/nbn/resources/characteristic_values/cvc_value.ex @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.CvcValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + CvcValue - AshTyped Struct for Connectivity Virtual Circuit Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:cvc_id, :bandwidth] + compact(true) + end + + outstanding do + expect [:cvc_id, :bandwidth] + end + + typed_struct do + field :cvc_id, :string, description: "the unique CVC identifier" + + field :bandwidth, :integer, description: "total CVC bandwidth in Mbps" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex b/lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex new file mode 100644 index 0000000..57e0d27 --- /dev/null +++ b/lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NbnEthernetValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NbnEthernetValue - AshTyped Struct for NBN Ethernet Circuit Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:circuit_id, :speed, :technology] + compact(true) + end + + outstanding do + expect [:circuit_id, :speed] + end + + typed_struct do + field :circuit_id, :string, description: "the unique NBN circuit identifier" + + field :speed, :integer, description: "the circuit download speed in Mbps" + + field :technology, :atom, + description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/nni_group_value.ex b/lib/nbn/resources/characteristic_values/nni_group_value.ex new file mode 100644 index 0000000..bed82c3 --- /dev/null +++ b/lib/nbn/resources/characteristic_values/nni_group_value.ex @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NniGroupValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NniGroupValue - AshTyped Struct for NNI Group Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:name, :location] + compact(true) + end + + outstanding do + expect [:name, :location] + end + + typed_struct do + field :name, :string, description: "the NNI group name" + + field :location, :string, description: "the Point of Interconnect (PoI) location" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/nni_value.ex b/lib/nbn/resources/characteristic_values/nni_value.ex new file mode 100644 index 0000000..e3028fa --- /dev/null +++ b/lib/nbn/resources/characteristic_values/nni_value.ex @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NniValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NniValue - AshTyped Struct for NNI Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:port_id, :capacity, :technology] + compact(true) + end + + outstanding do + expect [:port_id, :capacity] + end + + typed_struct do + field :port_id, :string, description: "the NNI port identifier" + + field :capacity, :integer, description: "the NNI port capacity in Gbps" + + field :technology, :atom, description: "the NNI technology (:Ethernet, :Fibre)" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/ntd_value.ex b/lib/nbn/resources/characteristic_values/ntd_value.ex new file mode 100644 index 0000000..85ee77b --- /dev/null +++ b/lib/nbn/resources/characteristic_values/ntd_value.ex @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NtdValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NtdValue - AshTyped Struct for NTD Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:model, :serial_number, :technology] + compact(true) + end + + outstanding do + expect [:model, :serial_number] + end + + typed_struct do + field :model, :string, description: "the NTD device model" + + field :serial_number, :string, description: "the NTD serial number" + + field :technology, :atom, + description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/characteristic_values/uni_value.ex b/lib/nbn/resources/characteristic_values/uni_value.ex new file mode 100644 index 0000000..56e8870 --- /dev/null +++ b/lib/nbn/resources/characteristic_values/uni_value.ex @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.UniValue do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + UniValue - AshTyped Struct for UNI Characteristic Value + """ + use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + + jason do + pick [:vlan_id, :bandwidth_profile, :technology] + compact(true) + end + + outstanding do + expect [:vlan_id, :technology] + end + + typed_struct do + field :vlan_id, :integer, description: "the VLAN ID for the UNI" + + field :bandwidth_profile, :string, description: "the bandwidth profile name for the UNI" + + field :technology, :atom, + description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + end + + defimpl String.Chars do + def to_string(struct) do + inspect(struct) + end + end +end diff --git a/lib/nbn/resources/cvc.ex b/lib/nbn/resources/cvc.ex new file mode 100644 index 0000000..ef17ccd --- /dev/null +++ b/lib/nbn/resources/cvc.ex @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Cvc do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Cvc - Connectivity Virtual Circuit Resource Instance + + A CVC is the wholesale bandwidth product that aggregates one or more AVC + resources and terminates at an NNI Group resource. Each AVC has a related UNI. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + alias Diffo.Provider.Assigner + alias Diffo.Provider.Assignment + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing a Connectivity Virtual Circuit (CVC)" + plural_name :Cvcs + end + + specification do + id "d4e5f6a7-8b9c-4d0e-bf1a-3b4c5d6e7f8a" + name "cvc" + type :resourceSpecification + description "A Connectivity Virtual Circuit Resource Instance that aggregates AVCs and terminates at an NNI Group" + category "Network Resource" + end + + characteristics do + characteristic :cvc, DiffoExample.Nbn.CvcValue + characteristic :cvlan_ids, Diffo.Provider.AssignableValue + end + + actions do + create :build do + description "creates a new CVC resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_cvc_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the CVC" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_cvc_by_id(result.id), + do: {:ok, result} + end) + end + + update :assign_cvlan do + description "assigns a C-VLAN ID from the CVC pool to an AVC" + argument :assignment, :struct, constraints: [instance_of: Assignment] + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Assigner.assign(result, changeset, :cvlan_ids, :cvlan_id), + {:ok, result} <- Nbn.get_cvc_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the CVC with other instances (e.g. AVC aggregation, NNI Group termination)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_cvc_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/nbn_ethernet.ex b/lib/nbn/resources/nbn_ethernet.ex new file mode 100644 index 0000000..aee6dbf --- /dev/null +++ b/lib/nbn/resources/nbn_ethernet.ex @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NbnEthernet do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NbnEthernet - NBN Ethernet Circuit Resource Instance + + An NBN Ethernet circuit comprising a dedicated UNI and AVC resource. + The circuit is related to its UNI, which in turn is aggregated by a CVC + that terminates at an NNI Group. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing an NBN Ethernet Circuit" + plural_name :NbnEthernets + end + + specification do + id "f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c" + name "nbnEthernet" + type :resourceSpecification + description "An NBN Ethernet Circuit comprising a dedicated UNI and AVC" + category "Network Resource" + end + + characteristics do + characteristic :nbn_ethernet, DiffoExample.Nbn.NbnEthernetValue + end + + actions do + create :build do + description "creates a new NBN Ethernet circuit resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_nbn_ethernet_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the NBN Ethernet circuit" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_nbn_ethernet_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the NBN Ethernet circuit with other instances (e.g. UNI)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_nbn_ethernet_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/nni.ex b/lib/nbn/resources/nni.ex new file mode 100644 index 0000000..81e070a --- /dev/null +++ b/lib/nbn/resources/nni.ex @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Nni do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Nni - Network-to-Network Interface Resource Instance + + An NNI is the physical handover port between the NBN network and the + Retail Service Provider (RSP). Multiple NNI resources are grouped within + an NNI Group resource. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing a Network-to-Network Interface (NNI)" + plural_name :Nnis + end + + specification do + id "f6a7b8c9-0d1e-4f2a-9b3c-5d6e7f8a9b0c" + name "nni" + type :resourceSpecification + description "An NNI Resource Instance that is part of an NNI Group" + category "Network Resource" + end + + characteristics do + characteristic :nni, DiffoExample.Nbn.NniValue + end + + actions do + create :build do + description "creates a new NNI resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_nni_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the NNI" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_nni_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the NNI with other instances (e.g. its parent NNI Group)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_nni_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/nni_group.ex b/lib/nbn/resources/nni_group.ex new file mode 100644 index 0000000..630fdc5 --- /dev/null +++ b/lib/nbn/resources/nni_group.ex @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NniGroup do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + NniGroup - NNI Group Resource Instance + + An NNI Group is the Point of Interconnect (PoI) grouping where a CVC + terminates. It comprises multiple NNI resources. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + alias Diffo.Provider.Assigner + alias Diffo.Provider.Assignment + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing an NNI Group" + plural_name :NniGroups + end + + specification do + id "e5f6a7b8-9c0d-4e1f-8a2b-4c5d6e7f8a9b" + name "nniGroup" + type :resourceSpecification + description "An NNI Group Resource Instance comprising multiple NNI resources" + category "Network Resource" + end + + characteristics do + characteristic :nni_group, DiffoExample.Nbn.NniGroupValue + characteristic :svlan_ids, Diffo.Provider.AssignableValue + end + + actions do + create :build do + description "creates a new NNI Group resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_nni_group_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the NNI Group" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_nni_group_by_id(result.id), + do: {:ok, result} + end) + end + + update :assign_svlan do + description "assigns an S-VLAN ID from the NNI Group pool to a CVC" + argument :assignment, :struct, constraints: [instance_of: Assignment] + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Assigner.assign(result, changeset, :svlan_ids, :svlan_id), + {:ok, result} <- Nbn.get_nni_group_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the NNI Group with other instances (e.g. NNI resources it comprises)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_nni_group_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/ntd.ex b/lib/nbn/resources/ntd.ex new file mode 100644 index 0000000..6ef9617 --- /dev/null +++ b/lib/nbn/resources/ntd.ex @@ -0,0 +1,88 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Ntd do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Ntd - Network Termination Device Resource Instance + + An NTD is the device installed at the customer premises that connects + the premises to the NBN network. It is related to a UNI resource. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing a Network Termination Device (NTD)" + plural_name :Ntds + end + + specification do + id "c3d4e5f6-7a8b-4c9d-ae0f-2a3b4c5d6e7f" + name "ntd" + type :resourceSpecification + description "An NTD Resource Instance related to a UNI" + category "Network Resource" + end + + characteristics do + characteristic :ntd, DiffoExample.Nbn.NtdValue + end + + actions do + create :build do + description "creates a new NTD resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_ntd_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the NTD" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_ntd_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the NTD with other instances (e.g. UNI)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_ntd_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/lib/nbn/resources/uni.ex b/lib/nbn/resources/uni.ex new file mode 100644 index 0000000..2408411 --- /dev/null +++ b/lib/nbn/resources/uni.ex @@ -0,0 +1,89 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Uni do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Uni - User Network Interface Resource Instance + + A UNI is the physical/logical interface at the customer premises. It is + related to an NTD resource and to its parent NBN Ethernet circuit. + It is related to an AVC resource, which is in turn aggregated by a CVC. + """ + + alias Diffo.Provider.BaseInstance + alias Diffo.Provider.Instance.Relationship + alias Diffo.Provider.Instance.Characteristic + alias Diffo.Provider.Instance.ActionHelper + + alias DiffoExample.Nbn + + use Ash.Resource, + fragments: [BaseInstance], + domain: Nbn + + resource do + description "An Ash Resource representing a User Network Interface (UNI)" + plural_name :Unis + end + + specification do + id "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d" + name "uni" + type :resourceSpecification + description "A UNI Resource Instance related to an NTD and an NBN Ethernet circuit" + category "Network Resource" + end + + characteristics do + characteristic :uni, DiffoExample.Nbn.UniValue + end + + actions do + create :build do + description "creates a new UNI resource instance" + accept [:id, :name, :type, :which] + argument :specified_by, :uuid, public?: false + argument :relationships, {:array, :struct} + argument :features, {:array, :uuid}, public?: false + argument :characteristics, {:array, :uuid}, public?: false + argument :places, {:array, :struct} + argument :parties, {:array, :struct} + + change set_attribute(:type, :resource) + + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) + + change after_action(fn changeset, result, _context -> + ActionHelper.build_after(changeset, result, Nbn, :get_uni_by_id) + end) + + change load [:href] + upsert? false + end + + update :define do + description "defines the UNI" + argument :characteristic_value_updates, {:array, :term} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_uni_by_id(result.id), + do: {:ok, result} + end) + end + + update :relate do + description "relates the UNI with other instances (e.g. NTD, NBN Ethernet circuit)" + argument :relationships, {:array, :struct} + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Relationship.relate_instance(result, changeset), + {:ok, result} <- Nbn.get_uni_by_id(result.id), + do: {:ok, result} + end) + end + end +end diff --git a/mix.exs b/mix.exs index ce6cbe8..ab342bd 100644 --- a/mix.exs +++ b/mix.exs @@ -78,7 +78,7 @@ defmodule DiffoExample.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:diffo, diffo_version("~> 0.1.5")}, + {:diffo, diffo_version("~> 0.1.6")}, {:igniter, "~> 0.6", only: [:dev, :test]}, {:ex_doc, "~> 0.37", only: [:dev, :test], runtime: false} ] diff --git a/mix.lock b/mix.lock index 34987a8..7134cf1 100644 --- a/mix.lock +++ b/mix.lock @@ -1,14 +1,14 @@ %{ "ash": {:hex, :ash, "3.19.3", "58b1bb3aea3d1d45d1c990059ffd0753409cc92fc4afe387376cb155e2a8c2a0", [:mix], [{:crux, ">= 0.1.2 and < 1.0.0-0", [hex: :crux, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 1.0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.3.14 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.3", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "94b628319f2e144affaf1f8008277bad3340a198d48e6d2ed372990ac1643f9b"}, "ash_jason": {:hex, :ash_jason, "3.1.0", "84a88dfe5e25a20d55cf2d2664885cd086fa45871e8777aedc3ad96a282e2a6f", [:mix], [{:ash, ">= 3.6.2 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:spark, ">= 2.1.21 and < 3.0.0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "71e6bbc421fb2cf7079f8804814145cca458116c839fc798f9606b806e07eb2b"}, - "ash_neo4j": {:hex, :ash_neo4j, "0.2.14", "c6bb1b895510ab423afc4225840d4c4e94716490316bf75aa8da1da464f53e0a", [:mix], [{:ash, ">= 3.19.1 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:bolty, ">= 0.0.7", [hex: :bolty, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "888bd4a9fcc3c231642dfb4cb9110d9aef8a57ff61f16f078bfd278cde17cd69"}, + "ash_neo4j": {:hex, :ash_neo4j, "0.2.15", "1380f3a3f6b43fb346308808d621bd19b891b859431fd4b1ac26472fcee5b372", [:mix], [{:ash, ">= 3.19.1 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:bolty, ">= 0.0.7", [hex: :bolty, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8be0a84e02ce70d691b3619c1f7cb7c8e1d9daf001673a227b27762f3690ac40"}, "ash_outstanding": {:hex, :ash_outstanding, "0.2.4", "c72b91f1b8e4859fb033eddf66d0ba36cfd8af0c2a9748c7ef9e6ccfdb5d093d", [:mix], [{:ash, ">= 3.6.2 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:outstanding, "~> 0.2.4", [hex: :outstanding, repo: "hexpm", optional: false]}], "hexpm", "64ba8f582ce69c9050352c75f0895db186c7a56f35039dab34c8e1ab7516f9ce"}, "ash_state_machine": {:hex, :ash_state_machine, "0.2.12", "c0f7ebb8a176584f70c6ed196b7d0118c930d73e0590ade705d2dddc48aa7311", [:mix], [{:ash, ">= 3.4.66 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}], "hexpm", "394ce761ce82358e3c715e1cae6c5cf1390be27c03a8b661f2e5a2fda849873d"}, "bolty": {:hex, :bolty, "0.0.7", "257889f71bd16a9291bda8290c1018123c4084c8761f905b97d7176e65d8b111", [:mix], [{:db_connection, "~> 2.7.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "fc8f88b86d292ad1336f9e246a6c0452e64774b38fac0351c141549420946e5e"}, "crux": {:hex, :crux, "0.1.2", "4441c9e3a34f1e340954ce96b9ad5a2de13ceb4f97b3f910211227bb92e2ca90", [:mix], [{:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: true]}], "hexpm", "563ea3748ebfba9cc078e6d198a1d6a06015a8fae503f0b721363139f0ddb350"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, - "diffo": {:hex, :diffo, "0.1.5", "e37d32763bd3a9b6a984523fb4f8ef9310266349576523c1d3085d7a1b8234c7", [:mix], [{:ash, ">= 3.19.1 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_jason, "~> 3.0", [hex: :ash_jason, repo: "hexpm", optional: false]}, {:ash_neo4j, "~> 0.2.14", [hex: :ash_neo4j, repo: "hexpm", optional: false]}, {:ash_outstanding, "~> 0.2.3", [hex: :ash_outstanding, repo: "hexpm", optional: false]}, {:ash_state_machine, "~> 0.2.12", [hex: :ash_state_machine, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "761538f03f2f79c3170413925cf8b42545063303e5c7f4a0d1923bb964528161"}, + "diffo": {:hex, :diffo, "0.1.6", "12325449e4c17e1d2e14d821387cebcd2167542e3efccf3af15cc3ef99ef425f", [:mix], [{:ash, ">= 3.19.1 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ash_jason, "~> 3.0", [hex: :ash_jason, repo: "hexpm", optional: false]}, {:ash_neo4j, "~> 0.2.15", [hex: :ash_neo4j, repo: "hexpm", optional: false]}, {:ash_outstanding, "~> 0.2.3", [hex: :ash_outstanding, repo: "hexpm", optional: false]}, {:ash_state_machine, "~> 0.2.12", [hex: :ash_state_machine, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:uuid, "~> 1.1", [hex: :uuid, repo: "hexpm", optional: false]}], "hexpm", "ef07a854021bb8a86ba38d59cdf6fdd4fbc00e0e01fdc0bc7cf9c606977ce9f8"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"}, "ets": {:hex, :ets, "0.9.0", "79c6a6c205436780486f72d84230c6cba2f8a9920456750ddd1e47389107d5fd", [:mix], [], "hexpm", "2861fdfb04bcaeff370f1a5904eec864f0a56dcfebe5921ea9aadf2a481c822b"}, diff --git a/test/nbn/nbn_ethernet_test.exs b/test/nbn/nbn_ethernet_test.exs new file mode 100644 index 0000000..5b3e0d0 --- /dev/null +++ b/test/nbn/nbn_ethernet_test.exs @@ -0,0 +1,240 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.NbnEthernetTest do + @moduledoc false + use ExUnit.Case + alias Diffo.Provider.Specification + alias Diffo.Provider.Characteristic + alias DiffoExample.Nbn + alias DiffoExample.Nbn.NbnEthernet + alias DiffoExample.Nbn.Uni + alias DiffoExample.Nbn.Avc + alias DiffoExample.Nbn.Ntd + alias DiffoExample.Nbn.Cvc + alias DiffoExample.Nbn.NniGroup + alias DiffoExample.Nbn.Nni + alias DiffoExample.Test.Characteristics + + setup_all do + AshNeo4j.BoltyHelper.start() + end + + setup do + on_exit(fn -> + AshNeo4j.Neo4jHelper.delete_all() + end) + end + + describe "build nbn_ethernet" do + test "create an nbn_ethernet circuit" do + {:ok, circuit} = Nbn.build_nbn_ethernet(%{}) + + # check the instance is an NbnEthernet + assert is_struct(circuit, NbnEthernet) + + # check specification resource enrichment and node relationship + refute is_nil(circuit.specification_id) + assert is_struct(circuit.specification, Specification) + + assert AshNeo4j.Neo4jHelper.nodes_relate_how?( + :Instance, + %{uuid: circuit.id}, + :Specification, + %{uuid: circuit.specification_id}, + :SPECIFIED_BY, + :outgoing + ) + + # check characteristic resource enrichment and node relationships + assert is_list(circuit.characteristics) + assert length(circuit.characteristics) == 1 + + Enum.each(circuit.characteristics, fn characteristic -> + assert is_struct(characteristic, Characteristic) + + assert AshNeo4j.Neo4jHelper.nodes_relate_how?( + :Instance, + %{uuid: circuit.id}, + :Characteristic, + %{uuid: characteristic.id}, + :HAS, + :outgoing + ) + end) + + encoding = Jason.encode!(circuit) |> Diffo.Util.summarise_dates() + + assert encoding == + ~s({"id":"#{circuit.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{circuit.id}","category":"Network Resource","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"nbn_ethernet","value":{}}]}) + end + + test "define nbn_ethernet circuit" do + {:ok, circuit} = Nbn.build_nbn_ethernet(%{}) + + updates = [ + nbn_ethernet: [circuit_id: "NBN-CID-123456", speed: 1000, technology: :FTTP] + ] + + {:ok, circuit} = Nbn.define_nbn_ethernet(circuit, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [nbn_ethernet: [circuit_id: "NBN-CID-123456", speed: 1000, technology: :FTTP]], + circuit + ) + end + end + + describe "build uni" do + test "create a uni" do + {:ok, uni} = Nbn.build_uni(%{}) + + assert is_struct(uni, Uni) + refute is_nil(uni.specification_id) + assert is_struct(uni.specification, Specification) + assert is_list(uni.characteristics) + assert length(uni.characteristics) == 1 + end + + test "define uni" do + {:ok, uni} = Nbn.build_uni(%{}) + + updates = [ + uni: [vlan_id: 101, bandwidth_profile: "TC4", technology: :FTTP] + ] + + {:ok, uni} = Nbn.define_uni(uni, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [uni: [vlan_id: 101, bandwidth_profile: "TC4", technology: :FTTP]], + uni + ) + end + end + + describe "build avc" do + test "create an avc" do + {:ok, avc} = Nbn.build_avc(%{}) + + assert is_struct(avc, Avc) + refute is_nil(avc.specification_id) + assert is_struct(avc.specification, Specification) + assert is_list(avc.characteristics) + assert length(avc.characteristics) == 1 + end + + test "define avc" do + {:ok, avc} = Nbn.build_avc(%{}) + + updates = [ + avc: [cir: 100, pir: 1000] + ] + + {:ok, avc} = Nbn.define_avc(avc, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [avc: [cir: 100, pir: 1000]], + avc + ) + end + end + + describe "build ntd" do + test "create an ntd" do + {:ok, ntd} = Nbn.build_ntd(%{}) + + assert is_struct(ntd, Ntd) + refute is_nil(ntd.specification_id) + end + + test "define ntd" do + {:ok, ntd} = Nbn.build_ntd(%{}) + + updates = [ + ntd: [model: "Arris CM8200", serial_number: "SN-ABC-123456", technology: :HFC] + ] + + {:ok, ntd} = Nbn.define_ntd(ntd, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [ntd: [model: "Arris CM8200", serial_number: "SN-ABC-123456", technology: :HFC]], + ntd + ) + end + end + + describe "build cvc" do + test "create a cvc" do + {:ok, cvc} = Nbn.build_cvc(%{}) + + assert is_struct(cvc, Cvc) + refute is_nil(cvc.specification_id) + end + + test "define cvc" do + {:ok, cvc} = Nbn.build_cvc(%{}) + + updates = [ + cvc: [cvc_id: "CVC-POI-SYD-001", bandwidth: 10000] + ] + + {:ok, cvc} = Nbn.define_cvc(cvc, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [cvc: [cvc_id: "CVC-POI-SYD-001", bandwidth: 10000]], + cvc + ) + end + end + + describe "build nni_group" do + test "create an nni_group" do + {:ok, nni_group} = Nbn.build_nni_group(%{}) + + assert is_struct(nni_group, NniGroup) + refute is_nil(nni_group.specification_id) + end + + test "define nni_group" do + {:ok, nni_group} = Nbn.build_nni_group(%{}) + + updates = [ + nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"] + ] + + {:ok, nni_group} = + Nbn.define_nni_group(nni_group, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"]], + nni_group + ) + end + end + + describe "build nni" do + @tag debug2: true + test "create an nni" do + {:ok, nni} = Nbn.build_nni(%{}) + + assert is_struct(nni, Nni) + refute is_nil(nni.specification_id) + end + + test "define nni" do + {:ok, nni} = Nbn.build_nni(%{}) + + updates = [ + nni: [port_id: "SYD-01-ETH-001", capacity: 10, technology: :Ethernet] + ] + + {:ok, nni} = Nbn.define_nni(nni, %{characteristic_value_updates: updates}) + + Characteristics.check_values( + [nni: [port_id: "SYD-01-ETH-001", capacity: 10, technology: :Ethernet]], + nni + ) + end + end +end From d51421e3ff69b629d8a5f3c0de250ef1635e30fc Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Tue, 24 Mar 2026 13:02:43 +1030 Subject: [PATCH 2/6] improved identifiers --- lib/access/util.ex | 6 +- lib/nbn/nbn.ex | 1 + lib/nbn/resources/avc.ex | 8 ++- .../{nbn_ethernet_value.ex => pri_value.ex} | 16 +++-- lib/nbn/resources/cvc.ex | 16 ++++- lib/nbn/resources/nbn_ethernet.ex | 63 ++++++++++++++--- lib/nbn/resources/nni.ex | 8 ++- lib/nbn/resources/nni_group.ex | 2 +- lib/nbn/resources/ntd.ex | 8 ++- lib/nbn/resources/uni.ex | 14 ++-- lib/nbn/util.ex | 41 +++++++++++ test/access/shelf_test.exs | 1 - test/diffo_example_test.exs | 10 +++ test/nbn/nbn_ethernet_test.exs | 69 ++++++++++++------- 14 files changed, 214 insertions(+), 49 deletions(-) rename lib/nbn/resources/characteristic_values/{nbn_ethernet_value.ex => pri_value.ex} (55%) create mode 100644 lib/nbn/util.ex create mode 100644 test/diffo_example_test.exs diff --git a/lib/access/util.ex b/lib/access/util.ex index 4f21dee..1b92c74 100644 --- a/lib/access/util.ex +++ b/lib/access/util.ex @@ -13,9 +13,13 @@ defmodule DiffoExample.Access.Util do alias Diffo.Provider.Assignment - def assignments(instance, type) do + @doc """ + Lists things that are assigned_to an Instance, as Assignments + """ + def assignments(instance, type) when is_struct(instance, Ash.Resource) and is_atom(type) do Enum.reduce(instance.reverse_relationships, [], fn reverse_relationship, acc -> IO.inspect(reverse_relationship, label: :reverse_relationship) + case reverse_relationship.type do :assignedTo -> characteristic = diff --git a/lib/nbn/nbn.ex b/lib/nbn/nbn.ex index 8290ac6..0663e89 100644 --- a/lib/nbn/nbn.ex +++ b/lib/nbn/nbn.ex @@ -33,6 +33,7 @@ defmodule DiffoExample.Nbn do define :build_nbn_ethernet, action: :build define :define_nbn_ethernet, action: :define define :relate_nbn_ethernet, action: :relate + define :mine_nbn_ethernet, action: :mine end resource Uni do diff --git a/lib/nbn/resources/avc.ex b/lib/nbn/resources/avc.ex index 8213d4b..e28923f 100644 --- a/lib/nbn/resources/avc.ex +++ b/lib/nbn/resources/avc.ex @@ -43,7 +43,7 @@ defmodule DiffoExample.Nbn.Avc do actions do create :build do description "creates a new AVC resource instance" - accept [:id, :name, :type, :which] + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -51,6 +51,8 @@ defmodule DiffoExample.Nbn.Avc do argument :places, {:array, :struct} argument :parties, {:array, :struct} + change set_attribute(:name, &DiffoExample.Nbn.Avc.identifier/0) + change set_attribute(:type, :resource) change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) @@ -85,4 +87,8 @@ defmodule DiffoExample.Nbn.Avc do end) end end + + def identifier() do + DiffoExample.Nbn.Util.identifier("AVC") + end end diff --git a/lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex b/lib/nbn/resources/characteristic_values/pri_value.ex similarity index 55% rename from lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex rename to lib/nbn/resources/characteristic_values/pri_value.ex index 57e0d27..2a42e56 100644 --- a/lib/nbn/resources/characteristic_values/nbn_ethernet_value.ex +++ b/lib/nbn/resources/characteristic_values/pri_value.ex @@ -2,16 +2,18 @@ # # SPDX-License-Identifier: MIT -defmodule DiffoExample.Nbn.NbnEthernetValue do +defmodule DiffoExample.Nbn.PriValue do @moduledoc """ Diffo - TMF Service and Resource Management with a difference - NbnEthernetValue - AshTyped Struct for NBN Ethernet Circuit Characteristic Value + NbnEthernetValue - AshTyped Struct for NBN Ethernet Access Characteristic Value """ use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + @technologies [:FTTP, :FTTN, :FTTB, :FTTC, :HFC, :FixedWireless, :Satellite] + jason do - pick [:circuit_id, :speed, :technology] + pick [:avcid, :uniid, :speed, :technology] compact(true) end @@ -20,12 +22,16 @@ defmodule DiffoExample.Nbn.NbnEthernetValue do end typed_struct do - field :circuit_id, :string, description: "the unique NBN circuit identifier" + field :avcid, :string, description: "the avcid from the owne Avc Resource" + + field :uniid, :string, description: "the uniid from the owned Uni Resource" field :speed, :integer, description: "the circuit download speed in Mbps" field :technology, :atom, - description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + description: "the access technology", + constraints: [one_of: @technologies], + default: :FTTP end defimpl String.Chars do diff --git a/lib/nbn/resources/cvc.ex b/lib/nbn/resources/cvc.ex index ef17ccd..f5bcee6 100644 --- a/lib/nbn/resources/cvc.ex +++ b/lib/nbn/resources/cvc.ex @@ -34,7 +34,9 @@ defmodule DiffoExample.Nbn.Cvc do id "d4e5f6a7-8b9c-4d0e-bf1a-3b4c5d6e7f8a" name "cvc" type :resourceSpecification + description "A Connectivity Virtual Circuit Resource Instance that aggregates AVCs and terminates at an NNI Group" + category "Network Resource" end @@ -46,7 +48,7 @@ defmodule DiffoExample.Nbn.Cvc do actions do create :build do description "creates a new CVC resource instance" - accept [:id, :name, :type, :which] + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -54,6 +56,8 @@ defmodule DiffoExample.Nbn.Cvc do argument :places, {:array, :struct} argument :parties, {:array, :struct} + change set_attribute(:name, &DiffoExample.Nbn.Cvc.identifier/0) + change set_attribute(:type, :resource) change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) @@ -99,4 +103,14 @@ defmodule DiffoExample.Nbn.Cvc do end) end end + + attributes do + attribute :cvcid, :string do + default &DiffoExample.Nbn.Cvc.identifier/0 + end + end + + def identifier() do + DiffoExample.Nbn.Util.identifier("CVC") + end end diff --git a/lib/nbn/resources/nbn_ethernet.ex b/lib/nbn/resources/nbn_ethernet.ex index aee6dbf..44a82f4 100644 --- a/lib/nbn/resources/nbn_ethernet.ex +++ b/lib/nbn/resources/nbn_ethernet.ex @@ -6,10 +6,10 @@ defmodule DiffoExample.Nbn.NbnEthernet do @moduledoc """ Diffo - TMF Service and Resource Management with a difference - NbnEthernet - NBN Ethernet Circuit Resource Instance + NbnEthernet - NBN Ethernet access Resource Instance - An NBN Ethernet circuit comprising a dedicated UNI and AVC resource. - The circuit is related to its UNI, which in turn is aggregated by a CVC + An NBN Ethernet access comprising a dedicated UNI and AVC resource. + The access is related to its UNI, which in turn is aggregated by a CVC that terminates at an NNI Group. """ @@ -25,7 +25,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do domain: Nbn resource do - description "An Ash Resource representing an NBN Ethernet Circuit" + description "An Ash Resource representing an NBN Ethernet access" plural_name :NbnEthernets end @@ -33,18 +33,22 @@ defmodule DiffoExample.Nbn.NbnEthernet do id "f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c" name "nbnEthernet" type :resourceSpecification - description "An NBN Ethernet Circuit comprising a dedicated UNI and AVC" + description "An NBN Ethernet access comprising a dedicated UNI and AVC" category "Network Resource" end characteristics do - characteristic :nbn_ethernet, DiffoExample.Nbn.NbnEthernetValue + characteristic :pri, DiffoExample.Nbn.PriValue + # values do + # value :uniid, DiffoExample.Nbn.Uni, :owns, :name + # value :avcid, DiffoExample.Nbn.Avc, :owns, :name + # end end actions do create :build do - description "creates a new NBN Ethernet circuit resource instance" - accept [:id, :name, :type, :which] + description "creates a new NBN Ethernet access resource instance" + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -52,6 +56,8 @@ defmodule DiffoExample.Nbn.NbnEthernet do argument :places, {:array, :struct} argument :parties, {:array, :struct} + change set_attribute(:name, &DiffoExample.Nbn.NbnEthernet.identifier/0) + change set_attribute(:type, :resource) change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) @@ -65,7 +71,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do end update :define do - description "defines the NBN Ethernet circuit" + description "defines the NBN Ethernet access" argument :characteristic_value_updates, {:array, :term} change after_action(fn changeset, result, _context -> @@ -76,7 +82,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do end update :relate do - description "relates the NBN Ethernet circuit with other instances (e.g. UNI)" + description "relates the NBN Ethernet access with other instances (e.g. UNI)" argument :relationships, {:array, :struct} change after_action(fn changeset, result, _context -> @@ -85,5 +91,42 @@ defmodule DiffoExample.Nbn.NbnEthernet do do: {:ok, result} end) end + + update :mine do + description "updates the NBN Ethernet access with data mined from related instances" + argument :characteristic_value_updates, {:array, :term} + + change before_action(fn changeset, context -> + DiffoExample.Nbn.NbnEthernet.mine_related(changeset, context) + end) + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_nbn_ethernet_by_id(result.id), + do: {:ok, result} + end) + end + end + + def identifier() do + DiffoExample.Nbn.Util.identifier("PRI") + end + + # mines related resource to characteristics + def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do + forward_relationships = Ash.Changeset.get_attribute(changeset, :forward_relationships) + + pri_updates = + Enum.into(forward_relationships, [], fn forward_relationship -> + {:ok, related} = Diffo.Provider.get_instance_by_id(forward_relationship.target_id) + {alias_to_id(forward_relationship.alias), related.name} + end) + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, pri: pri_updates) + end + + defp alias_to_id(alias) when is_atom(alias) do + (Atom.to_string(alias) <> "id") + |> String.to_atom() end end diff --git a/lib/nbn/resources/nni.ex b/lib/nbn/resources/nni.ex index 81e070a..92785a5 100644 --- a/lib/nbn/resources/nni.ex +++ b/lib/nbn/resources/nni.ex @@ -44,7 +44,7 @@ defmodule DiffoExample.Nbn.Nni do actions do create :build do description "creates a new NNI resource instance" - accept [:id, :name, :type, :which] + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -54,6 +54,8 @@ defmodule DiffoExample.Nbn.Nni do change set_attribute(:type, :resource) + change set_attribute(:name, &DiffoExample.Nbn.Nni.identifier/0) + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) change after_action(fn changeset, result, _context -> @@ -85,5 +87,9 @@ defmodule DiffoExample.Nbn.Nni do do: {:ok, result} end) end + + def identifier() do + DiffoExample.Nbn.Util.identifier("NNI") + end end end diff --git a/lib/nbn/resources/nni_group.ex b/lib/nbn/resources/nni_group.ex index 630fdc5..d12080c 100644 --- a/lib/nbn/resources/nni_group.ex +++ b/lib/nbn/resources/nni_group.ex @@ -46,7 +46,7 @@ defmodule DiffoExample.Nbn.NniGroup do actions do create :build do description "creates a new NNI Group resource instance" - accept [:id, :name, :type, :which] + accept [:id, :name, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false diff --git a/lib/nbn/resources/ntd.ex b/lib/nbn/resources/ntd.ex index 6ef9617..d2f2036 100644 --- a/lib/nbn/resources/ntd.ex +++ b/lib/nbn/resources/ntd.ex @@ -43,7 +43,7 @@ defmodule DiffoExample.Nbn.Ntd do actions do create :build do description "creates a new NTD resource instance" - accept [:id, :name, :type, :which] + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -53,6 +53,8 @@ defmodule DiffoExample.Nbn.Ntd do change set_attribute(:type, :resource) + change set_attribute(:name, &DiffoExample.Nbn.Ntd.identifier/0) + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) change after_action(fn changeset, result, _context -> @@ -85,4 +87,8 @@ defmodule DiffoExample.Nbn.Ntd do end) end end + + def identifier() do + DiffoExample.Nbn.Util.identifier("NTD") + end end diff --git a/lib/nbn/resources/uni.ex b/lib/nbn/resources/uni.ex index 2408411..b9bf9dd 100644 --- a/lib/nbn/resources/uni.ex +++ b/lib/nbn/resources/uni.ex @@ -9,7 +9,7 @@ defmodule DiffoExample.Nbn.Uni do Uni - User Network Interface Resource Instance A UNI is the physical/logical interface at the customer premises. It is - related to an NTD resource and to its parent NBN Ethernet circuit. + related to an NTD resource and to its parent NBN Ethernet access. It is related to an AVC resource, which is in turn aggregated by a CVC. """ @@ -33,7 +33,7 @@ defmodule DiffoExample.Nbn.Uni do id "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d" name "uni" type :resourceSpecification - description "A UNI Resource Instance related to an NTD and an NBN Ethernet circuit" + description "A UNI Resource Instance related to an NTD and an NBN Ethernet access" category "Network Resource" end @@ -44,7 +44,7 @@ defmodule DiffoExample.Nbn.Uni do actions do create :build do description "creates a new UNI resource instance" - accept [:id, :name, :type, :which] + accept [:id, :which] argument :specified_by, :uuid, public?: false argument :relationships, {:array, :struct} argument :features, {:array, :uuid}, public?: false @@ -54,6 +54,8 @@ defmodule DiffoExample.Nbn.Uni do change set_attribute(:type, :resource) + change set_attribute(:name, &DiffoExample.Nbn.Uni.identifier/0) + change before_action(fn changeset, _context -> ActionHelper.build_before(changeset) end) change after_action(fn changeset, result, _context -> @@ -76,7 +78,7 @@ defmodule DiffoExample.Nbn.Uni do end update :relate do - description "relates the UNI with other instances (e.g. NTD, NBN Ethernet circuit)" + description "relates the UNI with other instances (e.g. NTD, NBN Ethernet access)" argument :relationships, {:array, :struct} change after_action(fn changeset, result, _context -> @@ -86,4 +88,8 @@ defmodule DiffoExample.Nbn.Uni do end) end end + + def identifier() do + DiffoExample.Nbn.Util.identifier("UNI") + end end diff --git a/lib/nbn/util.ex b/lib/nbn/util.ex new file mode 100644 index 0000000..ea68e1d --- /dev/null +++ b/lib/nbn/util.ex @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExample.Nbn.Util do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Util - various utilities for NBN domain + + """ + + @doc """ + Generates a new random NBN identifier with the prefix + + ## Examples + iex> identifier = DiffoExample.Nbn.Util.identifier("AVC") + iex> DiffoExample.Nbn.Util.identifier?(identifier) + true + + """ + def identifier(prefix) when is_binary(prefix) and byte_size(prefix) == 3 do + prefix <> + (:rand.uniform(000_999_999_999) + |> Integer.to_string() + |> String.pad_leading(12, "0")) + end + + @doc """ + Returns whether the identifier is a valid NBN identifier + + ## Examples + iex> DiffoExample.Nbn.Util.identifier?("AVC120123456789") + true + iex> DiffoExample.Nbn.Util.identifier?("avc120123456789") + false + """ + def identifier?(identifier) when is_binary(identifier) do + Regex.match?(~r/[A-Z]{3}\d{12}/, identifier) + end +end diff --git a/test/access/shelf_test.exs b/test/access/shelf_test.exs index f49f692..f3bc30e 100644 --- a/test/access/shelf_test.exs +++ b/test/access/shelf_test.exs @@ -122,7 +122,6 @@ defmodule DiffoExample.Access.ShelfTest do ~s({\"id\":\"#{shelf.id}",\"href\":\"resourceInventoryManagement/v4/resource/shelf/#{shelf.id}",\"category\":\"Network Resource\",\"resourceSpecification\":{\"id\":\"ef016d85-9dbd-429c-84da-1df56cc7dda5\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/ef016d85-9dbd-429c-84da-1df56cc7dda5\",\"name\":\"shelf\",\"version\":\"v1.0.0\"},\"resourceRelationship\":[{\"type\":\"contains\",\"resource\":{\"id\":\"#{card0.id}\",\"href\":\"resourceInventoryManagement/v4/resource/card/#{card0.id}\"}},{\"type\":\"contains\",\"resource\":{\"id\":\"#{card1.id}\",\"href\":\"resourceInventoryManagement/v4/resource/card/#{card1.id}\"}},{\"type\":\"contains\",\"resource\":{\"id\":\"#{card2.id}\",\"href\":\"resourceInventoryManagement/v4/resource/card/#{card2.id}\"}},{\"type\":\"contains\",\"resource\":{\"id\":\"#{card3.id}\",\"href\":\"resourceInventoryManagement/v4/resource/card/#{card3.id}\"}}],\"resourceCharacteristic\":[{\"name\":\"shelf\",\"value\":{\"name\":\"QDONC-1001\",\"family\":\"ISAM\",\"model\":\"ISAM7330\",\"technology\":\"DSLAM\"}},{\"name\":\"slots\",\"value\":{\"first\":1,\"last\":10,\"free\":10,\"type\":\"LineCard\",\"algorithm\":\"lowest\"}}],\"place\":[{\"id\":\"DONC-0001\",\"href\":\"place/telco/DONC-0001\",\"name\":\"esaId\",\"role\":\"ServingArea\",\"@referredType\":\"GeographicLocation\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"Access\",\"name\":\"organizationId\",\"role\":\"Provider\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) end - @tag debug: true test "auto assign line cards" do places = [create_esa_place()] parties = [create_provider_party()] diff --git a/test/diffo_example_test.exs b/test/diffo_example_test.exs new file mode 100644 index 0000000..d500bb4 --- /dev/null +++ b/test/diffo_example_test.exs @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT + +defmodule DiffoExampleTest do + @moduledoc false + use ExUnit.Case + doctest DiffoExample.Access.Util + doctest DiffoExample.Nbn.Util +end diff --git a/test/nbn/nbn_ethernet_test.exs b/test/nbn/nbn_ethernet_test.exs index 5b3e0d0..8f3a24a 100644 --- a/test/nbn/nbn_ethernet_test.exs +++ b/test/nbn/nbn_ethernet_test.exs @@ -16,47 +16,49 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do alias DiffoExample.Nbn.NniGroup alias DiffoExample.Nbn.Nni alias DiffoExample.Test.Characteristics + alias Diffo.Provider.Instance.Relationship setup_all do AshNeo4j.BoltyHelper.start() end setup do - on_exit(fn -> - AshNeo4j.Neo4jHelper.delete_all() - end) + # on_exit(fn -> + # AshNeo4j.Neo4jHelper.delete_all() + # end) + :ok end describe "build nbn_ethernet" do - test "create an nbn_ethernet circuit" do - {:ok, circuit} = Nbn.build_nbn_ethernet(%{}) + test "create an nbn_ethernet access" do + {:ok, access} = Nbn.build_nbn_ethernet(%{}) # check the instance is an NbnEthernet - assert is_struct(circuit, NbnEthernet) + assert is_struct(access, NbnEthernet) # check specification resource enrichment and node relationship - refute is_nil(circuit.specification_id) - assert is_struct(circuit.specification, Specification) + refute is_nil(access.specification_id) + assert is_struct(access.specification, Specification) assert AshNeo4j.Neo4jHelper.nodes_relate_how?( :Instance, - %{uuid: circuit.id}, + %{uuid: access.id}, :Specification, - %{uuid: circuit.specification_id}, + %{uuid: access.specification_id}, :SPECIFIED_BY, :outgoing ) # check characteristic resource enrichment and node relationships - assert is_list(circuit.characteristics) - assert length(circuit.characteristics) == 1 + assert is_list(access.characteristics) + assert length(access.characteristics) == 1 - Enum.each(circuit.characteristics, fn characteristic -> + Enum.each(access.characteristics, fn characteristic -> assert is_struct(characteristic, Characteristic) assert AshNeo4j.Neo4jHelper.nodes_relate_how?( :Instance, - %{uuid: circuit.id}, + %{uuid: access.id}, :Characteristic, %{uuid: characteristic.id}, :HAS, @@ -64,26 +66,48 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do ) end) - encoding = Jason.encode!(circuit) |> Diffo.Util.summarise_dates() + encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({"id":"#{circuit.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{circuit.id}","category":"Network Resource","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"nbn_ethernet","value":{}}]}) + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource",\"name\":\"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"pri","value":{\"technology\":\"FTTP\"}}]}) end - test "define nbn_ethernet circuit" do - {:ok, circuit} = Nbn.build_nbn_ethernet(%{}) + test "define nbn_ethernet access" do + {:ok, access} = Nbn.build_nbn_ethernet(%{}) updates = [ - nbn_ethernet: [circuit_id: "NBN-CID-123456", speed: 1000, technology: :FTTP] + pri: [avcid: "AVC000910202941", uniid: "UNI000302814545", speed: 1000, technology: :FTTP] ] - {:ok, circuit} = Nbn.define_nbn_ethernet(circuit, %{characteristic_value_updates: updates}) + {:ok, access} = Nbn.define_nbn_ethernet(access, %{characteristic_value_updates: updates}) Characteristics.check_values( - [nbn_ethernet: [circuit_id: "NBN-CID-123456", speed: 1000, technology: :FTTP]], - circuit + [pri: [avcid: "AVC000910202941", uniid: "UNI000302814545", speed: 1000, technology: :FTTP]], + access ) end + + test "relate nbn_ethernet" do + {:ok, access} = Nbn.build_nbn_ethernet(%{}) + + {:ok, avc} = Nbn.build_avc(%{}) + + {:ok, uni} = Nbn.build_uni(%{}) + + relationships = [ + %Relationship{id: avc.id, direction: :forward, type: :owns, alias: :avc}, + %Relationship{id: uni.id, direction: :forward, type: :owns, alias: :uni} + ] + + {:ok, access} = Nbn.relate_nbn_ethernet(access, %{relationships: relationships}) + + {:ok, access} = Nbn.mine_nbn_ethernet(access) + + encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() + + assert encoding == + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource","name":"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceRelationship":[{"alias":"avc","type":"owns","resource":{"id":"#{avc.id}","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"}},{"alias":"uni","type":"owns","resource":{"id\":"#{uni.id}","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}}],"supportingResource":[{"id":"avc","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"},{"id\":"uni","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}],"resourceCharacteristic":[{"name":"pri","value":{"avcid":"#{avc.name}","uniid":"#{uni.name}","technology":"FTTP"}}]}) + end end describe "build uni" do @@ -214,7 +238,6 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do end describe "build nni" do - @tag debug2: true test "create an nni" do {:ok, nni} = Nbn.build_nni(%{}) From 87680027be1a307ec2994c8671767016e73354f2 Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Thu, 26 Mar 2026 23:55:04 +1030 Subject: [PATCH 3/6] derive speeds from ntd technology and avc bandwidth_profile --- lib/access/util.ex | 2 - lib/nbn/nbn.ex | 4 + lib/nbn/resources/avc.ex | 25 +++ .../characteristic_values/avc_value.ex | 10 +- .../characteristic_values/cvc_value.ex | 6 +- .../characteristic_values/ntd_value.ex | 5 +- .../characteristic_values/pri_value.ex | 19 +- .../characteristic_values/uni_value.ex | 13 +- lib/nbn/resources/cvc.ex | 34 +++- lib/nbn/resources/nbn_ethernet.ex | 24 ++- lib/nbn/resources/nni_group.ex | 5 +- lib/nbn/resources/ntd.ex | 16 +- lib/nbn/resources/types/bandwidth_profile.ex | 23 +++ lib/nbn/resources/types/speeds.ex | 49 +++++ lib/nbn/resources/types/technology.ex | 21 +++ lib/nbn/resources/uni.ex | 29 +++ lib/nbn/util.ex | 127 ++++++++++++- test/nbn/nbn_ethernet_test.exs | 170 ++++++++++++++++-- test/support/characteristics.ex | 3 + 19 files changed, 522 insertions(+), 63 deletions(-) create mode 100644 lib/nbn/resources/types/bandwidth_profile.ex create mode 100644 lib/nbn/resources/types/speeds.ex create mode 100644 lib/nbn/resources/types/technology.ex diff --git a/lib/access/util.ex b/lib/access/util.ex index 1b92c74..d8285a7 100644 --- a/lib/access/util.ex +++ b/lib/access/util.ex @@ -18,8 +18,6 @@ defmodule DiffoExample.Access.Util do """ def assignments(instance, type) when is_struct(instance, Ash.Resource) and is_atom(type) do Enum.reduce(instance.reverse_relationships, [], fn reverse_relationship, acc -> - IO.inspect(reverse_relationship, label: :reverse_relationship) - case reverse_relationship.type do :assignedTo -> characteristic = diff --git a/lib/nbn/nbn.ex b/lib/nbn/nbn.ex index 0663e89..3ae584e 100644 --- a/lib/nbn/nbn.ex +++ b/lib/nbn/nbn.ex @@ -41,6 +41,7 @@ defmodule DiffoExample.Nbn do define :build_uni, action: :build define :define_uni, action: :define define :relate_uni, action: :relate + define :mine_uni, action: :mine end resource Avc do @@ -48,12 +49,14 @@ defmodule DiffoExample.Nbn do define :build_avc, action: :build define :define_avc, action: :define define :relate_avc, action: :relate + define :mine_avc, action: :mine end resource Ntd do define :get_ntd_by_id, action: :read, get_by: :id define :build_ntd, action: :build define :define_ntd, action: :define + define :assign_port, action: :assign_port define :relate_ntd, action: :relate end @@ -63,6 +66,7 @@ defmodule DiffoExample.Nbn do define :define_cvc, action: :define define :assign_cvlan, action: :assign_cvlan define :relate_cvc, action: :relate + define :mine_cvc, action: :mine end resource NniGroup do diff --git a/lib/nbn/resources/avc.ex b/lib/nbn/resources/avc.ex index e28923f..5f36f8a 100644 --- a/lib/nbn/resources/avc.ex +++ b/lib/nbn/resources/avc.ex @@ -38,6 +38,7 @@ defmodule DiffoExample.Nbn.Avc do characteristics do characteristic :avc, DiffoExample.Nbn.AvcValue + characteristic :cvc, DiffoExample.Nbn.CvcValue end actions do @@ -86,9 +87,33 @@ defmodule DiffoExample.Nbn.Avc do do: {:ok, result} end) end + + update :mine do + description "updates the AVC with data mined from related instances" + argument :characteristic_value_updates, {:array, :term} + + change before_action(fn changeset, context -> + DiffoExample.Nbn.Avc.mine_related(changeset, context) + end) + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_avc_by_id(result.id), + do: {:ok, result} + end) + end end def identifier() do DiffoExample.Nbn.Util.identifier("AVC") end + + # mines related resource to characteristics + def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do + reverse_relationships = Ash.Changeset.get_attribute(changeset, :reverse_relationships) + + cvlan = {:cvlan, hd(hd(reverse_relationships).characteristics).value} + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, avc: [cvlan]) + end end diff --git a/lib/nbn/resources/characteristic_values/avc_value.ex b/lib/nbn/resources/characteristic_values/avc_value.ex index f24266a..201d484 100644 --- a/lib/nbn/resources/characteristic_values/avc_value.ex +++ b/lib/nbn/resources/characteristic_values/avc_value.ex @@ -10,19 +10,21 @@ defmodule DiffoExample.Nbn.AvcValue do """ use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + alias DiffoExample.Nbn.BandwidthProfile + jason do - pick [:cir, :pir] + pick [:cvlan, :bandwidth_profile] compact(true) end outstanding do - expect [:cir, :pir] + expect [:cvlan, :bandwidth_profile] end typed_struct do - field :cir, :integer, description: "Committed Information Rate in Mbps" + field :cvlan, :string, description: "the cvlan of the AVC, assigned by the related CVC" - field :pir, :integer, description: "Peak Information Rate in Mbps" + field :bandwidth_profile, BandwidthProfile, description: "the bandwidth profile of the AVC" end defimpl String.Chars do diff --git a/lib/nbn/resources/characteristic_values/cvc_value.ex b/lib/nbn/resources/characteristic_values/cvc_value.ex index dc68a2e..e13e3d6 100644 --- a/lib/nbn/resources/characteristic_values/cvc_value.ex +++ b/lib/nbn/resources/characteristic_values/cvc_value.ex @@ -11,16 +11,16 @@ defmodule DiffoExample.Nbn.CvcValue do use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] jason do - pick [:cvc_id, :bandwidth] + pick [:svlan, :bandwidth] compact(true) end outstanding do - expect [:cvc_id, :bandwidth] + expect [:svlan, :bandwidth] end typed_struct do - field :cvc_id, :string, description: "the unique CVC identifier" + field :svlan, :string, description: "the svlan of the CVC, assigned by the related NNI Group" field :bandwidth, :integer, description: "total CVC bandwidth in Mbps" end diff --git a/lib/nbn/resources/characteristic_values/ntd_value.ex b/lib/nbn/resources/characteristic_values/ntd_value.ex index 85ee77b..fa591a0 100644 --- a/lib/nbn/resources/characteristic_values/ntd_value.ex +++ b/lib/nbn/resources/characteristic_values/ntd_value.ex @@ -10,6 +10,8 @@ defmodule DiffoExample.Nbn.NtdValue do """ use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + alias DiffoExample.Nbn.Technology + jason do pick [:model, :serial_number, :technology] compact(true) @@ -24,8 +26,7 @@ defmodule DiffoExample.Nbn.NtdValue do field :serial_number, :string, description: "the NTD serial number" - field :technology, :atom, - description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + field :technology, Technology, description: "the access technology type", default: Technology.default end defimpl String.Chars do diff --git a/lib/nbn/resources/characteristic_values/pri_value.ex b/lib/nbn/resources/characteristic_values/pri_value.ex index 2a42e56..efe9a09 100644 --- a/lib/nbn/resources/characteristic_values/pri_value.ex +++ b/lib/nbn/resources/characteristic_values/pri_value.ex @@ -10,28 +10,29 @@ defmodule DiffoExample.Nbn.PriValue do """ use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] - @technologies [:FTTP, :FTTN, :FTTB, :FTTC, :HFC, :FixedWireless, :Satellite] + alias DiffoExample.Nbn.Technology + alias DiffoExample.Nbn.BandwidthProfile + alias DiffoExample.Nbn.Speeds jason do - pick [:avcid, :uniid, :speed, :technology] + pick [:avcid, :uniid, :technology, :bandwidth_profile, :speeds] compact(true) end outstanding do - expect [:circuit_id, :speed] + expect [:avcid, :uniid, :technology, :bandwidth_profile, :speeds] end typed_struct do - field :avcid, :string, description: "the avcid from the owne Avc Resource" + field :avcid, :string, description: "the avcid from the owned Avc Resource" field :uniid, :string, description: "the uniid from the owned Uni Resource" - field :speed, :integer, description: "the circuit download speed in Mbps" + field :technology, Technology, description: "the technology type" - field :technology, :atom, - description: "the access technology", - constraints: [one_of: @technologies], - default: :FTTP + field :bandwidth_profile, BandwidthProfile, description: "the bandwidth profile" + + field :speeds, Speeds, description: "the downstream and upstream speeds in Mbps" end defimpl String.Chars do diff --git a/lib/nbn/resources/characteristic_values/uni_value.ex b/lib/nbn/resources/characteristic_values/uni_value.ex index 56e8870..8bd5670 100644 --- a/lib/nbn/resources/characteristic_values/uni_value.ex +++ b/lib/nbn/resources/characteristic_values/uni_value.ex @@ -10,22 +10,23 @@ defmodule DiffoExample.Nbn.UniValue do """ use Ash.TypedStruct, extensions: [AshJason.TypedStruct, AshOutstanding.TypedStruct] + alias DiffoExample.Nbn.Technology + jason do - pick [:vlan_id, :bandwidth_profile, :technology] + pick [:port, :encapsulation, :technology] compact(true) end outstanding do - expect [:vlan_id, :technology] + expect [:port, :encapsulation, :technology] end typed_struct do - field :vlan_id, :integer, description: "the VLAN ID for the UNI" + field :port, :integer, description: "the port of the UNI, assigned by the related NTD" - field :bandwidth_profile, :string, description: "the bandwidth profile name for the UNI" + field :encapsulation, :string, description: "the encapsulation of the UNI" - field :technology, :atom, - description: "the access technology (:FTTP, :FTTN, :HFC, :Fixed_Wireless)" + field :technology, Technology, description: "the access technology type" end defimpl String.Chars do diff --git a/lib/nbn/resources/cvc.ex b/lib/nbn/resources/cvc.ex index f5bcee6..9b21921 100644 --- a/lib/nbn/resources/cvc.ex +++ b/lib/nbn/resources/cvc.ex @@ -8,8 +8,8 @@ defmodule DiffoExample.Nbn.Cvc do Cvc - Connectivity Virtual Circuit Resource Instance - A CVC is the wholesale bandwidth product that aggregates one or more AVC - resources and terminates at an NNI Group resource. Each AVC has a related UNI. + A CVC is the wholesale bandwidth product that supports AVC and terminates at an NNI Group. + The CVC assigns cvlan to AVC. """ alias Diffo.Provider.BaseInstance @@ -42,7 +42,7 @@ defmodule DiffoExample.Nbn.Cvc do characteristics do characteristic :cvc, DiffoExample.Nbn.CvcValue - characteristic :cvlan_ids, Diffo.Provider.AssignableValue + characteristic :cvlans, Diffo.Provider.AssignableValue end actions do @@ -86,7 +86,7 @@ defmodule DiffoExample.Nbn.Cvc do argument :assignment, :struct, constraints: [instance_of: Assignment] change after_action(fn changeset, result, _context -> - with {:ok, result} <- Assigner.assign(result, changeset, :cvlan_ids, :cvlan_id), + with {:ok, result} <- Assigner.assign(result, changeset, :cvlans, :cvlan), {:ok, result} <- Nbn.get_cvc_by_id(result.id), do: {:ok, result} end) @@ -102,15 +102,33 @@ defmodule DiffoExample.Nbn.Cvc do do: {:ok, result} end) end - end - attributes do - attribute :cvcid, :string do - default &DiffoExample.Nbn.Cvc.identifier/0 + update :mine do + description "updates the CVC with data mined from related instances" + argument :characteristic_value_updates, {:array, :term} + + change before_action(fn changeset, context -> + DiffoExample.Nbn.Cvc.mine_related(changeset, context) + end) + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_cvc_by_id(result.id), + do: {:ok, result} + end) end end def identifier() do DiffoExample.Nbn.Util.identifier("CVC") end + + # mines related resource to characteristics + def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do + reverse_relationships = Ash.Changeset.get_attribute(changeset, :reverse_relationships) + + svlan = {:svlan, hd(hd(reverse_relationships).characteristics).value} + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, cvc: [svlan]) + end end diff --git a/lib/nbn/resources/nbn_ethernet.ex b/lib/nbn/resources/nbn_ethernet.ex index 44a82f4..ebb0413 100644 --- a/lib/nbn/resources/nbn_ethernet.ex +++ b/lib/nbn/resources/nbn_ethernet.ex @@ -8,9 +8,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do NbnEthernet - NBN Ethernet access Resource Instance - An NBN Ethernet access comprising a dedicated UNI and AVC resource. - The access is related to its UNI, which in turn is aggregated by a CVC - that terminates at an NNI Group. + An NBN Ethernet access comprises of dedicated UNI and AVC resources. """ alias Diffo.Provider.BaseInstance @@ -19,6 +17,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do alias Diffo.Provider.Instance.ActionHelper alias DiffoExample.Nbn + alias DiffoExample.Nbn.Util use Ash.Resource, fragments: [BaseInstance], @@ -117,12 +116,25 @@ defmodule DiffoExample.Nbn.NbnEthernet do forward_relationships = Ash.Changeset.get_attribute(changeset, :forward_relationships) pri_updates = - Enum.into(forward_relationships, [], fn forward_relationship -> + Enum.reduce(forward_relationships, [], fn forward_relationship, acc -> {:ok, related} = Diffo.Provider.get_instance_by_id(forward_relationship.target_id) - {alias_to_id(forward_relationship.alias), related.name} + related_name = {alias_to_id(forward_relationship.alias), related.name} + case forward_relationship.alias do + :uni -> + # extract technology from uni characteristic + [{:technology, Util.extract(related.characteristics, :uni, :technology)} | [ related_name | acc]] + :avc -> + # extract bandwidth_profile from avc characteristic + [{:bandwidth_profile, Util.extract(related.characteristics, :avc, :bandwidth_profile)} | [ related_name | acc]] + _ -> + [ related_name | acc] + end end) - Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, pri: pri_updates) + # calculate the speeds from the extracted technology and bandwidth_profile + speeds = {:speeds, Util.speeds(Keyword.get(pri_updates, :bandwidth_profile), Keyword.get(pri_updates, :technology))} + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, pri: [speeds | pri_updates]) end defp alias_to_id(alias) when is_atom(alias) do diff --git a/lib/nbn/resources/nni_group.ex b/lib/nbn/resources/nni_group.ex index d12080c..9629d5b 100644 --- a/lib/nbn/resources/nni_group.ex +++ b/lib/nbn/resources/nni_group.ex @@ -10,6 +10,7 @@ defmodule DiffoExample.Nbn.NniGroup do An NNI Group is the Point of Interconnect (PoI) grouping where a CVC terminates. It comprises multiple NNI resources. + The NNI Group assigns svlan to CVC. """ alias Diffo.Provider.BaseInstance @@ -40,7 +41,7 @@ defmodule DiffoExample.Nbn.NniGroup do characteristics do characteristic :nni_group, DiffoExample.Nbn.NniGroupValue - characteristic :svlan_ids, Diffo.Provider.AssignableValue + characteristic :svlans, Diffo.Provider.AssignableValue end actions do @@ -82,7 +83,7 @@ defmodule DiffoExample.Nbn.NniGroup do argument :assignment, :struct, constraints: [instance_of: Assignment] change after_action(fn changeset, result, _context -> - with {:ok, result} <- Assigner.assign(result, changeset, :svlan_ids, :svlan_id), + with {:ok, result} <- Assigner.assign(result, changeset, :svlans, :svlan), {:ok, result} <- Nbn.get_nni_group_by_id(result.id), do: {:ok, result} end) diff --git a/lib/nbn/resources/ntd.ex b/lib/nbn/resources/ntd.ex index d2f2036..b9fa3ea 100644 --- a/lib/nbn/resources/ntd.ex +++ b/lib/nbn/resources/ntd.ex @@ -9,13 +9,15 @@ defmodule DiffoExample.Nbn.Ntd do Ntd - Network Termination Device Resource Instance An NTD is the device installed at the customer premises that connects - the premises to the NBN network. It is related to a UNI resource. + the premises to the NBN network. The NTD can assign ports to UNI. """ alias Diffo.Provider.BaseInstance alias Diffo.Provider.Instance.Relationship alias Diffo.Provider.Instance.Characteristic alias Diffo.Provider.Instance.ActionHelper + alias Diffo.Provider.Assigner + alias Diffo.Provider.Assignment alias DiffoExample.Nbn @@ -38,6 +40,7 @@ defmodule DiffoExample.Nbn.Ntd do characteristics do characteristic :ntd, DiffoExample.Nbn.NtdValue + characteristic :ports, Diffo.Provider.AssignableValue end actions do @@ -76,6 +79,17 @@ defmodule DiffoExample.Nbn.Ntd do end) end + update :assign_port do + description "assigns a port from the NTD pool to a UNI" + argument :assignment, :struct, constraints: [instance_of: Assignment] + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Assigner.assign(result, changeset, :ports, :port), + {:ok, result} <- Nbn.get_ntd_by_id(result.id), + do: {:ok, result} + end) + end + update :relate do description "relates the NTD with other instances (e.g. UNI)" argument :relationships, {:array, :struct} diff --git a/lib/nbn/resources/types/bandwidth_profile.ex b/lib/nbn/resources/types/bandwidth_profile.ex new file mode 100644 index 0000000..fdc6848 --- /dev/null +++ b/lib/nbn/resources/types/bandwidth_profile.ex @@ -0,0 +1,23 @@ +defmodule DiffoExample.Nbn.BandwidthProfile do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + BandwidthProfile type for NBN domain + """ + + require Ash.Type.NewType + + use Ash.Type.NewType, + subtype_of: :atom, + constraints: [one_of: bandwidth_profiles()] + + def default do + :home_fast + end + + def bandwidth_profiles do + [:D12_U1, :D25_U5, :D25_U10, :D50_U20, :D100_U40, :D250_U100, :D500_U200, :D1000_U400, + :wireless_plus, :wireless_fast, :wireless_superfast, + :home_fast, :home_superfast, :home_ultrafast, :home_hyperfast] + end +end diff --git a/lib/nbn/resources/types/speeds.ex b/lib/nbn/resources/types/speeds.ex new file mode 100644 index 0000000..36fea9c --- /dev/null +++ b/lib/nbn/resources/types/speeds.ex @@ -0,0 +1,49 @@ +defmodule DiffoExample.Nbn.Speeds do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Speeds type for NBN domain + """ + + require Ash.Type.NewType + + use Ash.Type.NewType, + subtype_of: :tuple, + constraints: [ + fields: [downstream: [type: :integer], upstream: [type: :integer]] + ] + + def speeds do + [ + {12, 1}, {25, 5}, {25, 10}, {50, 20}, {100, 40}, {250, 100}, {500, 200}, {1000, 400}, + # :home_fast + {500, 50}, + # :home_superfast + {750, 50}, + # :home_ultrafast + {1000, 100}, + # :home_hyperfast + {2000, 100}, {2000, 200}, + # :wireless_plus, :wireless_fast, :wireless_superfast + {100, 20}, {250, 20}, {400, 40} + ] + end + + @impl true + def cast_input(value, _constraints) when is_tuple(value) do + if value in speeds() do + {:ok, value} + else + {:error, "invalid downstream and upstream speed combination"} + end + end + + def cast_input({_value, _constraints}), do: {:error, "value must be a tuple"} + + defimpl Jason.Encoder do + def encode(speeds, _opts) do + Jason.OrderedObject.new(downstream: speeds.downstream, upstream: speeds.upstream, units: "Mbps") + |> Jason.encode!() + end + end +end diff --git a/lib/nbn/resources/types/technology.ex b/lib/nbn/resources/types/technology.ex new file mode 100644 index 0000000..c9d6003 --- /dev/null +++ b/lib/nbn/resources/types/technology.ex @@ -0,0 +1,21 @@ +defmodule DiffoExample.Nbn.Technology do + @moduledoc """ + Diffo - TMF Service and Resource Management with a difference + + Technology type for NBN domain + """ + + require Ash.Type.NewType + + use Ash.Type.NewType, + subtype_of: :atom, + constraints: [one_of: technology()] + + def default do + :FTTP + end + + def technology do + [:FTTP, :FTTN, :FTTB, :FTTC, :HFC, :FixedWireless, :Satellite] + end +end diff --git a/lib/nbn/resources/uni.ex b/lib/nbn/resources/uni.ex index b9bf9dd..3966828 100644 --- a/lib/nbn/resources/uni.ex +++ b/lib/nbn/resources/uni.ex @@ -19,6 +19,7 @@ defmodule DiffoExample.Nbn.Uni do alias Diffo.Provider.Instance.ActionHelper alias DiffoExample.Nbn + alias DiffoExample.Nbn.Util use Ash.Resource, fragments: [BaseInstance], @@ -87,9 +88,37 @@ defmodule DiffoExample.Nbn.Uni do do: {:ok, result} end) end + + update :mine do + description "updates the UNI with data mined from related instances" + argument :characteristic_value_updates, {:array, :term} + + change before_action(fn changeset, context -> + DiffoExample.Nbn.Uni.mine_related(changeset, context) + end) + + change after_action(fn changeset, result, _context -> + with {:ok, result} <- Characteristic.update_values(result, changeset), + {:ok, result} <- Nbn.get_uni_by_id(result.id), + do: {:ok, result} + end) + end end def identifier() do DiffoExample.Nbn.Util.identifier("UNI") end + + # mines related resource to characteristics + def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do + reverse_relationships = Ash.Changeset.get_attribute(changeset, :reverse_relationships) + + ntd_relationship = hd(reverse_relationships) + + port = {:port, hd(ntd_relationship.characteristics).value} + {:ok, ntd} = Diffo.Provider.get_instance_by_id(ntd_relationship.source_id) + technology = {:technology, Util.extract(ntd.characteristics, :ntd, :technology)} + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, uni: [port, technology]) + end end diff --git a/lib/nbn/util.ex b/lib/nbn/util.ex index ea68e1d..345041f 100644 --- a/lib/nbn/util.ex +++ b/lib/nbn/util.ex @@ -7,9 +7,10 @@ defmodule DiffoExample.Nbn.Util do Diffo - TMF Service and Resource Management with a difference Util - various utilities for NBN domain - """ + alias DiffoExample.Nbn.Technology + @doc """ Generates a new random NBN identifier with the prefix @@ -38,4 +39,128 @@ defmodule DiffoExample.Nbn.Util do def identifier?(identifier) when is_binary(identifier) do Regex.match?(~r/[A-Z]{3}\d{12}/, identifier) end + + @doc """ + Extracts a field value from a named item in a list + + ## Examples + iex> DiffoExample.Nbn.Util.extract([%{name: :avc, value: %{cvlan: 1}}], :avc, :cvlan) + 1 + """ + def extract(items, name, field) when is_list(items) and is_atom(name) and is_atom(field) do + Enum.reduce_while(items, nil, fn item, acc -> + if name == item.name do + if item.value != nil do + {:halt, Map.get(item.value, field)} + else + {:halt, nil} + end + else + {:cont, acc} + end + end) + end + + @doc""" + Returns a tuple of maximum downstream and upstream speeds in Mbps + given the bandwidth_profile and technology, or :error + + ## Examples + iex> DiffoExample.Nbn.Util.speeds(:D12_U1, :Satellite) + {12, 1} + iex> DiffoExample.Nbn.Util.speeds(:home_fast, :FTTP) + {500, 50} + iex> DiffoExample.Nbn.Util.speeds(:home_hyperfast, :HFC) + {2000, 100} + iex> DiffoExample.Nbn.Util.speeds(:home_fast, :FixedWireless) + :error + """ + def speeds(:D12_U1, technology) when is_atom(technology) do + if technology in Technology.technology() do + {12, 1} + else + :error + end + end + + def speeds(:D25_U5, technology) when is_atom(technology) do + if technology in Technology.technology() do + {25, 5} + else + :error + end + end + + def speeds(:D25_U10, technology) when is_atom(technology) do + if technology in [:FTTP, :HFC, :FTTC] do + {25, 10} + else + :error + end + end + + def speeds(:D50_U20, technology) when is_atom(technology) do + if technology in [:FTTP, :HFC, :FTTC] do + {50, 20} + else + :error + end + end + + def speeds(bandwidth_profile, :FixedWireless) do + case bandwidth_profile do + :wireless_plus -> + {100, 20} + :wireless_fast -> + {250, 20} + :wireless_superfast -> + {400, 40} + _ -> + :error + end + end + + def speeds(bandwidth_profile, :HFC) do + case bandwidth_profile do + :home_fast -> + {500, 50} + :home_superfast -> + {750, 50} + :home_ultrafast -> + {1000, 100} + :home_hyperfast -> + {2000, 100} + :U100_D40 -> + {100, 40} + _ -> + :error + end + end + + def speeds(bandwidth_profile, :FTTP) do + case bandwidth_profile do + :home_fast -> + {500, 50} + :home_superfast -> + {750, 50} + :home_ultrafast -> + {1000, 100} + :home_hyperfast -> + {2000, 200} + :D100_U40 -> + {100, 40} + :D250_U100 -> + {250, 100} + :D500_200 -> + {500, 200} + :D1000_400 -> + {1000, 400} + _ -> + :error + end + end + + def speed(_bandwidth, _technology) do + :error + end end diff --git a/test/nbn/nbn_ethernet_test.exs b/test/nbn/nbn_ethernet_test.exs index 8f3a24a..ba5899f 100644 --- a/test/nbn/nbn_ethernet_test.exs +++ b/test/nbn/nbn_ethernet_test.exs @@ -16,6 +16,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do alias DiffoExample.Nbn.NniGroup alias DiffoExample.Nbn.Nni alias DiffoExample.Test.Characteristics + alias Diffo.Provider.Assignment alias Diffo.Provider.Instance.Relationship setup_all do @@ -23,10 +24,9 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do end setup do - # on_exit(fn -> - # AshNeo4j.Neo4jHelper.delete_all() - # end) - :ok + on_exit(fn -> + AshNeo4j.Neo4jHelper.delete_all() + end) end describe "build nbn_ethernet" do @@ -69,7 +69,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource",\"name\":\"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"pri","value":{\"technology\":\"FTTP\"}}]}) + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource",\"name\":\"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"pri","value":{}}]}) end test "define nbn_ethernet access" do @@ -82,17 +82,40 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do {:ok, access} = Nbn.define_nbn_ethernet(access, %{characteristic_value_updates: updates}) Characteristics.check_values( - [pri: [avcid: "AVC000910202941", uniid: "UNI000302814545", speed: 1000, technology: :FTTP]], + [ + pri: [ + avcid: "AVC000910202941", + uniid: "UNI000302814545", + speed: 1000, + technology: :FTTP + ] + ], access ) end + @tag debug: true test "relate nbn_ethernet" do {:ok, access} = Nbn.build_nbn_ethernet(%{}) + {:ok, nni_group} = Nbn.build_nni_group(%{}) + {:ok, cvc} = Nbn.build_cvc(%{}) + {:ok, _nni_group} = Nbn.assign_svlan(nni_group, %{assignment: %Assignment{assignee_id: cvc.id, operation: :auto_assign}}) + {:ok, cvc} = Nbn.get_cvc_by_id(cvc.id, load: [:reverse_relationships]) + {:ok, cvc} = Nbn.mine_cvc(cvc) + {:ok, avc} = Nbn.build_avc(%{}) + {:ok, avc} = Nbn.define_avc(avc, %{characteristic_value_updates: [avc: [bandwidth_profile: :home_fast]]}) + {:ok, _cvc} = Nbn.assign_cvlan(cvc, %{assignment: %Assignment{assignee_id: avc.id, operation: :auto_assign}}) + {:ok, avc} = Nbn.get_avc_by_id(avc.id, load: [:reverse_relationships]) + {:ok, avc} = Nbn.mine_avc(avc) + {:ok, ntd} = Nbn.build_ntd(%{}) + {:ok, ntd} = Nbn.define_ntd(ntd, %{characteristic_value_updates: [ntd: [technology: :FTTP]]}) {:ok, uni} = Nbn.build_uni(%{}) + {:ok, _ntd} = Nbn.assign_port(ntd, %{assignment: %Assignment{assignee_id: uni.id, operation: :auto_assign}}) + {:ok, uni} = Nbn.get_uni_by_id(uni.id, load: [:reverse_relationships]) + {:ok, uni} = Nbn.mine_uni(uni) relationships = [ %Relationship{id: avc.id, direction: :forward, type: :owns, alias: :avc}, @@ -106,7 +129,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource","name":"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceRelationship":[{"alias":"avc","type":"owns","resource":{"id":"#{avc.id}","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"}},{"alias":"uni","type":"owns","resource":{"id\":"#{uni.id}","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}}],"supportingResource":[{"id":"avc","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"},{"id\":"uni","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}],"resourceCharacteristic":[{"name":"pri","value":{"avcid":"#{avc.name}","uniid":"#{uni.name}","technology":"FTTP"}}]}) + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource","name":"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceRelationship":[{"alias":"avc","type":"owns","resource":{"id":"#{avc.id}","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"}},{"alias":"uni","type":"owns","resource":{"id\":"#{uni.id}","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}}],"supportingResource":[{"id":"avc","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"},{"id\":"uni","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}],"resourceCharacteristic":[{"name":"pri","value":{"avcid":"#{avc.name}","uniid":"#{uni.name}","technology":"FTTP","bandwidth_profile":"home_fast","speeds":[500,50]}}]}) end end @@ -145,20 +168,20 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do refute is_nil(avc.specification_id) assert is_struct(avc.specification, Specification) assert is_list(avc.characteristics) - assert length(avc.characteristics) == 1 + assert length(avc.characteristics) == 2 end test "define avc" do {:ok, avc} = Nbn.build_avc(%{}) updates = [ - avc: [cir: 100, pir: 1000] + avc: [cvlan: 1, bandwidth_profile: :home_fast] ] {:ok, avc} = Nbn.define_avc(avc, %{characteristic_value_updates: updates}) Characteristics.check_values( - [avc: [cir: 100, pir: 1000]], + [avc: [cvlan: 1, bandwidth_profile: :home_fast]], avc ) end @@ -172,19 +195,50 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do refute is_nil(ntd.specification_id) end - test "define ntd" do + test "define ntd and assign ports to unis" do {:ok, ntd} = Nbn.build_ntd(%{}) updates = [ - ntd: [model: "Arris CM8200", serial_number: "SN-ABC-123456", technology: :HFC] + ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], + ports: [first: 1, last: 4, free: 4, type: "port"] ] {:ok, ntd} = Nbn.define_ntd(ntd, %{characteristic_value_updates: updates}) Characteristics.check_values( - [ntd: [model: "Arris CM8200", serial_number: "SN-ABC-123456", technology: :HFC]], + [ + ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], + ports: [first: 1, last: 4, free: 4, type: "port"] + ], + ntd + ) + + {:ok, ntd} = Nbn.assign_port(ntd, %{assignment: create_uni()}) + {:ok, ntd} = Nbn.assign_port(ntd, %{assignment: create_uni()}) + + Characteristics.check_values( + [ + ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], + ports: [first: 1, last: 4, free: 2, type: "port"] + ], ntd ) + + # mine and check each uni + Enum.each(ntd.forward_relationships, fn relationship -> + {:ok, uni} = + Nbn.get_uni_by_id(relationship.target_id, load: [:reverse_relationships]) + + {:ok, uni} = Nbn.mine_uni(uni) + + # uni should have an uni characteristic with the port + Characteristics.check_values( + [ + uni: [port: &Outstand.any_integer/1] + ], + uni + ) + end) end end @@ -196,19 +250,51 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do refute is_nil(cvc.specification_id) end - test "define cvc" do + test "define cvc and assign cvlans to avcs" do {:ok, cvc} = Nbn.build_cvc(%{}) updates = [ - cvc: [cvc_id: "CVC-POI-SYD-001", bandwidth: 10000] + cvc: [svlan: 1, bandwidth: 10000], + cvlans: [first: 1, last: 4000, free: 4000, type: "cvlan"] ] {:ok, cvc} = Nbn.define_cvc(cvc, %{characteristic_value_updates: updates}) Characteristics.check_values( - [cvc: [cvc_id: "CVC-POI-SYD-001", bandwidth: 10000]], + [ + cvc: [svlan: 1, bandwidth: 10000], + cvlans: [first: 1, last: 4000, free: 4000, type: "cvlan"] + ], + cvc + ) + + {:ok, cvc} = Nbn.assign_cvlan(cvc, %{assignment: create_avc()}) + {:ok, cvc} = Nbn.assign_cvlan(cvc, %{assignment: create_avc()}) + + Characteristics.check_values( + [ + cvc: [svlan: 1, bandwidth: 10000], + cvlans: [first: 1, last: 4000, free: 3998, type: "cvlan"] + ], cvc ) + + # mine and check each avc + Enum.each(cvc.forward_relationships, fn relationship -> + {:ok, avc} = + Nbn.get_avc_by_id(relationship.target_id, load: [:reverse_relationships]) + + {:ok, avc} = Nbn.mine_avc(avc) + + # avc should have an avc characteristic with the cvlan + Characteristics.check_values( + [ + avc: [cvlan: &Outstand.any_integer/1], + cvc: [svlan: :no_value] + ], + avc + ) + end) end end @@ -220,20 +306,51 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do refute is_nil(nni_group.specification_id) end - test "define nni_group" do + test "define nni_group and assign svlans to cvcs" do {:ok, nni_group} = Nbn.build_nni_group(%{}) updates = [ - nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"] + nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], + svlans: [first: 1, last: 4000, free: 4000, type: "svlan"] ] {:ok, nni_group} = Nbn.define_nni_group(nni_group, %{characteristic_value_updates: updates}) Characteristics.check_values( - [nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"]], + [ + nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], + svlans: [first: 1, last: 4000, free: 4000, type: "svlan"] + ], nni_group ) + + {:ok, nni_group} = Nbn.assign_svlan(nni_group, %{assignment: create_cvc()}) + {:ok, nni_group} = Nbn.assign_svlan(nni_group, %{assignment: create_cvc()}) + + Characteristics.check_values( + [ + nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], + svlans: [first: 1, last: 4000, free: 3998, type: "svlan"] + ], + nni_group + ) + + # mine and check each cvc + Enum.each(nni_group.forward_relationships, fn relationship -> + {:ok, cvc} = + Nbn.get_cvc_by_id(relationship.target_id, load: [:reverse_relationships]) + + {:ok, avc} = Nbn.mine_cvc(cvc) + + # cvc should have an cvc characteristic with the svlan + Characteristics.check_values( + [ + cvc: [svlan: &Outstand.any_integer/1] + ], + avc + ) + end) end end @@ -260,4 +377,19 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do ) end end + + defp create_uni() do + {:ok, uni} = Nbn.build_uni(%{}) + %Assignment{assignee_id: uni.id, operation: :auto_assign} + end + + defp create_cvc() do + {:ok, cvc} = Nbn.build_cvc(%{}) + %Assignment{assignee_id: cvc.id, operation: :auto_assign} + end + + defp create_avc() do + {:ok, avc} = Nbn.build_avc(%{}) + %Assignment{assignee_id: avc.id, operation: :auto_assign} + end end diff --git a/test/support/characteristics.ex b/test/support/characteristics.ex index 2ae4b42..5719a84 100644 --- a/test/support/characteristics.ex +++ b/test/support/characteristics.ex @@ -11,6 +11,9 @@ defmodule DiffoExample.Test.Characteristics do import Outstand import ExUnit.Assertions + @doc""" + uses Outstanding to check expected values within instance characteristics + """ def check_values(expected_values, instance) when is_list(expected_values) and is_struct(instance) do Enum.each( From 4e59dc0c3fc8588f75abc860819e79882b8a1746 Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Sat, 18 Apr 2026 16:13:07 +0930 Subject: [PATCH 4/6] update nbn domain for refactored diffo --- .../aggregate_interface.ex | 6 ++ .../services/characteristic_values/circuit.ex | 4 +- .../characteristic_values/constraints.ex | 1 + lib/nbn/resources/avc.ex | 2 +- .../characteristic_values/avc_value.ex | 4 +- .../characteristic_values/cvc_value.ex | 8 ++- .../characteristic_values/nni_value.ex | 1 + .../characteristic_values/ntd_value.ex | 4 +- .../characteristic_values/pri_value.ex | 1 + lib/nbn/resources/cvc.ex | 2 +- lib/nbn/resources/nbn_ethernet.ex | 29 ++++++-- lib/nbn/resources/types/bandwidth_profile.ex | 25 +++++-- lib/nbn/resources/types/speeds.ex | 37 +++++++--- lib/nbn/resources/types/technology.ex | 3 + lib/nbn/resources/uni.ex | 6 +- lib/nbn/util.ex | 36 ++++++---- mix.lock | 2 +- test/access/characteristic_value_test.exs | 39 ++++++----- test/access/dsl_access_test.exs | 6 +- test/nbn/nbn_ethernet_test.exs | 68 +++++++++++++------ test/support/characteristics.ex | 2 +- test/test_helper.exs | 2 +- 22 files changed, 202 insertions(+), 86 deletions(-) diff --git a/lib/access/services/characteristic_values/aggregate_interface.ex b/lib/access/services/characteristic_values/aggregate_interface.ex index b860c74..e733215 100644 --- a/lib/access/services/characteristic_values/aggregate_interface.ex +++ b/lib/access/services/characteristic_values/aggregate_interface.ex @@ -13,6 +13,12 @@ defmodule DiffoExample.Access.AggregateInterface do jason do pick [:name, :physical_interface, :physical_layer, :link_layer, :svlan_id, :vpi] compact(true) + + rename physical_interface: "physicalInterface", + physical_layer: "physicalLayer", + link_layer: "linkLayer", + svlan_id: "svlanId", + vpi: "VPI" end outstanding do diff --git a/lib/access/services/characteristic_values/circuit.ex b/lib/access/services/characteristic_values/circuit.ex index c56cacb..1488631 100644 --- a/lib/access/services/characteristic_values/circuit.ex +++ b/lib/access/services/characteristic_values/circuit.ex @@ -15,6 +15,7 @@ defmodule DiffoExample.Access.Circuit do jason do pick [:circuit_id, :cvlan_id, :vci, :encapsulation, :bandwidth_profile] compact(true) + rename circuit_id: "circuitId", vci: "VCI", bandwidth_profile: "bandwidthProfile" end outstanding do @@ -41,8 +42,7 @@ defmodule DiffoExample.Access.Circuit do constraints: [one_of: [:PPPoA, :PPPoE, :IPoE]], description: "the circuit encapsulation" - field :bandwidth_profile, BandwidthProfile, - description: "the circuit bandwidth profile" + field :bandwidth_profile, BandwidthProfile, description: "the circuit bandwidth profile" end defimpl String.Chars do diff --git a/lib/access/services/characteristic_values/constraints.ex b/lib/access/services/characteristic_values/constraints.ex index 092d6bc..2b023f1 100644 --- a/lib/access/services/characteristic_values/constraints.ex +++ b/lib/access/services/characteristic_values/constraints.ex @@ -13,6 +13,7 @@ defmodule DiffoExample.Access.Constraints do jason do pick [:max_latency, :min_profile] compact(true) + rename max_latency: "maxLatency", min_profile: "minProfile" end outstanding do diff --git a/lib/nbn/resources/avc.ex b/lib/nbn/resources/avc.ex index 5f36f8a..467d699 100644 --- a/lib/nbn/resources/avc.ex +++ b/lib/nbn/resources/avc.ex @@ -112,7 +112,7 @@ defmodule DiffoExample.Nbn.Avc do def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do reverse_relationships = Ash.Changeset.get_attribute(changeset, :reverse_relationships) - cvlan = {:cvlan, hd(hd(reverse_relationships).characteristics).value} + cvlan = {:cvlan, Diffo.Unwrap.unwrap(hd(hd(reverse_relationships).characteristics).value)} Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, avc: [cvlan]) end diff --git a/lib/nbn/resources/characteristic_values/avc_value.ex b/lib/nbn/resources/characteristic_values/avc_value.ex index 201d484..4060c1c 100644 --- a/lib/nbn/resources/characteristic_values/avc_value.ex +++ b/lib/nbn/resources/characteristic_values/avc_value.ex @@ -22,7 +22,9 @@ defmodule DiffoExample.Nbn.AvcValue do end typed_struct do - field :cvlan, :string, description: "the cvlan of the AVC, assigned by the related CVC" + field :cvlan, :integer, + constraints: [min: 0, max: 4000], + description: "the cvlan of the AVC, assigned by the related CVC" field :bandwidth_profile, BandwidthProfile, description: "the bandwidth profile of the AVC" end diff --git a/lib/nbn/resources/characteristic_values/cvc_value.ex b/lib/nbn/resources/characteristic_values/cvc_value.ex index e13e3d6..390d7c4 100644 --- a/lib/nbn/resources/characteristic_values/cvc_value.ex +++ b/lib/nbn/resources/characteristic_values/cvc_value.ex @@ -20,9 +20,13 @@ defmodule DiffoExample.Nbn.CvcValue do end typed_struct do - field :svlan, :string, description: "the svlan of the CVC, assigned by the related NNI Group" + field :svlan, :integer, + constraints: [min: 0, max: 4000], + description: "the svlan of the CVC, assigned by the related NNI Group" - field :bandwidth, :integer, description: "total CVC bandwidth in Mbps" + field :bandwidth, :integer, + constraints: [min: 0, max: 10000], + description: "total CVC bandwidth in Mbps" end defimpl String.Chars do diff --git a/lib/nbn/resources/characteristic_values/nni_value.ex b/lib/nbn/resources/characteristic_values/nni_value.ex index e3028fa..6135bf2 100644 --- a/lib/nbn/resources/characteristic_values/nni_value.ex +++ b/lib/nbn/resources/characteristic_values/nni_value.ex @@ -13,6 +13,7 @@ defmodule DiffoExample.Nbn.NniValue do jason do pick [:port_id, :capacity, :technology] compact(true) + rename port_id: "portId" end outstanding do diff --git a/lib/nbn/resources/characteristic_values/ntd_value.ex b/lib/nbn/resources/characteristic_values/ntd_value.ex index fa591a0..2ff219e 100644 --- a/lib/nbn/resources/characteristic_values/ntd_value.ex +++ b/lib/nbn/resources/characteristic_values/ntd_value.ex @@ -26,7 +26,9 @@ defmodule DiffoExample.Nbn.NtdValue do field :serial_number, :string, description: "the NTD serial number" - field :technology, Technology, description: "the access technology type", default: Technology.default + field :technology, Technology, + description: "the access technology type", + default: Technology.default() end defimpl String.Chars do diff --git a/lib/nbn/resources/characteristic_values/pri_value.ex b/lib/nbn/resources/characteristic_values/pri_value.ex index efe9a09..6c1bfb7 100644 --- a/lib/nbn/resources/characteristic_values/pri_value.ex +++ b/lib/nbn/resources/characteristic_values/pri_value.ex @@ -17,6 +17,7 @@ defmodule DiffoExample.Nbn.PriValue do jason do pick [:avcid, :uniid, :technology, :bandwidth_profile, :speeds] compact(true) + rename avcid: "AVCID", uniid: "UNIID", bandwidth_profile: "bandwidthProfile" end outstanding do diff --git a/lib/nbn/resources/cvc.ex b/lib/nbn/resources/cvc.ex index 9b21921..8e75dcf 100644 --- a/lib/nbn/resources/cvc.ex +++ b/lib/nbn/resources/cvc.ex @@ -127,7 +127,7 @@ defmodule DiffoExample.Nbn.Cvc do def mine_related(changeset, _context) when is_struct(changeset, Ash.Changeset) do reverse_relationships = Ash.Changeset.get_attribute(changeset, :reverse_relationships) - svlan = {:svlan, hd(hd(reverse_relationships).characteristics).value} + svlan = {:svlan, Diffo.Unwrap.unwrap(hd(hd(reverse_relationships).characteristics).value)} Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, cvc: [svlan]) end diff --git a/lib/nbn/resources/nbn_ethernet.ex b/lib/nbn/resources/nbn_ethernet.ex index ebb0413..57e3e49 100644 --- a/lib/nbn/resources/nbn_ethernet.ex +++ b/lib/nbn/resources/nbn_ethernet.ex @@ -119,22 +119,39 @@ defmodule DiffoExample.Nbn.NbnEthernet do Enum.reduce(forward_relationships, [], fn forward_relationship, acc -> {:ok, related} = Diffo.Provider.get_instance_by_id(forward_relationship.target_id) related_name = {alias_to_id(forward_relationship.alias), related.name} + case forward_relationship.alias do :uni -> # extract technology from uni characteristic - [{:technology, Util.extract(related.characteristics, :uni, :technology)} | [ related_name | acc]] + [ + {:technology, Util.extract(related.characteristics, :uni, :technology)} + | [related_name | acc] + ] + :avc -> # extract bandwidth_profile from avc characteristic - [{:bandwidth_profile, Util.extract(related.characteristics, :avc, :bandwidth_profile)} | [ related_name | acc]] + [ + {:bandwidth_profile, + Util.extract(related.characteristics, :avc, :bandwidth_profile)} + | [related_name | acc] + ] + _ -> - [ related_name | acc] + [related_name | acc] end end) # calculate the speeds from the extracted technology and bandwidth_profile - speeds = {:speeds, Util.speeds(Keyword.get(pri_updates, :bandwidth_profile), Keyword.get(pri_updates, :technology))} - - Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, pri: [speeds | pri_updates]) + speeds = + {:speeds, + Util.speeds( + Keyword.get(pri_updates, :bandwidth_profile), + Keyword.get(pri_updates, :technology) + )} + + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, + pri: [speeds | pri_updates] + ) end defp alias_to_id(alias) when is_atom(alias) do diff --git a/lib/nbn/resources/types/bandwidth_profile.ex b/lib/nbn/resources/types/bandwidth_profile.ex index fdc6848..b9a11da 100644 --- a/lib/nbn/resources/types/bandwidth_profile.ex +++ b/lib/nbn/resources/types/bandwidth_profile.ex @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT defmodule DiffoExample.Nbn.BandwidthProfile do @moduledoc """ Diffo - TMF Service and Resource Management with a difference @@ -9,15 +12,29 @@ defmodule DiffoExample.Nbn.BandwidthProfile do use Ash.Type.NewType, subtype_of: :atom, - constraints: [one_of: bandwidth_profiles()] + constraints: [one_of: bandwidth_profiles()] def default do :home_fast end def bandwidth_profiles do - [:D12_U1, :D25_U5, :D25_U10, :D50_U20, :D100_U40, :D250_U100, :D500_U200, :D1000_U400, - :wireless_plus, :wireless_fast, :wireless_superfast, - :home_fast, :home_superfast, :home_ultrafast, :home_hyperfast] + [ + :D12_U1, + :D25_U5, + :D25_U10, + :D50_U20, + :D100_U40, + :D250_U100, + :D500_U200, + :D1000_U400, + :wireless_plus, + :wireless_fast, + :wireless_superfast, + :home_fast, + :home_superfast, + :home_ultrafast, + :home_hyperfast + ] end end diff --git a/lib/nbn/resources/types/speeds.ex b/lib/nbn/resources/types/speeds.ex index 36fea9c..01fae67 100644 --- a/lib/nbn/resources/types/speeds.ex +++ b/lib/nbn/resources/types/speeds.ex @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT defmodule DiffoExample.Nbn.Speeds do @moduledoc """ Diffo - TMF Service and Resource Management with a difference @@ -10,12 +13,19 @@ defmodule DiffoExample.Nbn.Speeds do use Ash.Type.NewType, subtype_of: :tuple, constraints: [ - fields: [downstream: [type: :integer], upstream: [type: :integer]] + fields: [downstream: [type: :integer], upstream: [type: :integer]] ] def speeds do [ - {12, 1}, {25, 5}, {25, 10}, {50, 20}, {100, 40}, {250, 100}, {500, 200}, {1000, 400}, + {12, 1}, + {25, 5}, + {25, 10}, + {50, 20}, + {100, 40}, + {250, 100}, + {500, 200}, + {1000, 400}, # :home_fast {500, 50}, # :home_superfast @@ -23,26 +33,35 @@ defmodule DiffoExample.Nbn.Speeds do # :home_ultrafast {1000, 100}, # :home_hyperfast - {2000, 100}, {2000, 200}, + {2000, 100}, + {2000, 200}, # :wireless_plus, :wireless_fast, :wireless_superfast - {100, 20}, {250, 20}, {400, 40} + {100, 20}, + {250, 20}, + {400, 40} ] end @impl true + def cast_input(nil, _constraints), do: {:ok, nil} + def cast_input(value, _constraints) when is_tuple(value) do - if value in speeds() do + if value in speeds() do {:ok, value} - else - {:error, "invalid downstream and upstream speed combination"} - end + else + {:error, "invalid downstream and upstream speed combination"} + end end def cast_input({_value, _constraints}), do: {:error, "value must be a tuple"} defimpl Jason.Encoder do def encode(speeds, _opts) do - Jason.OrderedObject.new(downstream: speeds.downstream, upstream: speeds.upstream, units: "Mbps") + Jason.OrderedObject.new( + downstream: speeds.downstream, + upstream: speeds.upstream, + units: "Mbps" + ) |> Jason.encode!() end end diff --git a/lib/nbn/resources/types/technology.ex b/lib/nbn/resources/types/technology.ex index c9d6003..6841c91 100644 --- a/lib/nbn/resources/types/technology.ex +++ b/lib/nbn/resources/types/technology.ex @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2025 diffo_example contributors +# +# SPDX-License-Identifier: MIT defmodule DiffoExample.Nbn.Technology do @moduledoc """ Diffo - TMF Service and Resource Management with a difference diff --git a/lib/nbn/resources/uni.ex b/lib/nbn/resources/uni.ex index 3966828..0abd419 100644 --- a/lib/nbn/resources/uni.ex +++ b/lib/nbn/resources/uni.ex @@ -115,10 +115,12 @@ defmodule DiffoExample.Nbn.Uni do ntd_relationship = hd(reverse_relationships) - port = {:port, hd(ntd_relationship.characteristics).value} + port = {:port, Diffo.Unwrap.unwrap(hd(ntd_relationship.characteristics).value)} {:ok, ntd} = Diffo.Provider.get_instance_by_id(ntd_relationship.source_id) technology = {:technology, Util.extract(ntd.characteristics, :ntd, :technology)} - Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, uni: [port, technology]) + Ash.Changeset.force_set_argument(changeset, :characteristic_value_updates, + uni: [port, technology] + ) end end diff --git a/lib/nbn/util.ex b/lib/nbn/util.ex index 345041f..71c0a0d 100644 --- a/lib/nbn/util.ex +++ b/lib/nbn/util.ex @@ -41,27 +41,21 @@ defmodule DiffoExample.Nbn.Util do end @doc """ - Extracts a field value from a named item in a list + Extracts a field value from a named item value map in a list, each value map is unwrapped with Diffo.Unwrap protocol ## Examples iex> DiffoExample.Nbn.Util.extract([%{name: :avc, value: %{cvlan: 1}}], :avc, :cvlan) 1 """ def extract(items, name, field) when is_list(items) and is_atom(name) and is_atom(field) do - Enum.reduce_while(items, nil, fn item, acc -> - if name == item.name do - if item.value != nil do - {:halt, Map.get(item.value, field)} - else - {:halt, nil} - end - else - {:cont, acc} - end - end) + case Enum.find(items, &(&1.name == name)) do + nil -> nil + %{value: nil} -> nil + %{value: value} -> value |> Diffo.Unwrap.unwrap() |> Map.get(field) + end end - @doc""" + @doc """ Returns a tuple of maximum downstream and upstream speeds in Mbps given the bandwidth_profile and technology, or :error @@ -111,10 +105,13 @@ defmodule DiffoExample.Nbn.Util do case bandwidth_profile do :wireless_plus -> {100, 20} + :wireless_fast -> {250, 20} + :wireless_superfast -> {400, 40} + _ -> :error end @@ -124,14 +121,19 @@ defmodule DiffoExample.Nbn.Util do case bandwidth_profile do :home_fast -> {500, 50} + :home_superfast -> {750, 50} + :home_ultrafast -> {1000, 100} + :home_hyperfast -> {2000, 100} + :U100_D40 -> {100, 40} + _ -> :error end @@ -141,20 +143,28 @@ defmodule DiffoExample.Nbn.Util do case bandwidth_profile do :home_fast -> {500, 50} + :home_superfast -> {750, 50} + :home_ultrafast -> {1000, 100} + :home_hyperfast -> {2000, 200} + :D100_U40 -> {100, 40} + :D250_U100 -> {250, 100} + :D500_200 -> {500, 200} + :D1000_400 -> {1000, 400} + _ -> :error end diff --git a/mix.lock b/mix.lock index 56ac9a9..3a8d2e0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "ash": {:hex, :ash, "3.24.2", "38beca133e0dcab07e3c8a7c26e573287ada26e8ba8d4c90ac692b52b34b0309", [:mix], [{:crux, ">= 0.1.2 and < 1.0.0-0", [hex: :crux, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 1.0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.6.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.3", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3fd2a99504c1f58290efc3382501369ee9070098784925bdd7df9dbea8611d32"}, + "ash": {:hex, :ash, "3.24.3", "f7280a43c5e64f769a450f3dd59ace6dcd73edcdd0de7599815b1b31f59292fb", [:mix], [{:crux, ">= 0.1.2 and < 1.0.0-0", [hex: :crux, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.6.29 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 1.0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.6.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.3", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c1022f8c549632137cbc8956f07bb4981405297f5abe7a752b4dffac175c3381"}, "ash_jason": {:hex, :ash_jason, "3.1.0", "84a88dfe5e25a20d55cf2d2664885cd086fa45871e8777aedc3ad96a282e2a6f", [:mix], [{:ash, ">= 3.6.2 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:spark, ">= 2.1.21 and < 3.0.0", [hex: :spark, repo: "hexpm", optional: false]}], "hexpm", "71e6bbc421fb2cf7079f8804814145cca458116c839fc798f9606b806e07eb2b"}, "ash_neo4j": {:git, "https://github.com/diffo-dev/ash_neo4j.git", "044d9d123af30719a9f1f377e2c24b5cc8e21ea8", [branch: "dev"]}, "ash_outstanding": {:hex, :ash_outstanding, "0.2.4", "c72b91f1b8e4859fb033eddf66d0ba36cfd8af0c2a9748c7ef9e6ccfdb5d093d", [:mix], [{:ash, ">= 3.6.2 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:outstanding, "~> 0.2.4", [hex: :outstanding, repo: "hexpm", optional: false]}], "hexpm", "64ba8f582ce69c9050352c75f0895db186c7a56f35039dab34c8e1ab7516f9ce"}, diff --git a/test/access/characteristic_value_test.exs b/test/access/characteristic_value_test.exs index 8620106..5ac058e 100644 --- a/test/access/characteristic_value_test.exs +++ b/test/access/characteristic_value_test.exs @@ -47,12 +47,14 @@ defmodule DiffoExample.Access.CharacteristicValueTest do assert encoding == ~s({\"name\":\"dslam\",\"value\":{\"name\":\"#{@dslam}\",\"family\":\"ISAM",\"model\":\"#{@model}\",\"technology\":\"eth\"}}) - aggregate_interface_value = Value.dynamic( - AggregateInterface.new!(%{ - name: "F DONC BOXH 010J", - physical_interface: "1000BASE-LX", - svlan_id: @svlan_id - })) + aggregate_interface_value = + Value.dynamic( + AggregateInterface.new!(%{ + name: "F DONC BOXH 010J", + physical_interface: "1000BASE-LX", + svlan_id: @svlan_id + }) + ) aggregate_interface = Diffo.Provider.create_characteristic!(%{ @@ -62,19 +64,21 @@ defmodule DiffoExample.Access.CharacteristicValueTest do }) assert Jason.encode!(aggregate_interface) == - ~s({\"name\":\"aggregate_interface\",\"value\":{\"name\":\"F DONC BOXH 010J\",\"physical_interface\":\"1000BASE-LX\",\"physical_layer\":\"GbE\",\"link_layer\":\"QinQ\",\"svlan_id\":3108,\"vpi\":0}}) + ~s({\"name\":\"aggregate_interface\",\"value\":{\"name\":\"F DONC BOXH 010J\",\"physicalInterface\":\"1000BASE-LX\",\"physicalLayer\":\"GbE\",\"linkLayer\":\"QinQ\",\"svlanId\":3108,\"VPI\":0}}) bandwidth_profile = BandwidthProfile.new!(%{downstream: 24, upstream: 1}) assert Jason.encode!(bandwidth_profile) == ~s({\"downstream\":24,\"upstream\":1,\"units\":\"Mbps\"}) - circuit_value = Value.dynamic( - Circuit.new!%{ - circuit_id: @circuit_id, - cvlan_id: @cvlan_id, - bandwidth_profile: bandwidth_profile - }) + circuit_value = + Value.dynamic( + Circuit.new!(%{ + circuit_id: @circuit_id, + cvlan_id: @cvlan_id, + bandwidth_profile: bandwidth_profile + }) + ) circuit = Diffo.Provider.create_characteristic!(%{ @@ -84,11 +88,12 @@ defmodule DiffoExample.Access.CharacteristicValueTest do }) assert Jason.encode!(circuit) == - ~s({\"name\":\"circuit\",\"value\":{\"circuit_id\":\"#{@circuit_id}\",\"cvlan_id\":82,\"vci\":0,\"encapsulation\":\"IPoE\",\"bandwidth_profile\":{\"downstream\":24,\"upstream\":1,\"units\":\"Mbps\"}}}) + ~s({\"name\":\"circuit\",\"value\":{\"circuitId\":\"#{@circuit_id}\",\"cvlan_id\":82,\"VCI\":0,\"encapsulation\":\"IPoE\",\"bandwidthProfile\":{\"downstream\":24,\"upstream\":1,\"units\":\"Mbps\"}}}) - line_value = Value.dynamic( - Line.new!(%{port: @port, slot: @slot, standard: :ADSL2plus, profile: @profile}) - ) + line_value = + Value.dynamic( + Line.new!(%{port: @port, slot: @slot, standard: :ADSL2plus, profile: @profile}) + ) line = Diffo.Provider.create_characteristic!(%{ diff --git a/test/access/dsl_access_test.exs b/test/access/dsl_access_test.exs index 41a7e41..58818f7 100644 --- a/test/access/dsl_access_test.exs +++ b/test/access/dsl_access_test.exs @@ -106,7 +106,7 @@ defmodule DiffoExample.Access.DslAccessTest do encoding = Jason.encode!(dsl_access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"initial\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"physical_layer\":\"GbE\",\"link_layer\":\"QinQ\",\"svlan_id\":0,\"vpi\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":0,\"vci\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"family\":\"ISAM\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) + ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"initial\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"physicalLayer\":\"GbE\",\"linkLayer\":\"QinQ\",\"svlanId\":0,\"VPI\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":0,\"VCI\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"family\":\"ISAM\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) end test "advance service to feasibilityChecked" do @@ -134,7 +134,7 @@ defmodule DiffoExample.Access.DslAccessTest do encoding = Jason.encode!(dsl_access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"feasibilityChecked\",\"operatingStatus\":\"feasible\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"physical_layer\":\"GbE\",\"link_layer\":\"QinQ\",\"svlan_id\":0,\"vpi\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":0,\"vci\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"family\":\"ISAM\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"},{\"id\":\"DONC-0001\",\"href\":\"place/telco/DONC-0001\",\"name\":\"esaId\",\"role\":\"ServingArea\",\"@referredType\":\"GeographicLocation\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) + ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"feasibilityChecked\",\"operatingStatus\":\"feasible\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"physicalLayer\":\"GbE\",\"linkLayer\":\"QinQ\",\"svlanId\":0,\"VPI\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":0,\"VCI\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"family\":\"ISAM\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"},{\"id\":\"DONC-0001\",\"href\":\"place/telco/DONC-0001\",\"name\":\"esaId\",\"role\":\"ServingArea\",\"@referredType\":\"GeographicLocation\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) end end @@ -175,7 +175,7 @@ defmodule DiffoExample.Access.DslAccessTest do encoding = Jason.encode!(dsl_access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"reserved\",\"operatingStatus\":\"feasible\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"name\":\"eth0\",\"physical_layer\":\"GbE\",\"link_layer\":\"QinQ\",\"svlan_id\":3108,\"vpi\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":82,\"vci\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"name\":\"QDONC0001\",\"family\":\"ISAM\",\"model\":\"ISAM7330\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"port\":5,\"slot\":10,\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"},{\"id\":\"DONC-0001\",\"href\":\"place/telco/DONC-0001\",\"name\":\"esaId\",\"role\":\"ServingArea\",\"@referredType\":\"GeographicLocation\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) + ~s({\"id\":\"#{dsl_access.id}",\"href\":\"serviceInventoryManagement/v4/service/#{dsl_access.id}\",\"category\":\"Network Service\",\"serviceSpecification\":{\"id\":\"da9b207a-26c3-451d-8abd-0640c6349979\",\"href\":\"serviceCatalogManagement/v4/serviceSpecification/da9b207a-26c3-451d-8abd-0640c6349979\",\"name\":\"dslAccess\",\"version\":\"v1.0.0\"},\"serviceDate\":\"now\",\"state\":\"reserved\",\"operatingStatus\":\"feasible\",\"feature\":[{\"name\":\"dynamic_line_management\",\"isEnabled\":true,\"featureCharacteristic\":[{\"name\":\"constraints\",\"value\":{}}]}],\"serviceCharacteristic\":[{\"name\":\"aggregate_interface\",\"value\":{\"name\":\"eth0\",\"physicalLayer\":\"GbE\",\"linkLayer\":\"QinQ\",\"svlanId\":3108,\"VPI\":0}},{\"name\":\"circuit\",\"value\":{\"cvlan_id\":82,\"VCI\":0,\"encapsulation\":\"IPoE\"}},{\"name\":\"dslam\",\"value\":{\"name\":\"QDONC0001\",\"family\":\"ISAM\",\"model\":\"ISAM7330\",\"technology\":\"eth\"}},{\"name\":\"line\",\"value\":{\"port\":5,\"slot\":10,\"standard\":\"ADSL2plus\"}}],\"place\":[{\"id\":\"1657363\",\"href\":\"place/telco/1657363\",\"name\":\"addressId\",\"role\":\"CustomerSite\",\"@referredType\":\"GeographicAddress\",\"@type\":\"PlaceRef\"},{\"id\":\"DONC-0001\",\"href\":\"place/telco/DONC-0001\",\"name\":\"esaId\",\"role\":\"ServingArea\",\"@referredType\":\"GeographicLocation\",\"@type\":\"PlaceRef\"}],\"relatedParty\":[{\"id\":\"IND000000897354\",\"name\":\"individualId\",\"role\":\"Customer\",\"@referredType\":\"Individual\",\"@type\":\"PartyRef\"},{\"id\":\"ORG000000123456\",\"name\":\"organizationId\",\"role\":\"Reseller\",\"@referredType\":\"Organization\",\"@type\":\"PartyRef\"}]}) end end diff --git a/test/nbn/nbn_ethernet_test.exs b/test/nbn/nbn_ethernet_test.exs index ba5899f..86abbf8 100644 --- a/test/nbn/nbn_ethernet_test.exs +++ b/test/nbn/nbn_ethernet_test.exs @@ -69,14 +69,19 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource",\"name\":\"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"pri","value":{}}]}) + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/#{access.id}","category":"Network Resource",\"name\":\"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceCharacteristic":[{"name":"pri","value":{}}]}) end test "define nbn_ethernet access" do {:ok, access} = Nbn.build_nbn_ethernet(%{}) updates = [ - pri: [avcid: "AVC000910202941", uniid: "UNI000302814545", speed: 1000, technology: :FTTP] + pri: [ + avcid: "AVC000910202941", + uniid: "UNI000302814545", + speeds: {500, 50}, + technology: :FTTP + ] ] {:ok, access} = Nbn.define_nbn_ethernet(access, %{characteristic_value_updates: updates}) @@ -86,7 +91,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do pri: [ avcid: "AVC000910202941", uniid: "UNI000302814545", - speed: 1000, + speeds: {500, 50}, technology: :FTTP ] ], @@ -94,26 +99,47 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do ) end - @tag debug: true test "relate nbn_ethernet" do {:ok, access} = Nbn.build_nbn_ethernet(%{}) {:ok, nni_group} = Nbn.build_nni_group(%{}) {:ok, cvc} = Nbn.build_cvc(%{}) - {:ok, _nni_group} = Nbn.assign_svlan(nni_group, %{assignment: %Assignment{assignee_id: cvc.id, operation: :auto_assign}}) + + {:ok, _nni_group} = + Nbn.assign_svlan(nni_group, %{ + assignment: %Assignment{assignee_id: cvc.id, operation: :auto_assign} + }) + {:ok, cvc} = Nbn.get_cvc_by_id(cvc.id, load: [:reverse_relationships]) {:ok, cvc} = Nbn.mine_cvc(cvc) {:ok, avc} = Nbn.build_avc(%{}) - {:ok, avc} = Nbn.define_avc(avc, %{characteristic_value_updates: [avc: [bandwidth_profile: :home_fast]]}) - {:ok, _cvc} = Nbn.assign_cvlan(cvc, %{assignment: %Assignment{assignee_id: avc.id, operation: :auto_assign}}) + + {:ok, avc} = + Nbn.define_avc(avc, %{ + characteristic_value_updates: [avc: [bandwidth_profile: :home_fast]] + }) + + {:ok, _cvc} = + Nbn.assign_cvlan(cvc, %{ + assignment: %Assignment{assignee_id: avc.id, operation: :auto_assign} + }) + {:ok, avc} = Nbn.get_avc_by_id(avc.id, load: [:reverse_relationships]) {:ok, avc} = Nbn.mine_avc(avc) {:ok, ntd} = Nbn.build_ntd(%{}) - {:ok, ntd} = Nbn.define_ntd(ntd, %{characteristic_value_updates: [ntd: [technology: :FTTP]]}) + + {:ok, ntd} = + Nbn.define_ntd(ntd, %{characteristic_value_updates: [ntd: [technology: :FTTP]]}) + {:ok, uni} = Nbn.build_uni(%{}) - {:ok, _ntd} = Nbn.assign_port(ntd, %{assignment: %Assignment{assignee_id: uni.id, operation: :auto_assign}}) + + {:ok, _ntd} = + Nbn.assign_port(ntd, %{ + assignment: %Assignment{assignee_id: uni.id, operation: :auto_assign} + }) + {:ok, uni} = Nbn.get_uni_by_id(uni.id, load: [:reverse_relationships]) {:ok, uni} = Nbn.mine_uni(uni) @@ -129,7 +155,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do encoding = Jason.encode!(access) |> Diffo.Util.summarise_dates() assert encoding == - ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/nbnEthernet/#{access.id}","category":"Network Resource","name":"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceRelationship":[{"alias":"avc","type":"owns","resource":{"id":"#{avc.id}","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"}},{"alias":"uni","type":"owns","resource":{"id\":"#{uni.id}","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}}],"supportingResource":[{"id":"avc","href":"resourceInventoryManagement/v4/resource/avc/#{avc.id}"},{"id\":"uni","href":"resourceInventoryManagement/v4/resource/uni/#{uni.id}"}],"resourceCharacteristic":[{"name":"pri","value":{"avcid":"#{avc.name}","uniid":"#{uni.name}","technology":"FTTP","bandwidth_profile":"home_fast","speeds":[500,50]}}]}) + ~s({"id":"#{access.id}","href":"resourceInventoryManagement/v4/resource/#{access.id}","category":"Network Resource","name":"#{access.name}","resourceSpecification":{"id":"f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","href":"resourceCatalogManagement/v4/resourceSpecification/f2a4c6e8-1b3d-4f5a-8c7e-9d0b2e4f6a8c","name":"nbnEthernet","version":"v1.0.0"},"resourceRelationship":[{"alias":"avc","type":"owns","resource":{"id":"#{avc.id}","href":"resourceInventoryManagement/v4/resource/#{avc.id}"}},{"alias":"uni","type":"owns","resource":{"id\":"#{uni.id}","href":"resourceInventoryManagement/v4/resource/#{uni.id}"}}],"supportingResource":[{"id":"avc","href":"resourceInventoryManagement/v4/resource/#{avc.id}"},{"id\":"uni","href":"resourceInventoryManagement/v4/resource/#{uni.id}"}],"resourceCharacteristic":[{"name":"pri","value":{"AVCID":"#{avc.name}","UNIID":"#{uni.name}","technology":"FTTP","bandwidthProfile":"home_fast","speeds":[500,50]}}]}) end end @@ -148,13 +174,13 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do {:ok, uni} = Nbn.build_uni(%{}) updates = [ - uni: [vlan_id: 101, bandwidth_profile: "TC4", technology: :FTTP] + uni: [port: 1, encapsulation: "DSCP Mapped", technology: :FTTP] ] {:ok, uni} = Nbn.define_uni(uni, %{characteristic_value_updates: updates}) Characteristics.check_values( - [uni: [vlan_id: 101, bandwidth_profile: "TC4", technology: :FTTP]], + [uni: [port: 1, encapsulation: "DSCP Mapped", technology: :FTTP]], uni ) end @@ -200,7 +226,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do updates = [ ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], - ports: [first: 1, last: 4, free: 4, type: "port"] + ports: [first: 1, last: 4, free: 4, assignable_type: "port"] ] {:ok, ntd} = Nbn.define_ntd(ntd, %{characteristic_value_updates: updates}) @@ -208,7 +234,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], - ports: [first: 1, last: 4, free: 4, type: "port"] + ports: [first: 1, last: 4, free: 4, assignable_type: "port"] ], ntd ) @@ -219,7 +245,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ ntd: [model: "Sercomm CG4000A", serial_number: "SCOMA1A057A2", technology: :FTTP], - ports: [first: 1, last: 4, free: 2, type: "port"] + ports: [first: 1, last: 4, free: 2, assignable_type: "port"] ], ntd ) @@ -255,7 +281,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do updates = [ cvc: [svlan: 1, bandwidth: 10000], - cvlans: [first: 1, last: 4000, free: 4000, type: "cvlan"] + cvlans: [first: 1, last: 4000, free: 4000, assignable_type: "cvlan"] ] {:ok, cvc} = Nbn.define_cvc(cvc, %{characteristic_value_updates: updates}) @@ -263,7 +289,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ cvc: [svlan: 1, bandwidth: 10000], - cvlans: [first: 1, last: 4000, free: 4000, type: "cvlan"] + cvlans: [first: 1, last: 4000, free: 4000, assignable_type: "cvlan"] ], cvc ) @@ -274,7 +300,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ cvc: [svlan: 1, bandwidth: 10000], - cvlans: [first: 1, last: 4000, free: 3998, type: "cvlan"] + cvlans: [first: 1, last: 4000, free: 3998, assignable_type: "cvlan"] ], cvc ) @@ -311,7 +337,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do updates = [ nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], - svlans: [first: 1, last: 4000, free: 4000, type: "svlan"] + svlans: [first: 1, last: 4000, free: 4000, assignable_type: "svlan"] ] {:ok, nni_group} = @@ -320,7 +346,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], - svlans: [first: 1, last: 4000, free: 4000, type: "svlan"] + svlans: [first: 1, last: 4000, free: 4000, assignable_type: "svlan"] ], nni_group ) @@ -331,7 +357,7 @@ defmodule DiffoExample.Nbn.NbnEthernetTest do Characteristics.check_values( [ nni_group: [name: "SYD-POI-01", location: "Sydney Olympic Park"], - svlans: [first: 1, last: 4000, free: 3998, type: "svlan"] + svlans: [first: 1, last: 4000, free: 3998, assignable_type: "svlan"] ], nni_group ) diff --git a/test/support/characteristics.ex b/test/support/characteristics.ex index 519c233..00bed31 100644 --- a/test/support/characteristics.ex +++ b/test/support/characteristics.ex @@ -11,7 +11,7 @@ defmodule DiffoExample.Test.Characteristics do import Outstand import ExUnit.Assertions - @doc""" + @doc """ uses Outstanding to check expected values within instance characteristics """ def check_values(expected_values, instance) diff --git a/test/test_helper.exs b/test/test_helper.exs index 1caa23b..35444cb 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -6,4 +6,4 @@ Mix.Task.run("app.start") ExUnit.start() level = Application.get_env(:logger, :console) |> Keyword.get(:level) Logger.put_application_level(:diffo, level) -Logger.put_application_level(:ash_neo4j, level) +Logger.put_application_level(:ash_neo4j, :error) From df7fed154057865ca199e0889901b95d00e86c13 Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Sat, 18 Apr 2026 16:55:07 +0930 Subject: [PATCH 5/6] speeds and technology now Enum --- lib/nbn/resources/nbn_ethernet.ex | 3 +- lib/nbn/resources/types/bandwidth_profile.ex | 17 +-- lib/nbn/resources/types/speeds.ex | 120 ++++++++++++++++++ lib/nbn/resources/types/technology.ex | 11 +- lib/nbn/util.ex | 121 ------------------- test/diffo_example_test.exs | 1 + 6 files changed, 129 insertions(+), 144 deletions(-) diff --git a/lib/nbn/resources/nbn_ethernet.ex b/lib/nbn/resources/nbn_ethernet.ex index 57e3e49..d4594a5 100644 --- a/lib/nbn/resources/nbn_ethernet.ex +++ b/lib/nbn/resources/nbn_ethernet.ex @@ -18,6 +18,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do alias DiffoExample.Nbn alias DiffoExample.Nbn.Util + alias DiffoExample.Nbn.Speeds use Ash.Resource, fragments: [BaseInstance], @@ -144,7 +145,7 @@ defmodule DiffoExample.Nbn.NbnEthernet do # calculate the speeds from the extracted technology and bandwidth_profile speeds = {:speeds, - Util.speeds( + Speeds.speeds( Keyword.get(pri_updates, :bandwidth_profile), Keyword.get(pri_updates, :technology) )} diff --git a/lib/nbn/resources/types/bandwidth_profile.ex b/lib/nbn/resources/types/bandwidth_profile.ex index b9a11da..0f30dea 100644 --- a/lib/nbn/resources/types/bandwidth_profile.ex +++ b/lib/nbn/resources/types/bandwidth_profile.ex @@ -8,18 +8,8 @@ defmodule DiffoExample.Nbn.BandwidthProfile do BandwidthProfile type for NBN domain """ - require Ash.Type.NewType - - use Ash.Type.NewType, - subtype_of: :atom, - constraints: [one_of: bandwidth_profiles()] - - def default do - :home_fast - end - - def bandwidth_profiles do - [ + use Ash.Type.Enum, + values: [ :D12_U1, :D25_U5, :D25_U10, @@ -36,5 +26,6 @@ defmodule DiffoExample.Nbn.BandwidthProfile do :home_ultrafast, :home_hyperfast ] - end + + def default, do: :home_fast end diff --git a/lib/nbn/resources/types/speeds.ex b/lib/nbn/resources/types/speeds.ex index 01fae67..ce3a931 100644 --- a/lib/nbn/resources/types/speeds.ex +++ b/lib/nbn/resources/types/speeds.ex @@ -9,6 +9,7 @@ defmodule DiffoExample.Nbn.Speeds do """ require Ash.Type.NewType + alias DiffoExample.Nbn.Technology use Ash.Type.NewType, subtype_of: :tuple, @@ -65,4 +66,123 @@ defmodule DiffoExample.Nbn.Speeds do |> Jason.encode!() end end + + @doc """ + Returns a tuple of maximum downstream and upstream speeds in Mbps + given the bandwidth_profile and technology, or :error + + ## Examples + iex> DiffoExample.Nbn.Speeds.speeds(:D12_U1, :Satellite) + {12, 1} + iex> DiffoExample.Nbn.Speeds.speeds(:home_fast, :FTTP) + {500, 50} + iex> DiffoExample.Nbn.Speeds.speeds(:home_hyperfast, :HFC) + {2000, 100} + iex> DiffoExample.Nbn.Speeds.speeds(:home_fast, :FixedWireless) + :error + """ + def speeds(:D12_U1, technology) when is_atom(technology) do + if technology in Technology.values() do + {12, 1} + else + :error + end + end + + def speeds(:D25_U5, technology) when is_atom(technology) do + if technology in Technology.values() do + {25, 5} + else + :error + end + end + + def speeds(:D25_U10, technology) when is_atom(technology) do + if technology in [:FTTP, :HFC, :FTTC] do + {25, 10} + else + :error + end + end + + def speeds(:D50_U20, technology) when is_atom(technology) do + if technology in [:FTTP, :HFC, :FTTC] do + {50, 20} + else + :error + end + end + + def speeds(bandwidth_profile, :FixedWireless) do + case bandwidth_profile do + :wireless_plus -> + {100, 20} + + :wireless_fast -> + {250, 20} + + :wireless_superfast -> + {400, 40} + + _ -> + :error + end + end + + def speeds(bandwidth_profile, :HFC) do + case bandwidth_profile do + :home_fast -> + {500, 50} + + :home_superfast -> + {750, 50} + + :home_ultrafast -> + {1000, 100} + + :home_hyperfast -> + {2000, 100} + + :U100_D40 -> + {100, 40} + + _ -> + :error + end + end + + def speeds(bandwidth_profile, :FTTP) do + case bandwidth_profile do + :home_fast -> + {500, 50} + + :home_superfast -> + {750, 50} + + :home_ultrafast -> + {1000, 100} + + :home_hyperfast -> + {2000, 200} + + :D100_U40 -> + {100, 40} + + :D250_U100 -> + {250, 100} + + :D500_200 -> + {500, 200} + + :D1000_400 -> + {1000, 400} + + _ -> + :error + end + end + + def speeds(_bandwidth, _technology) do + :error + end end diff --git a/lib/nbn/resources/types/technology.ex b/lib/nbn/resources/types/technology.ex index 6841c91..74f7b6a 100644 --- a/lib/nbn/resources/types/technology.ex +++ b/lib/nbn/resources/types/technology.ex @@ -8,17 +8,10 @@ defmodule DiffoExample.Nbn.Technology do Technology type for NBN domain """ - require Ash.Type.NewType - - use Ash.Type.NewType, - subtype_of: :atom, - constraints: [one_of: technology()] + use Ash.Type.Enum, + values: [:FTTP, :FTTN, :FTTB, :FTTC, :HFC, :FixedWireless, :Satellite] def default do :FTTP end - - def technology do - [:FTTP, :FTTN, :FTTB, :FTTC, :HFC, :FixedWireless, :Satellite] - end end diff --git a/lib/nbn/util.ex b/lib/nbn/util.ex index 71c0a0d..453bcb7 100644 --- a/lib/nbn/util.ex +++ b/lib/nbn/util.ex @@ -9,8 +9,6 @@ defmodule DiffoExample.Nbn.Util do Util - various utilities for NBN domain """ - alias DiffoExample.Nbn.Technology - @doc """ Generates a new random NBN identifier with the prefix @@ -54,123 +52,4 @@ defmodule DiffoExample.Nbn.Util do %{value: value} -> value |> Diffo.Unwrap.unwrap() |> Map.get(field) end end - - @doc """ - Returns a tuple of maximum downstream and upstream speeds in Mbps - given the bandwidth_profile and technology, or :error - - ## Examples - iex> DiffoExample.Nbn.Util.speeds(:D12_U1, :Satellite) - {12, 1} - iex> DiffoExample.Nbn.Util.speeds(:home_fast, :FTTP) - {500, 50} - iex> DiffoExample.Nbn.Util.speeds(:home_hyperfast, :HFC) - {2000, 100} - iex> DiffoExample.Nbn.Util.speeds(:home_fast, :FixedWireless) - :error - """ - def speeds(:D12_U1, technology) when is_atom(technology) do - if technology in Technology.technology() do - {12, 1} - else - :error - end - end - - def speeds(:D25_U5, technology) when is_atom(technology) do - if technology in Technology.technology() do - {25, 5} - else - :error - end - end - - def speeds(:D25_U10, technology) when is_atom(technology) do - if technology in [:FTTP, :HFC, :FTTC] do - {25, 10} - else - :error - end - end - - def speeds(:D50_U20, technology) when is_atom(technology) do - if technology in [:FTTP, :HFC, :FTTC] do - {50, 20} - else - :error - end - end - - def speeds(bandwidth_profile, :FixedWireless) do - case bandwidth_profile do - :wireless_plus -> - {100, 20} - - :wireless_fast -> - {250, 20} - - :wireless_superfast -> - {400, 40} - - _ -> - :error - end - end - - def speeds(bandwidth_profile, :HFC) do - case bandwidth_profile do - :home_fast -> - {500, 50} - - :home_superfast -> - {750, 50} - - :home_ultrafast -> - {1000, 100} - - :home_hyperfast -> - {2000, 100} - - :U100_D40 -> - {100, 40} - - _ -> - :error - end - end - - def speeds(bandwidth_profile, :FTTP) do - case bandwidth_profile do - :home_fast -> - {500, 50} - - :home_superfast -> - {750, 50} - - :home_ultrafast -> - {1000, 100} - - :home_hyperfast -> - {2000, 200} - - :D100_U40 -> - {100, 40} - - :D250_U100 -> - {250, 100} - - :D500_200 -> - {500, 200} - - :D1000_400 -> - {1000, 400} - - _ -> - :error - end - end - - def speed(_bandwidth, _technology) do - :error - end end diff --git a/test/diffo_example_test.exs b/test/diffo_example_test.exs index d500bb4..61a86e7 100644 --- a/test/diffo_example_test.exs +++ b/test/diffo_example_test.exs @@ -7,4 +7,5 @@ defmodule DiffoExampleTest do use ExUnit.Case doctest DiffoExample.Access.Util doctest DiffoExample.Nbn.Util + doctest DiffoExample.Nbn.Speeds end From 04d7fefc960a233093a3b3e167ab0f666dc4205f Mon Sep 17 00:00:00 2001 From: Matt Beanland Date: Sat, 18 Apr 2026 16:59:42 +0930 Subject: [PATCH 6/6] removed String.Chars --- lib/access/resources/characteristic_values/cable_value.ex | 6 ------ lib/access/resources/characteristic_values/card_value.ex | 6 ------ lib/access/resources/characteristic_values/float_unit.ex | 6 ------ lib/access/resources/characteristic_values/integer_unit.ex | 6 ------ lib/access/resources/characteristic_values/path_value.ex | 6 ------ lib/access/resources/characteristic_values/shelf_value.ex | 6 ------ .../services/characteristic_values/aggregate_interface.ex | 6 ------ .../services/characteristic_values/bandwidth_profile.ex | 6 ------ lib/access/services/characteristic_values/circuit.ex | 6 ------ lib/access/services/characteristic_values/constraints.ex | 6 ------ lib/access/services/characteristic_values/dslam.ex | 6 ------ lib/access/services/characteristic_values/line.ex | 6 ------ lib/nbn/resources/characteristic_values/avc_value.ex | 6 ------ lib/nbn/resources/characteristic_values/cvc_value.ex | 6 ------ lib/nbn/resources/characteristic_values/nni_group_value.ex | 6 ------ lib/nbn/resources/characteristic_values/nni_value.ex | 6 ------ lib/nbn/resources/characteristic_values/ntd_value.ex | 6 ------ lib/nbn/resources/characteristic_values/pri_value.ex | 6 ------ lib/nbn/resources/characteristic_values/uni_value.ex | 6 ------ 19 files changed, 114 deletions(-) diff --git a/lib/access/resources/characteristic_values/cable_value.ex b/lib/access/resources/characteristic_values/cable_value.ex index 1c72707..e5a315a 100644 --- a/lib/access/resources/characteristic_values/cable_value.ex +++ b/lib/access/resources/characteristic_values/cable_value.ex @@ -30,10 +30,4 @@ defmodule DiffoExample.Access.CableValue do field :technology, :atom, description: "the cable technology" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/resources/characteristic_values/card_value.ex b/lib/access/resources/characteristic_values/card_value.ex index 7d59590..44441da 100644 --- a/lib/access/resources/characteristic_values/card_value.ex +++ b/lib/access/resources/characteristic_values/card_value.ex @@ -28,10 +28,4 @@ defmodule DiffoExample.Access.CardValue do field :technology, :atom, description: "the card technology" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/resources/characteristic_values/float_unit.ex b/lib/access/resources/characteristic_values/float_unit.ex index 7e75101..ad05cdf 100644 --- a/lib/access/resources/characteristic_values/float_unit.ex +++ b/lib/access/resources/characteristic_values/float_unit.ex @@ -24,10 +24,4 @@ defmodule DiffoExample.Access.FloatUnit do field :unit, :atom, description: "the unit" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/resources/characteristic_values/integer_unit.ex b/lib/access/resources/characteristic_values/integer_unit.ex index 4f9be8c..11d4c9b 100644 --- a/lib/access/resources/characteristic_values/integer_unit.ex +++ b/lib/access/resources/characteristic_values/integer_unit.ex @@ -24,10 +24,4 @@ defmodule DiffoExample.Access.IntegerUnit do field :unit, :atom, description: "the unit" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/resources/characteristic_values/path_value.ex b/lib/access/resources/characteristic_values/path_value.ex index fba2f11..7ea2e0e 100644 --- a/lib/access/resources/characteristic_values/path_value.ex +++ b/lib/access/resources/characteristic_values/path_value.ex @@ -33,10 +33,4 @@ defmodule DiffoExample.Access.PathValue do field :technology, :atom, description: "the path technology" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/resources/characteristic_values/shelf_value.ex b/lib/access/resources/characteristic_values/shelf_value.ex index e31da9f..80b1313 100644 --- a/lib/access/resources/characteristic_values/shelf_value.ex +++ b/lib/access/resources/characteristic_values/shelf_value.ex @@ -28,10 +28,4 @@ defmodule DiffoExample.Access.ShelfValue do field :technology, :atom, description: "the shelf technology" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/aggregate_interface.ex b/lib/access/services/characteristic_values/aggregate_interface.ex index e733215..40d677c 100644 --- a/lib/access/services/characteristic_values/aggregate_interface.ex +++ b/lib/access/services/characteristic_values/aggregate_interface.ex @@ -52,10 +52,4 @@ defmodule DiffoExample.Access.AggregateInterface do default: 0, description: "the aggregate interface vpi" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/bandwidth_profile.ex b/lib/access/services/characteristic_values/bandwidth_profile.ex index 5d49921..bd1c35b 100644 --- a/lib/access/services/characteristic_values/bandwidth_profile.ex +++ b/lib/access/services/characteristic_values/bandwidth_profile.ex @@ -33,10 +33,4 @@ defmodule DiffoExample.Access.BandwidthProfile do constraints: [one_of: [:kbps, :Mbps]], description: "the bandwidth profile units" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/circuit.ex b/lib/access/services/characteristic_values/circuit.ex index 1488631..2aaddd4 100644 --- a/lib/access/services/characteristic_values/circuit.ex +++ b/lib/access/services/characteristic_values/circuit.ex @@ -44,10 +44,4 @@ defmodule DiffoExample.Access.Circuit do field :bandwidth_profile, BandwidthProfile, description: "the circuit bandwidth profile" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/constraints.ex b/lib/access/services/characteristic_values/constraints.ex index 2b023f1..720e45f 100644 --- a/lib/access/services/characteristic_values/constraints.ex +++ b/lib/access/services/characteristic_values/constraints.ex @@ -29,10 +29,4 @@ defmodule DiffoExample.Access.Constraints do constraints: [instance_of: BandwidthProfile], description: "the circuit bandwidth profile" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/dslam.ex b/lib/access/services/characteristic_values/dslam.ex index 5a2b92b..8042f43 100644 --- a/lib/access/services/characteristic_values/dslam.ex +++ b/lib/access/services/characteristic_values/dslam.ex @@ -36,10 +36,4 @@ defmodule DiffoExample.Access.Dslam do default: :eth, description: "the DSLAM technology" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/access/services/characteristic_values/line.ex b/lib/access/services/characteristic_values/line.ex index 67e6a80..dbc5624 100644 --- a/lib/access/services/characteristic_values/line.ex +++ b/lib/access/services/characteristic_values/line.ex @@ -35,10 +35,4 @@ defmodule DiffoExample.Access.Line do field :profile, :string, description: "the line port profile" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/avc_value.ex b/lib/nbn/resources/characteristic_values/avc_value.ex index 4060c1c..e298968 100644 --- a/lib/nbn/resources/characteristic_values/avc_value.ex +++ b/lib/nbn/resources/characteristic_values/avc_value.ex @@ -28,10 +28,4 @@ defmodule DiffoExample.Nbn.AvcValue do field :bandwidth_profile, BandwidthProfile, description: "the bandwidth profile of the AVC" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/cvc_value.ex b/lib/nbn/resources/characteristic_values/cvc_value.ex index 390d7c4..c67f791 100644 --- a/lib/nbn/resources/characteristic_values/cvc_value.ex +++ b/lib/nbn/resources/characteristic_values/cvc_value.ex @@ -28,10 +28,4 @@ defmodule DiffoExample.Nbn.CvcValue do constraints: [min: 0, max: 10000], description: "total CVC bandwidth in Mbps" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/nni_group_value.ex b/lib/nbn/resources/characteristic_values/nni_group_value.ex index bed82c3..9b1b72c 100644 --- a/lib/nbn/resources/characteristic_values/nni_group_value.ex +++ b/lib/nbn/resources/characteristic_values/nni_group_value.ex @@ -24,10 +24,4 @@ defmodule DiffoExample.Nbn.NniGroupValue do field :location, :string, description: "the Point of Interconnect (PoI) location" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/nni_value.ex b/lib/nbn/resources/characteristic_values/nni_value.ex index 6135bf2..a4d69b3 100644 --- a/lib/nbn/resources/characteristic_values/nni_value.ex +++ b/lib/nbn/resources/characteristic_values/nni_value.ex @@ -27,10 +27,4 @@ defmodule DiffoExample.Nbn.NniValue do field :technology, :atom, description: "the NNI technology (:Ethernet, :Fibre)" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/ntd_value.ex b/lib/nbn/resources/characteristic_values/ntd_value.ex index 2ff219e..03f0be7 100644 --- a/lib/nbn/resources/characteristic_values/ntd_value.ex +++ b/lib/nbn/resources/characteristic_values/ntd_value.ex @@ -30,10 +30,4 @@ defmodule DiffoExample.Nbn.NtdValue do description: "the access technology type", default: Technology.default() end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/pri_value.ex b/lib/nbn/resources/characteristic_values/pri_value.ex index 6c1bfb7..a678aeb 100644 --- a/lib/nbn/resources/characteristic_values/pri_value.ex +++ b/lib/nbn/resources/characteristic_values/pri_value.ex @@ -35,10 +35,4 @@ defmodule DiffoExample.Nbn.PriValue do field :speeds, Speeds, description: "the downstream and upstream speeds in Mbps" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end diff --git a/lib/nbn/resources/characteristic_values/uni_value.ex b/lib/nbn/resources/characteristic_values/uni_value.ex index 8bd5670..fcdd6a3 100644 --- a/lib/nbn/resources/characteristic_values/uni_value.ex +++ b/lib/nbn/resources/characteristic_values/uni_value.ex @@ -28,10 +28,4 @@ defmodule DiffoExample.Nbn.UniValue do field :technology, Technology, description: "the access technology type" end - - defimpl String.Chars do - def to_string(struct) do - inspect(struct) - end - end end