Skip to content

Commit 8fc1323

Browse files
authored
Merge pull request #27 from redwirelabs/attribute-merge-strategy
Attribute merge strategy
2 parents 3e21393 + 64b1aba commit 8fc1323

File tree

2 files changed

+85
-12
lines changed

2 files changed

+85
-12
lines changed

lib/speck/validation_metadata/attribute.ex

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,38 +19,60 @@ defmodule Speck.ValidationMetadata.Attribute do
1919

2020
@doc """
2121
Merge the values from metadata attributes into a given map.
22+
23+
## Opts
24+
- `:merge_strategy` - Determines whether a value in the params or attributes
25+
takes priority when both are present. Defaults to `:param_priority`.
2226
"""
23-
@spec merge(attributes :: [t], params :: map) :: map
24-
def merge(attributes, params) do
27+
@spec merge(
28+
attributes :: [t],
29+
params :: map,
30+
opts :: [merge_strategy: :param_priority | :attribute_priority]
31+
) :: map
32+
def merge(attributes, params, opts \\ []) do
33+
merge_strategy = Keyword.get(opts, :merge_strategy, :param_priority)
34+
2535
Enum.reduce(attributes, params, fn {path, _status, value}, acc ->
26-
merge(acc, path, value)
36+
merge(acc, path, value, merge_strategy)
2737
end)
2838
end
2939

30-
defp merge(nil = _params, [path], value) do
40+
defp merge(nil = _params, [path], value, _strategy) do
3141
%{path => value}
3242
end
3343

34-
defp merge(params, [path], value) when is_map(params) do
35-
params
36-
|> to_key_strings()
37-
|> Map.put(path, value)
44+
defp merge(nil = _params, [attribute | path], value, strategy)
45+
when not is_integer(attribute) do
46+
%{attribute => merge(%{}, path, value, strategy)}
47+
end
48+
49+
defp merge(params, [path], value, strategy) when is_map(params) do
50+
params_with_key_strings = to_key_strings(params)
51+
52+
skip_put =
53+
strategy == :param_priority
54+
&& Map.has_key?(params_with_key_strings, path)
55+
56+
case skip_put do
57+
true -> params_with_key_strings
58+
_ -> Map.put(params_with_key_strings, path, value)
59+
end
3860
end
3961

40-
defp merge(params, [index | path], value) when is_integer(index) do
62+
defp merge(params, [index | path], value, strategy) when is_integer(index) do
4163
params2 = params || []
4264
item = Enum.at(params2, index)
43-
new_item = merge(item, path, value)
65+
new_item = merge(item, path, value, strategy)
4466

4567
case item do
4668
nil -> List.insert_at(params2, -1, new_item)
4769
_ -> List.replace_at(params2, index, new_item)
4870
end
4971
end
5072

51-
defp merge(params, [attribute | path], value) do
73+
defp merge(params, [attribute | path], value, strategy) do
5274
params2 = to_key_strings(params)
53-
Map.put(params2, attribute, merge(params2[attribute], path, value))
75+
Map.put(params2, attribute, merge(params2[attribute], path, value, strategy))
5476
end
5577

5678
defp to_key_strings(map) do

test/validation_metadata/attribute_test.exs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,57 @@ defmodule Speck.ValidationMetadata.Attribute.Test do
33

44
alias Speck.ValidationMetadata.Attribute
55

6+
test "merge adds nested maps that don't exist" do
7+
attributes = [
8+
{["state", "reported", "serial"], :present, "sn1234"}
9+
]
10+
11+
assert Attribute.merge(attributes, %{}) ==
12+
%{"state" => %{"reported" => %{"serial" => "sn1234"}}}
13+
end
14+
15+
describe "merge strategy" do
16+
test "attribute priority" do
17+
params = %{"name" => "Test Device"}
18+
19+
attributes = [
20+
{["name"], :present, "My Device"}
21+
]
22+
23+
assert Attribute.merge(attributes, params, merge_strategy: :attribute_priority) ==
24+
%{"name" => "My Device"}
25+
26+
params = %{"state" => %{"reported" => %{"name" => "Test Device"}}}
27+
28+
attributes = [
29+
{["state", "reported", "name"], :present, "My Device"}
30+
]
31+
32+
assert Attribute.merge(attributes, params, merge_strategy: :attribute_priority) ==
33+
%{"state" => %{"reported" => %{"name" => "My Device"}}}
34+
end
35+
36+
test "param priority" do
37+
params = %{"name" => "Test Device"}
38+
39+
attributes = [
40+
{["name"], :present, "My Device"}
41+
]
42+
43+
assert Attribute.merge(attributes, params, merge_strategy: :param_priority) ==
44+
%{"name" => "Test Device"}
45+
46+
params = %{"state" => %{"reported" => %{"name" => "Test Device"}}}
47+
48+
attributes = [
49+
{["state", "reported", "name"], :present, "My Device"}
50+
]
51+
52+
assert Attribute.merge(attributes, params, merge_strategy: :param_priority) ==
53+
%{"state" => %{"reported" => %{"name" => "Test Device"}}}
54+
end
55+
end
56+
657
describe "use case" do
758
test "can merge unknown attributes back into a device shadow" do
859
shadow_reported = %{

0 commit comments

Comments
 (0)