Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ not. Add any useful hypotheses as a follow-up comment on the issue, then leave i
- Using the removed `AssignableValue` TypedStruct — it no longer exists; use `pools do`.
- Calling `Assigner.assign/4` when a `pools do` declaration exists — prefer `Assigner.assign/3` which looks up the thing automatically.
- Forgetting to call `Pool.update_pools/3` in `:define` actions when the resource has `pools do` — pool bounds (`first`, `last`, `algorithm`) are set here.
- Calling `Assigner.assign/3` on an instance that is not in the correct lifecycle state — the assigner enforces: resource instances must have `resource_state: :operating`; service instances must have `service_state: :active` or `:inactive`. Lifecycle state transitions are an internal domain concern managed by the provider; assignment actions are external-facing. Future: consumer reads may filter out non-`:operating` resources entirely.
- Using `characteristic :pool_name, Diffo.Provider.AssignedToRelationship` — `AssignedToRelationship` no longer exists; use `pools do / pool :name, :thing / end` instead.
- Querying `Diffo.Provider.Relationship` for assignment records — assignments are stored as `Diffo.Provider.DefinedSimpleRelationship`; access them via `instance.assignments`.
- Filtering `instance.forward_relationships` for `type == :assignedTo` — those records no longer exist there; use `instance.assignments` directly.
Expand Down
1 change: 1 addition & 0 deletions lib/diffo/provider.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ defmodule Diffo.Provider do
define :suspend_service, action: :suspend
define :terminate_service, action: :terminate
define :status_service, action: :status
define :lifecycle_resource, action: :lifecycle
define :respecify_instance, action: :specify
define :relate_instance_features, action: :relate_features
define :unrelate_instance_features, action: :unrelate_features
Expand Down
17 changes: 14 additions & 3 deletions lib/diffo/provider/assigner/assigner.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ defmodule Diffo.Provider.Assigner do
"""
def assign(result, changeset, pool_name)
when is_struct(result) and is_struct(changeset, Ash.Changeset) and is_atom(pool_name) do
case result.__struct__.pool(pool_name) do
nil -> {:error, "pool #{pool_name} not declared on #{result.__struct__}"}
pool -> assign(result, changeset, pool_name, pool.thing)
with :ok <- check_lifecycle(result) do
case result.__struct__.pool(pool_name) do
nil -> {:error, "pool #{pool_name} not declared on #{result.__struct__}"}
pool -> assign(result, changeset, pool_name, pool.thing)
end
end
end

Expand Down Expand Up @@ -58,6 +60,15 @@ defmodule Diffo.Provider.Assigner do
end
end

defp check_lifecycle(%{type: :resource, resource_state: state}) when state != :operating,
do: {:error, "cannot assign: resource lifecycle state is #{inspect(state)}, must be :operating"}

defp check_lifecycle(%{type: :service, service_state: state})
when state not in [:active, :inactive],
do: {:error, "cannot assign: service state is #{inspect(state)}, must be :active or :inactive"}

defp check_lifecycle(_), do: :ok

defp create_assignment(result, pool, thing, value, assignee_id)
when is_struct(result) and is_atom(pool) and is_atom(thing) and is_integer(value) and
is_bitstring(assignee_id) do
Expand Down
16 changes: 16 additions & 0 deletions lib/diffo/provider/components/base_instance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ defmodule Diffo.Provider.BaseInstance do
:endOperatingDate,
:state,
:operatingStatus,
:lifecycleState,
:administrativeState,
:operationalState,
:resourceStatus,
Expand Down Expand Up @@ -366,6 +367,14 @@ defmodule Diffo.Provider.BaseInstance do
constraints one_of: Diffo.Provider.Service.service_operating_statuses()
end

attribute :resource_state, :atom do
description "the TMF lifecycleState for resource instances: planning, installing, operating, or retiring"
allow_nil? true
public? true
default nil
constraints one_of: [:planning, :installing, :operating, :retiring]
end

create_timestamp :created_at

update_timestamp :updated_at
Expand Down Expand Up @@ -605,6 +614,13 @@ defmodule Diffo.Provider.BaseInstance do
accept [:service_operating_status]
end

update :lifecycle do
description "sets the TMF lifecycleState for a resource instance"
require_atomic? false
validate attribute_equals(:type, :resource)
accept [:resource_state]
end

update :relate_features do
description "relates features to the instance"
argument :features, {:array, :uuid}
Expand Down
5 changes: 4 additions & 1 deletion lib/diffo/provider/components/instance/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ defmodule Diffo.Provider.Instance.Util do
|> Diffo.Util.set(:operatingStatus, record.service_operating_status)

:resource ->
result
case record.resource_state do
nil -> result
state -> Diffo.Util.set(result, :lifecycleState, state)
end
# |> Diffo.Util.ensure_not_nil(:administrativeState, record.resource_administrative_state)
# |> Diffo.Util.ensure_not_nil(:operationalState, record.resource_operational_state)
# |> Diffo.Util.ensure_not_nil(:resourceStatus, record.resource_status)
Expand Down
12 changes: 8 additions & 4 deletions test/provider/extension/assigner_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
]

{:ok, card} = Servo.define_card(card, %{characteristic_value_updates: updates})
{:ok, card} = Servo.lifecycle_card(card, %{resource_state: :operating})

{:ok, card} =
Servo.assign_port(card, %{
Expand All @@ -109,7 +110,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
encoding = Jason.encode!(card) |> Diffo.Util.summarise_dates()

assert encoding ==
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":1}]}]})
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"lifecycleState\":\"operating\",\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":1}]}]})
end

test "auto assign two ports to same resource" do
Expand All @@ -123,6 +124,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
]

{:ok, card} = Servo.define_card(card, %{characteristic_value_updates: updates})
{:ok, card} = Servo.lifecycle_card(card, %{resource_state: :operating})

{:ok, card} =
Servo.assign_port(card, %{
Expand All @@ -139,7 +141,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
encoding = Jason.encode!(card) |> Diffo.Util.summarise_dates()

assert encoding ==
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":1}]},{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":2}]}]})
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"lifecycleState\":\"operating\",\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":1}]},{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":2}]}]})
end

test "specific assignment rejects duplicate request" do
Expand All @@ -153,6 +155,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
]

{:ok, card} = Servo.define_card(card, %{characteristic_value_updates: updates})
{:ok, card} = Servo.lifecycle_card(card, %{resource_state: :operating})

{:ok, card} =
Servo.assign_port(card, %{
Expand All @@ -169,7 +172,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
encoding = Jason.encode!(card) |> Diffo.Util.summarise_dates()

assert encoding ==
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":5}]}]})
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"lifecycleState\":\"operating\",\"resourceRelationship\":[{\"type\":\"assignedTo\",\"resource\":{\"id\":\"#{assignee.id}\",\"href\":\"resourceInventoryManagement/v4/resource/#{assignee.id}\"},\"resourceRelationshipCharacteristic\":[{\"name\":\"port\",\"value\":5}]}]})
end

test "unassign an auto-assigned port from a resource" do
Expand All @@ -183,6 +186,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
]

{:ok, card} = Servo.define_card(card, %{characteristic_value_updates: updates})
{:ok, card} = Servo.lifecycle_card(card, %{resource_state: :operating})

{:ok, card} =
Servo.assign_port(card, %{
Expand All @@ -207,7 +211,7 @@ defmodule Diffo.Provider.Extension.AssignerTest do
encoding = Jason.encode!(card) |> Diffo.Util.summarise_dates()

assert encoding ==
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"}})
~s({\"id\":\"#{card.id}",\"href\":\"resourceInventoryManagement/v4/resource/#{card.id}",\"category\":\"Network Resource\",\"description\":\"A Card Resource Instance\",\"resourceSpecification\":{\"id\":\"cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"href\":\"resourceCatalogManagement/v4/resourceSpecification/cd29956f-6c68-44cc-bf54-705eb8d2f754\",\"name\":\"card\",\"version\":\"v1.0.0\"},\"lifecycleState\":\"operating\"})
end
end
end
1 change: 1 addition & 0 deletions test/support/servo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ defmodule Diffo.Test.Servo do
define :define_card, action: :define
define :relate_card, action: :relate
define :assign_port, action: :assign_port
define :lifecycle_card, action: :lifecycle
end

resource Broadband do
Expand Down
Loading