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
4 changes: 2 additions & 2 deletions documentation/dsls/DSL-Diffo.Provider.Instance.Extension.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Adds a Characteristic
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`name`](#features-feature-characteristic-name){: #features-feature-characteristic-name .spark-required} | `atom` | | The name of the characteristic, an atom |
| [`value_type`](#features-feature-characteristic-value_type){: #features-feature-characteristic-value_type } | `atom` | | The optional type of the characteristic's value, an atom, may be a module name such as an Ash.TypedStruct |
| [`value_type`](#features-feature-characteristic-value_type){: #features-feature-characteristic-value_type } | `any` | | The type of the characteristic's value. An atom module name such as an Ash.TypedStruct for a scalar value, or `{:array, module}` for an array of values of that type. |



Expand Down Expand Up @@ -169,7 +169,7 @@ Adds a Characteristic
| Name | Type | Default | Docs |
|------|------|---------|------|
| [`name`](#characteristics-characteristic-name){: #characteristics-characteristic-name .spark-required} | `atom` | | The name of the characteristic, an atom |
| [`value_type`](#characteristics-characteristic-value_type){: #characteristics-characteristic-value_type } | `atom` | | The optional type of the characteristic's value, an atom, may be a module name such as an Ash.TypedStruct |
| [`value_type`](#characteristics-characteristic-value_type){: #characteristics-characteristic-value_type } | `any` | | The type of the characteristic's value. An atom module name such as an Ash.TypedStruct for a scalar value, or `{:array, module}` for an array of values of that type. |



Expand Down
57 changes: 53 additions & 4 deletions lib/diffo/provider/components/characteristic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,45 @@ defmodule Diffo.Provider.Characteristic do
end

jason do
pick [:name, :value]
pick [:name, :value, :values]
compact true
end

outstanding do
expect [:name, :value]
expect [:name]

customize fn outstanding, expected, actual ->
key = if expected.is_array, do: :values, else: :value
expected_val = Map.get(expected, key)

val_out =
if actual == nil do
expected_val
else
Outstanding.outstanding(expected_val, Map.get(actual, key))
end

cond do
val_out == nil ->
outstanding

outstanding == nil ->
%{key => val_out}
|> Outstand.map_to_struct(Diffo.Provider.Characteristic)
|> Ash.Test.strip_metadata()

true ->
Map.put(outstanding, key, val_out)
end
end
end

actions do
defaults [:destroy]

create :create do
description "creates a characteristic"
accept [:name, :value, :type]
accept [:name, :value, :values, :is_array, :type]
end

read :read do
Expand All @@ -71,7 +97,7 @@ defmodule Diffo.Provider.Characteristic do
update :update do
primary? true
description "updates the characteristic value or instance, feature or relationship"
accept [:value]
accept [:value, :values, :is_array]
argument :instance_id, :uuid
argument :feature_id, :uuid
argument :relationship_id, :uuid
Expand Down Expand Up @@ -101,6 +127,20 @@ defmodule Diffo.Provider.Characteristic do
public? true
end

attribute :values, {:array, Diffo.Type.Value} do
description "the array of values of the characteristic"
constraints items: Diffo.Type.Value.subtype_constraints()
allow_nil? true
public? true
end

attribute :is_array, :boolean do
description "true when this characteristic holds an array of values; defaults false"
default false
allow_nil? false
public? true
end

attribute :type, :atom do
description "the type of the characteristic"
allow_nil? false
Expand Down Expand Up @@ -154,6 +194,10 @@ defmodule Diffo.Provider.Characteristic do
validate present([:instance_id, :feature_id, :relationship_id], at_most: 1) do
message "characteristic must be related to at most one of an instance, feature or relationship"
end

validate present([:value, :values], at_most: 1) do
message "characteristic must have at most one of value or values"
end
end

preparations do
Expand All @@ -172,4 +216,9 @@ defmodule Diffo.Provider.Characteristic do

"""
def compare(%{name: name0}, %{name: name1}), do: Diffo.Util.compare(name0, name1)

defimpl Diffo.Unwrap do
def unwrap(%{values: values}) when is_list(values), do: Diffo.Unwrap.unwrap(values)
def unwrap(%{value: value}), do: Diffo.Unwrap.unwrap(value)
end
end
5 changes: 3 additions & 2 deletions lib/diffo/provider/components/instance/extension.ex
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ defmodule Diffo.Provider.Instance.Extension do
],
value_type: [
doc: """
The optional type of the characteristic's value, an atom, may be a module name such as an Ash.TypedStruct
The type of the characteristic's value. An atom module name such as an Ash.TypedStruct for a scalar value,
or `{:array, module}` for an array of values of that type.
""",
type: :atom
type: :any
]
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,16 @@ defmodule Diffo.Provider.Instance.Characteristic do

Enum.reduce_while(characteristics, [], fn %{name: name, value_type: value_type}, acc ->
try do
value = Value.dynamic(struct(value_type))
attrs =
case value_type do
{:array, _inner} ->
%{name: name, type: type, values: [], is_array: true}

case Provider.create_characteristic(%{name: name, type: type, value: value}) do
module ->
%{name: name, type: type, value: Value.dynamic(struct(module))}
end

case Provider.create_characteristic(attrs) do
{:ok, result} ->
{:cont, [result | acc]}

Expand Down
11 changes: 9 additions & 2 deletions lib/diffo/provider/components/instance/extension/feature.ex
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,16 @@ defmodule Diffo.Provider.Instance.Feature do
characteristic_ids =
Enum.reduce_while(characteristics, [], fn %{name: name, value_type: value_type}, acc ->
try do
value = Value.dynamic(struct(value_type))
attrs =
case value_type do
{:array, _inner} ->
%{name: name, type: :feature, values: [], is_array: true}

case Provider.create_characteristic(%{name: name, value: value, type: :feature}) do
module ->
%{name: name, type: :feature, value: Value.dynamic(struct(module))}
end

case Provider.create_characteristic(attrs) do
{:ok, result} ->
{:cont, [result.id | acc]}

Expand Down
6 changes: 6 additions & 0 deletions lib/diffo/type/value.ex
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ defmodule Diffo.Type.Value do
def handle_change(_old_value, nil, _constraints), do: {:ok, nil}
def handle_change(old_value, new_value, constraints), do: super(old_value, new_value, constraints)

def handle_change_array(_old_values, nil, _constraints), do: {:ok, nil}
def handle_change_array(old_values, new_values, constraints), do: super(old_values, new_values, constraints)

def prepare_change_array(_old_values, nil, _constraints), do: {:ok, nil}
def prepare_change_array(old_values, new_values, constraints), do: super(old_values, new_values, constraints)

def primitive(type, value), do: Diffo.Type.Primitive.wrap(type, value)

def dynamic(%type{} = dynamic), do: dynamic(type, dynamic)
Expand Down
9 changes: 9 additions & 0 deletions test/instance_extension/characteristic_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@ defmodule Diffo.InstanceExtension.CharacteristicTest do
assert hd(errors).message ==
"couldn't create characteristic with value of unknown type Elixir.InvalidValue"
end

test "create resource with array characteristic - success" do
{:ok, shelf} = Servo.build_shelf(%{})

shelves = Enum.find(shelf.characteristics, fn c -> c.name == :shelves end)
assert shelves.is_array == true
assert shelves.values == []
assert Diffo.Unwrap.unwrap(shelves) == []
end
end
end
11 changes: 11 additions & 0 deletions test/instance_extension/feature_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@ defmodule Diffo.InstanceExtension.FeatureTest do
assert hd(errors).message ==
"couldn't create feature characteristic with value of unknown type Elixir.InvalidValue"
end

test "create resource with array feature characteristic - success" do
{:ok, shelf} = Servo.build_shelf(%{})

spectral = Enum.find(shelf.features, fn f -> f.name == :spectralManagement end)
deployment_classes = Enum.find(spectral.characteristics, fn c -> c.name == :deploymentClasses end)

assert deployment_classes.is_array == true
assert deployment_classes.values == []
assert Diffo.Unwrap.unwrap(deployment_classes) == []
end
end
end
88 changes: 87 additions & 1 deletion test/provider/characteristic_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,81 @@ defmodule Diffo.Provider.CharacteristicTest do
end
end

describe "Diffo.Provider create array Characteristics" do
test "create characteristic with values - success" do
characteristic =
Diffo.Provider.create_characteristic!(%{
name: :ports,
values: [
Value.primitive("integer", 1),
Value.primitive("integer", 2),
Value.primitive("integer", 3)
],
is_array: true,
type: :instance
})

assert characteristic.is_array == true
assert Diffo.Unwrap.unwrap(characteristic) == [1, 2, 3]
end

test "create characteristic with both value and values - failure" do
assert {:error, _} =
Diffo.Provider.create_characteristic(%{
name: :bad,
value: Value.primitive("string", "x"),
values: [Value.primitive("string", "y")],
type: :instance
})
end
end

describe "Diffo.Provider update array Characteristics" do
test "update value characteristic to values (morphing) - success" do
characteristic =
Diffo.Provider.create_characteristic!(%{
name: :ports,
value: Value.primitive("integer", 1),
type: :instance
})

updated =
Diffo.Provider.update_characteristic!(characteristic, %{
value: nil,
values: [
Value.primitive("integer", 1),
Value.primitive("integer", 2)
],
is_array: true
})

assert updated.is_array == true
assert Diffo.Unwrap.unwrap(updated) == [1, 2]
end

test "update values characteristic back to value (shrinking) - success" do
characteristic =
Diffo.Provider.create_characteristic!(%{
name: :ports,
values: [Value.primitive("integer", 1), Value.primitive("integer", 2)],
is_array: true,
type: :instance
})

updated =
Diffo.Provider.update_characteristic!(characteristic, %{
values: nil,
value: Value.primitive("integer", 1),
is_array: false
})

assert updated.is_array == false
assert Diffo.Unwrap.unwrap(updated) == 1
end
end

describe "Diffo.Provider encode Characteristics" do
test "encode json - success" do
test "encode json value - success" do
characteristic =
Diffo.Provider.create_characteristic!(%{
name: :device,
Expand All @@ -218,6 +291,19 @@ defmodule Diffo.Provider.CharacteristicTest do
encoding = Jason.encode!(characteristic)
assert encoding == "{\"name\":\"device\",\"value\":\"managed\"}"
end

test "encode json values - success" do
characteristic =
Diffo.Provider.create_characteristic!(%{
name: :ports,
values: [Value.primitive("integer", 1), Value.primitive("integer", 2)],
is_array: true,
type: :instance
})

encoding = Jason.encode!(characteristic)
assert encoding == "{\"name\":\"ports\",\"values\":[1,2]}"
end
end

describe "Diffo.Provider outstanding Characteristics" do
Expand Down
2 changes: 2 additions & 0 deletions test/support/resource/shelf.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ defmodule Diffo.Test.Shelf do
feature :spectralManagement do
is_enabled? true
characteristic :deploymentClass, DeploymentClassValue
characteristic :deploymentClasses, {:array, DeploymentClassValue}
end
end

characteristics do
characteristic :shelf, ShelfValue
characteristic :slots, AssignableValue
characteristic :shelves, {:array, ShelfValue}
end

actions do
Expand Down
Loading