Skip to content

Commit ec3fa07

Browse files
committed
feat: groupByWithDefault template function
1 parent cc8ff2a commit ec3fa07

File tree

4 files changed

+58
-70
lines changed

4 files changed

+58
-70
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,7 @@ For example, this is a JSON version of an emitted RuntimeContainer struct:
382382
- _`exists $path`_: Returns `true` if `$path` refers to an existing file or directory. Takes a string.
383383
- _`eval $templateName [$data]`_: Evaluates the named template like Go's built-in `template` action, but instead of writing out the result it returns the result as a string so that it can be post-processed. The `$data` argument may be omitted, which is equivalent to passing `nil`.
384384
- _`groupBy $containers $fieldPath`_: Groups an array of `RuntimeContainer` instances based on the values of a field path expression `$fieldPath`. A field path expression is a dot-delimited list of map keys or struct member names specifying the path from container to a nested value, which must be a string. Returns a map from the value of the field path expression to an array of containers having that value. Containers that do not have a value for the field path in question are omitted.
385+
- _`groupByWithDefault $containers $fieldPath $defaultValue`_: Returns the same as `groupBy`, but containers that do not have a value for the field path are instead included in the map under the `$defaultValue` key.
385386
- _`groupByKeys $containers $fieldPath`_: Returns the same as `groupBy` but only returns the keys of the map.
386387
- _`groupByMulti $containers $fieldPath $sep`_: Like `groupBy`, but the string value specified by `$fieldPath` is first split by `$sep` into a list of strings. A container whose `$fieldPath` value contains a list of strings will show up in the map output under each of those strings.
387388
- _`groupByLabel $containers $label`_: Returns the same as `groupBy` but grouping by the given label's value.

internal/template/groupby.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,20 @@ func groupBy(entries interface{}, key string) (map[string][]interface{}, error)
5252
})
5353
}
5454

