Skip to content

Commit ed22fdb

Browse files
Merge pull request #75 from diffo-dev/69-homogenous-array-of-value
69 homogenous array of value
2 parents aed4c47 + ba09704 commit ed22fdb

10 files changed

Lines changed: 191 additions & 13 deletions

File tree

documentation/dsls/DSL-Diffo.Provider.Instance.Extension.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ Adds a Characteristic
110110
| Name | Type | Default | Docs |
111111
|------|------|---------|------|
112112
| [`name`](#features-feature-characteristic-name){: #features-feature-characteristic-name .spark-required} | `atom` | | The name of the characteristic, an atom |
113-
| [`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 |
113+
| [`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. |
114114

115115

116116

@@ -169,7 +169,7 @@ Adds a Characteristic
169169
| Name | Type | Default | Docs |
170170
|------|------|---------|------|
171171
| [`name`](#characteristics-characteristic-name){: #characteristics-characteristic-name .spark-required} | `atom` | | The name of the characteristic, an atom |
172-
| [`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 |
172+
| [`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. |
173173

174174

175175

lib/diffo/provider/components/characteristic.ex

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,45 @@ defmodule Diffo.Provider.Characteristic do
3434
end
3535

3636
jason do
37-
pick [:name, :value]
37+
pick [:name, :value, :values]
38+
compact true
3839
end
3940

4041
outstanding do
41-
expect [:name, :value]
42+
expect [:name]
43+
44+
customize fn outstanding, expected, actual ->
45+
key = if expected.is_array, do: :values, else: :value
46+
expected_val = Map.get(expected, key)
47+
48+
val_out =
49+
if actual == nil do
50+
expected_val
51+
else
52+
Outstanding.outstanding(expected_val, Map.get(actual, key))
53+
end
54+
55+
cond do
56+
val_out == nil ->
57+
outstanding
58+
59+
outstanding == nil ->
60+
%{key => val_out}
61+
|> Outstand.map_to_struct(Diffo.Provider.Characteristic)
62+
|> Ash.Test.strip_metadata()
63+
64+
true ->
65+
Map.put(outstanding, key, val_out)
66+
end
67+
end
4268
end
4369

4470
actions do
4571
defaults [:destroy]
4672

4773
create :create do
4874
description "creates a characteristic"
49-
accept [:name, :value, :type]
75+
accept [:name, :value, :values, :is_array, :type]
5076
end
5177

5278
read :read do
@@ -71,7 +97,7 @@ defmodule Diffo.Provider.Characteristic do
7197
update :update do
7298
primary? true
7399
description "updates the characteristic value or instance, feature or relationship"
74-
accept [:value]
100+
accept [:value, :values, :is_array]
75101
argument :instance_id, :uuid
76102
argument :feature_id, :uuid
77103
argument :relationship_id, :uuid
@@ -101,6 +127,20 @@ defmodule Diffo.Provider.Characteristic do
101127
public? true
102128
end
103129

130+
attribute :values, {:array, Diffo.Type.Value} do
131+
description "the array of values of the characteristic"
132+
constraints items: Diffo.Type.Value.subtype_constraints()
133+
allow_nil? true
134+
public? true
135+
end
136+
137+
attribute :is_array, :boolean do
138+
description "true when this characteristic holds an array of values; defaults false"
139+
default false
140+
allow_nil? false
141+
public? true
142+
end
143+
104144
attribute :type, :atom do
105145
description "the type of the characteristic"
106146
allow_nil? false
@@ -154,6 +194,10 @@ defmodule Diffo.Provider.Characteristic do
154194
validate present([:instance_id, :feature_id, :relationship_id], at_most: 1) do
155195
message "characteristic must be related to at most one of an instance, feature or relationship"
156196
end
197+
198+
validate present([:value, :values], at_most: 1) do
199+
message "characteristic must have at most one of value or values"
200+
end
157201
end
158202

159203
preparations do
@@ -172,4 +216,9 @@ defmodule Diffo.Provider.Characteristic do
172216
173217
"""
174218
def compare(%{name: name0}, %{name: name1}), do: Diffo.Util.compare(name0, name1)
219+
220+
defimpl Diffo.Unwrap do
221+
def unwrap(%{values: values}) when is_list(values), do: Diffo.Unwrap.unwrap(values)
222+
def unwrap(%{value: value}), do: Diffo.Unwrap.unwrap(value)
223+
end
175224
end

lib/diffo/provider/components/instance/extension.ex

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ defmodule Diffo.Provider.Instance.Extension do
8181
],
8282
value_type: [
8383
doc: """
84-
The optional type of the characteristic's value, an atom, may be a module name such as an Ash.TypedStruct
84+
The type of the characteristic's value. An atom module name such as an Ash.TypedStruct for a scalar value,
85+
or `{:array, module}` for an array of values of that type.
8586
""",
86-
type: :atom
87+
type: :any
8788
]
8889
]
8990
}

lib/diffo/provider/components/instance/extension/characteristic.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,16 @@ defmodule Diffo.Provider.Instance.Characteristic do
4848

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

53-
case Provider.create_characteristic(%{name: name, type: type, value: value}) do
56+
module ->
57+
%{name: name, type: type, value: Value.dynamic(struct(module))}
58+
end
59+
60+
case Provider.create_characteristic(attrs) do
5461
{:ok, result} ->
5562
{:cont, [result | acc]}
5663

lib/diffo/provider/components/instance/extension/feature.ex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,16 @@ defmodule Diffo.Provider.Instance.Feature do
5454
characteristic_ids =
5555
Enum.reduce_while(characteristics, [], fn %{name: name, value_type: value_type}, acc ->
5656
try do
57-
value = Value.dynamic(struct(value_type))
57+
attrs =
58+
case value_type do
59+
{:array, _inner} ->
60+
%{name: name, type: :feature, values: [], is_array: true}
5861

59-
case Provider.create_characteristic(%{name: name, value: value, type: :feature}) do
62+
module ->
63+
%{name: name, type: :feature, value: Value.dynamic(struct(module))}
64+
end
65+
66+
case Provider.create_characteristic(attrs) do
6067
{:ok, result} ->
6168
{:cont, [result.id | acc]}
6269

lib/diffo/type/value.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,12 @@ defmodule Diffo.Type.Value do
9292
def handle_change(_old_value, nil, _constraints), do: {:ok, nil}
9393
def handle_change(old_value, new_value, constraints), do: super(old_value, new_value, constraints)
9494

95+
def handle_change_array(_old_values, nil, _constraints), do: {:ok, nil}
96+
def handle_change_array(old_values, new_values, constraints), do: super(old_values, new_values, constraints)
97+
98+
def prepare_change_array(_old_values, nil, _constraints), do: {:ok, nil}
99+
def prepare_change_array(old_values, new_values, constraints), do: super(old_values, new_values, constraints)
100+
95101
def primitive(type, value), do: Diffo.Type.Primitive.wrap(type, value)
96102

97103
def dynamic(%type{} = dynamic), do: dynamic(type, dynamic)

test/instance_extension/characteristic_test.exs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,14 @@ defmodule Diffo.InstanceExtension.CharacteristicTest do
2525
assert hd(errors).message ==
2626
"couldn't create characteristic with value of unknown type Elixir.InvalidValue"
2727
end
28+
29+
test "create resource with array characteristic - success" do
30+
{:ok, shelf} = Servo.build_shelf(%{})
31+
32+
shelves = Enum.find(shelf.characteristics, fn c -> c.name == :shelves end)
33+
assert shelves.is_array == true
34+
assert shelves.values == []
35+
assert Diffo.Unwrap.unwrap(shelves) == []
36+
end
2837
end
2938
end

test/instance_extension/feature_test.exs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,16 @@ defmodule Diffo.InstanceExtension.FeatureTest do
2525
assert hd(errors).message ==
2626
"couldn't create feature characteristic with value of unknown type Elixir.InvalidValue"
2727
end
28+
29+
test "create resource with array feature characteristic - success" do
30+
{:ok, shelf} = Servo.build_shelf(%{})
31+
32+
spectral = Enum.find(shelf.features, fn f -> f.name == :spectralManagement end)
33+
deployment_classes = Enum.find(spectral.characteristics, fn c -> c.name == :deploymentClasses end)
34+
35+
assert deployment_classes.is_array == true
36+
assert deployment_classes.values == []
37+
assert Diffo.Unwrap.unwrap(deployment_classes) == []
38+
end
2839
end
2940
end

test/provider/characteristic_test.exs

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,81 @@ defmodule Diffo.Provider.CharacteristicTest do
206206
end
207207
end
208208

209+
describe "Diffo.Provider create array Characteristics" do
210+
test "create characteristic with values - success" do
211+
characteristic =
212+
Diffo.Provider.create_characteristic!(%{
213+
name: :ports,
214+
values: [
215+
Value.primitive("integer", 1),
216+
Value.primitive("integer", 2),
217+
Value.primitive("integer", 3)
218+
],
219+
is_array: true,
220+
type: :instance
221+
})
222+
223+
assert characteristic.is_array == true
224+
assert Diffo.Unwrap.unwrap(characteristic) == [1, 2, 3]
225+
end
226+
227+
test "create characteristic with both value and values - failure" do
228+
assert {:error, _} =
229+
Diffo.Provider.create_characteristic(%{
230+
name: :bad,
231+
value: Value.primitive("string", "x"),
232+
values: [Value.primitive("string", "y")],
233+
type: :instance
234+
})
235+
end
236+
end
237+
238+
describe "Diffo.Provider update array Characteristics" do
239+
test "update value characteristic to values (morphing) - success" do
240+
characteristic =
241+
Diffo.Provider.create_characteristic!(%{
242+
name: :ports,
243+
value: Value.primitive("integer", 1),
244+
type: :instance
245+
})
246+
247+
updated =
248+
Diffo.Provider.update_characteristic!(characteristic, %{
249+
value: nil,
250+
values: [
251+
Value.primitive("integer", 1),
252+
Value.primitive("integer", 2)
253+
],
254+
is_array: true
255+
})
256+
257+
assert updated.is_array == true
258+
assert Diffo.Unwrap.unwrap(updated) == [1, 2]
259+
end
260+
261+
test "update values characteristic back to value (shrinking) - success" do
262+
characteristic =
263+
Diffo.Provider.create_characteristic!(%{
264+
name: :ports,
265+
values: [Value.primitive("integer", 1), Value.primitive("integer", 2)],
266+
is_array: true,
267+
type: :instance
268+
})
269+
270+
updated =
271+
Diffo.Provider.update_characteristic!(characteristic, %{
272+
values: nil,
273+
value: Value.primitive("integer", 1),
274+
is_array: false
275+
})
276+
277+
assert updated.is_array == false
278+
assert Diffo.Unwrap.unwrap(updated) == 1
279+
end
280+
end
281+
209282
describe "Diffo.Provider encode Characteristics" do
210-
test "encode json - success" do
283+
test "encode json value - success" do
211284
characteristic =
212285
Diffo.Provider.create_characteristic!(%{
213286
name: :device,
@@ -218,6 +291,19 @@ defmodule Diffo.Provider.CharacteristicTest do
218291
encoding = Jason.encode!(characteristic)
219292
assert encoding == "{\"name\":\"device\",\"value\":\"managed\"}"
220293
end
294+
295+
test "encode json values - success" do
296+
characteristic =
297+
Diffo.Provider.create_characteristic!(%{
298+
name: :ports,
299+
values: [Value.primitive("integer", 1), Value.primitive("integer", 2)],
300+
is_array: true,
301+
type: :instance
302+
})
303+
304+
encoding = Jason.encode!(characteristic)
305+
assert encoding == "{\"name\":\"ports\",\"values\":[1,2]}"
306+
end
221307
end
222308

223309
describe "Diffo.Provider outstanding Characteristics" do

test/support/resource/shelf.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ defmodule Diffo.Test.Shelf do
4242
feature :spectralManagement do
4343
is_enabled? true
4444
characteristic :deploymentClass, DeploymentClassValue
45+
characteristic :deploymentClasses, {:array, DeploymentClassValue}
4546
end
4647
end
4748

4849
characteristics do
4950
characteristic :shelf, ShelfValue
5051
characteristic :slots, AssignableValue
52+
characteristic :shelves, {:array, ShelfValue}
5153
end
5254

5355
actions do

0 commit comments

Comments
 (0)