Skip to content

Commit

Permalink
feat: groupByWithDefault template function
Browse files Browse the repository at this point in the history
  • Loading branch information
buchdag committed May 5, 2024
1 parent cc8ff2a commit ec3fa07
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 70 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ For example, this is a JSON version of an emitted RuntimeContainer struct:
- _`exists $path`_: Returns `true` if `$path` refers to an existing file or directory. Takes a string.
- _`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`.
- _`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.
- _`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.
- _`groupByKeys $containers $fieldPath`_: Returns the same as `groupBy` but only returns the keys of the map.
- _`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.
- _`groupByLabel $containers $label`_: Returns the same as `groupBy` but grouping by the given label's value.
Expand Down
14 changes: 14 additions & 0 deletions internal/template/groupby.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,20 @@ func groupBy(entries interface{}, key string) (map[string][]interface{}, error)
})
}

// groupByWithDefault is the same as groupBy but allows a default value to be set
func groupByWithDefault(entries interface{}, key string, defaultValue string) (map[string][]interface{}, error) {
getValueWithDefault := func(v interface{}) (interface{}, error) {
value := deepGet(v, key)
if value == nil {
return defaultValue, nil
}
return value, nil
}
return generalizedGroupBy("groupByWithDefault", entries, getValueWithDefault, func(groups map[string][]interface{}, value interface{}, v interface{}) {
groups[value.(string)] = append(groups[value.(string)], v)
})
}

// groupByKeys is the same as groupBy but only returns a list of keys
func groupByKeys(entries interface{}, key string) ([]string, error) {
keys, err := generalizedGroupByKey("groupByKeys", entries, key, func(groups map[string][]interface{}, value interface{}, v interface{}) {
Expand Down
112 changes: 42 additions & 70 deletions internal/template/groupby_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,34 @@ import (
"github.com/stretchr/testify/assert"
)

func TestGroupByExistingKey(t *testing.T) {
containers := []*context.RuntimeContainer{
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "1",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "2",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo2.localhost",
},
ID: "3",
},
}
var groupByContainers = []*context.RuntimeContainer{
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
"EXTERNAL": "true",
},
ID: "1",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "2",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo2.localhost",
"EXTERNAL": "true",
},
ID: "3",
},
{
ID: "4",
},
}

groups, err := groupBy(containers, "Env.VIRTUAL_HOST")
func TestGroupByExistingKey(t *testing.T) {
groups, err := groupBy(groupByContainers, "Env.VIRTUAL_HOST")

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

func TestGroupByAfterWhere(t *testing.T) {
containers := []*context.RuntimeContainer{
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
"EXTERNAL": "true",
},
ID: "1",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "2",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo2.localhost",
"EXTERNAL": "true",
},
ID: "3",
},
}

filtered, _ := where(containers, "Env.EXTERNAL", "true")
filtered, _ := where(groupByContainers, "Env.EXTERNAL", "true")
groups, err := groupBy(filtered, "Env.VIRTUAL_HOST")

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

func TestGroupByKeys(t *testing.T) {
containers := []*context.RuntimeContainer{
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "1",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo1.localhost",
},
ID: "2",
},
{
Env: map[string]string{
"VIRTUAL_HOST": "demo2.localhost",
},
ID: "3",
},
}
func TestGroupByWithDefault(t *testing.T) {
groups, err := groupByWithDefault(groupByContainers, "Env.VIRTUAL_HOST", "default.localhost")

assert.NoError(t, err)
assert.Len(t, groups, 3)
assert.Len(t, groups["demo1.localhost"], 2)
assert.Len(t, groups["demo2.localhost"], 1)
assert.Len(t, groups["default.localhost"], 1)
assert.Equal(t, "4", groups["default.localhost"][0].(*context.RuntimeContainer).ID)
}

func TestGroupByKeys(t *testing.T) {
expected := []string{"demo1.localhost", "demo2.localhost"}
groups, err := groupByKeys(containers, "Env.VIRTUAL_HOST")
groups, err := groupByKeys(groupByContainers, "Env.VIRTUAL_HOST")
assert.NoError(t, err)
assert.ElementsMatch(t, expected, groups)

expected = []string{"1", "2", "3"}
groups, err = groupByKeys(containers, "ID")
expected = []string{"1", "2", "3", "4"}
groups, err = groupByKeys(groupByContainers, "ID")
assert.NoError(t, err)
assert.ElementsMatch(t, expected, groups)
}
Expand Down
1 change: 1 addition & 0 deletions internal/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ func newTemplate(name string) *template.Template {
"eval": eval,
"exists": utils.PathExists,
"groupBy": groupBy,
"groupByWithDefault": groupByWithDefault,
"groupByKeys": groupByKeys,
"groupByMulti": groupByMulti,
"groupByLabel": groupByLabel,
Expand Down

0 comments on commit ec3fa07

Please sign in to comment.