55+
// groupByWithDefault is the same as groupBy but allows a default value to be set
56+
func groupByWithDefault(entries interface{}, key string, defaultValue string) (map[string][]interface{}, error) {
57+
getValueWithDefault := func(v interface{}) (interface{}, error) {
58+
value := deepGet(v, key)
59+
if value == nil {
60+
return defaultValue, nil
61+
}
62+
return value, nil
63+
}
64+
return generalizedGroupBy("groupByWithDefault", entries, getValueWithDefault, func(groups map[string][]interface{}, value interface{}, v interface{}) {
65+
groups[value.(string)] = append(groups[value.(string)], v)
66+
})
67+
}
68+
5569
// groupByKeys is the same as groupBy but only returns a list of keys
5670
func groupByKeys(entries interface{}, key string) ([]string, error) {
5771
keys, err := generalizedGroupByKey("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {

internal/template/groupby_test.go

Lines changed: 42 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,34 @@ import (
77
"github.com/stretchr/testify/assert"
88
)
99

10-
func TestGroupByExistingKey(t *testing.T) {
11-
containers := []*context.RuntimeContainer{
12-
{
13-
Env: map[string]string{
14-
"VIRTUAL_HOST": "demo1.localhost",
15-
},
16-
ID: "1",
17-
},
18-
{
19-
Env: map[string]string{
20-
"VIRTUAL_HOST": "demo1.localhost",
21-
},
22-
ID: "2",
23-
},
24-
{
25-
Env: map[string]string{
26-
"VIRTUAL_HOST": "demo2.localhost",
27-
},
28-
ID: "3",
29-
},
30-
}
10+
var groupByContainers = []*context.RuntimeContainer{
11+
{
12+
Env: map[string]string{
13+
"VIRTUAL_HOST": "demo1.localhost",
14+
"EXTERNAL": "true",
15+
},
16+
ID: "1",
17+
},
18+
{
19+
Env: map[string]string{
20+
"VIRTUAL_HOST": "demo1.localhost",
21+
},
22+
ID: "2",
23+
},
24+
{
25+
Env: map[string]string{
26+
"VIRTUAL_HOST": "demo2.localhost",
27+
"EXTERNAL": "true",
28+
},
29+
ID: "3",
30+
},
31+
{
32+
ID: "4",
33+
},
34+
}
3135

32-
groups, err := groupBy(containers, "Env.VIRTUAL_HOST")
36+
func TestGroupByExistingKey(t *testing.T) {
37+
groups, err := groupBy(groupByContainers, "Env.VIRTUAL_HOST")
3338

3439
assert.NoError(t, err)
3540
assert.Len(t, groups, 2)
@@ -39,30 +44,7 @@ func TestGroupByExistingKey(t *testing.T) {
3944
}
4045

4146
func TestGroupByAfterWhere(t *testing.T) {
42-
containers := []*context.RuntimeContainer{
43-
{
44-
Env: map[string]string{
45-
"VIRTUAL_HOST": "demo1.localhost",
46-
"EXTERNAL": "true",
47-
},
48-
ID: "1",
49-
},
50-
{
51-
Env: map[string]string{
52-
"VIRTUAL_HOST": "demo1.localhost",
53-
},
54-
ID: "2",
55-
},
56-
{
57-
Env: map[string]string{
58-
"VIRTUAL_HOST": "demo2.localhost",
59-
"EXTERNAL": "true",
60-
},
61-
ID: "3",
62-
},
63-
}
64-
65-
filtered, _ := where(containers, "Env.EXTERNAL", "true")
47+
filtered, _ := where(groupByContainers, "Env.EXTERNAL", "true")
6648
groups, err := groupBy(filtered, "Env.VIRTUAL_HOST")
6749

6850
assert.NoError(t, err)
@@ -72,35 +54,25 @@ func TestGroupByAfterWhere(t *testing.T) {
7254
assert.Equal(t, "3", groups["demo2.localhost"][0].(*context.RuntimeContainer).ID)
7355
}
7456

75-
func TestGroupByKeys(t *testing.T) {
76-
containers := []*context.RuntimeContainer{
77-
{
78-
Env: map[string]string{
79-
"VIRTUAL_HOST": "demo1.localhost",
80-
},
81-
ID: "1",
82-
},
83-
{
84-
Env: map[string]string{
85-
"VIRTUAL_HOST": "demo1.localhost",
86-
},
87-
ID: "2",
88-
},
89-
{
90-
Env: map[string]string{
91-
"VIRTUAL_HOST": "demo2.localhost",
92-
},
93-
ID: "3",
94-
},
95-
}
57+
func TestGroupByWithDefault(t *testing.T) {
58+
groups, err := groupByWithDefault(groupByContainers, "Env.VIRTUAL_HOST", "default.localhost")
9659

60+
assert.NoError(t, err)
61+
assert.Len(t, groups, 3)
62+
assert.Len(t, groups["demo1.localhost"], 2)
63+
assert.Len(t, groups["demo2.localhost"], 1)
64+
assert.Len(t, groups["default.localhost"], 1)
65+
assert.Equal(t, "4", groups["default.localhost"][0].(*context.RuntimeContainer).ID)
66+
}
67+
68+
func TestGroupByKeys(t *testing.T) {
9769
expected := []string{"demo1.localhost", "demo2.localhost"}
98-
groups, err := groupByKeys(containers, "Env.VIRTUAL_HOST")
70+
groups, err := groupByKeys(groupByContainers, "Env.VIRTUAL_HOST")
9971
assert.NoError(t, err)
10072
assert.ElementsMatch(t, expected, groups)
10173

102-
expected = []string{"1", "2", "3"}
103-
groups, err = groupByKeys(containers, "ID")
74+
expected = []string{"1", "2", "3", "4"}
75+
groups, err = groupByKeys(groupByContainers, "ID")
10476
assert.NoError(t, err)
10577
assert.ElementsMatch(t, expected, groups)
10678
}

internal/template/template.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ func newTemplate(name string) *template.Template {
6565
"eval": eval,
6666
"exists": utils.PathExists,
6767
"groupBy": groupBy,
68+
"groupByWithDefault": groupByWithDefault,
6869
"groupByKeys": groupByKeys,
6970
"groupByMulti": groupByMulti,
7071
"groupByLabel": groupByLabel,

0 commit comments

Comments
 (0)