Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: slice of structs #15

Merged
merged 2 commits into from
Jul 24, 2023
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
2 changes: 1 addition & 1 deletion cmd/gencel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// Not really a test but just a runner so it's easier to attach a debugger.
func TestGencel(t *testing.T) {
func testGencel(t *testing.T) {
wd, _ := os.Getwd()
fmt.Printf("WD: %s", wd)

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ require (
github.com/Masterminds/goutils v1.1.1
github.com/Masterminds/semver/v3 v3.2.1
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc
github.com/flanksource/mapstructure v1.6.0
github.com/google/cel-go v0.17.1
github.com/google/uuid v1.3.0
github.com/gosimple/slug v1.13.1
github.com/hairyhenderson/toml v0.4.2-0.20210923231440-40456b8e66cf
github.com/itchyny/gojq v0.12.13
github.com/mitchellh/mapstructure v1.5.0
github.com/pkg/errors v0.9.1
github.com/robertkrimen/otto v0.2.1
github.com/stretchr/testify v1.8.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc h1:CPUNUw2pHnlF4ucBHx44vLTcCa4FlEEu6PkNo5rCvD4=
github.com/flanksource/is-healthy v0.0.0-20230705092916-3b4cf510c5fc/go.mod h1:4pQhmF+TnVqJroQKY8wSnSp+T18oLson6YQ2M0qPHfQ=
github.com/flanksource/mapstructure v1.6.0 h1:+1kJ+QsO1SxjAgktfLlpZXetsVSJ0uCLhGKrA4BtwTE=
github.com/flanksource/mapstructure v1.6.0/go.mod h1:dttg5+FFE2sp4D/CrcPCVqufNDrBggDaM+08nk5S8Ps=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
Expand Down Expand Up @@ -44,8 +46,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
Expand Down
52 changes: 37 additions & 15 deletions template.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
"github.com/flanksource/gomplate/v3/funcs"
_ "github.com/flanksource/gomplate/v3/js"
pkgStrings "github.com/flanksource/gomplate/v3/strings"
"github.com/flanksource/mapstructure"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/ext"
"github.com/mitchellh/mapstructure"
"github.com/robertkrimen/otto"
"github.com/robertkrimen/otto/registry"
_ "github.com/robertkrimen/otto/underscore"
Expand Down Expand Up @@ -114,7 +114,7 @@ func RunTemplate(environment map[string]any, template Template) (string, error)

out, _, err := prg.Eval(data)
if err != nil {
return "", err
return "", fmt.Errorf("error evaluating expression %s: %v", template.Expression, err)
}

return fmt.Sprintf("%v", out.Value()), nil
Expand Down Expand Up @@ -145,22 +145,44 @@ func serialize(in map[string]any) (map[string]any, error) {

newMap := make(map[string]any, len(in))
for k, v := range in {
if reflect.ValueOf(v).Kind() != reflect.Struct {
newMap[k] = v
continue
}
var dec *mapstructure.Decoder
var err error

vt := reflect.TypeOf(v)
switch vt.Kind() {
case reflect.Struct:
var result map[string]any
dec, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &result, Squash: true, Deep: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}

var vMap map[string]any
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &vMap, Squash: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}
if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}

if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}
newMap[k] = result

newMap[k] = vMap
case reflect.Slice:
var result any
if vt.Elem().Kind() == reflect.Struct {
result = make([]map[string]any, 0)
}

dec, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{TagName: "json", Result: &result, Squash: true, Deep: true})
if err != nil {
return nil, fmt.Errorf("error creating new mapstructure decoder: %w", err)
}
if err := dec.Decode(v); err != nil {
return nil, fmt.Errorf("error decoding %T to map[string]any: %w", v, err)
}

newMap[k] = result

default:
newMap[k] = v
continue
}
}

return newMap, nil
Expand Down
100 changes: 94 additions & 6 deletions template_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gomplate

import (
"reflect"
"testing"
"time"

Expand All @@ -10,22 +11,28 @@ import (
"github.com/stretchr/testify/assert"
)

type NoStructTag struct {
Name string
UPPER string
}

type Address struct {
City string `json:"city_name"`
}

type Person struct {
Name string `json:"name"`
Address Address
MetaData map[string]any
Codes []string
Name string `json:"name"`
Address *Address `json:",omitempty"`
MetaData map[string]any `json:",omitempty"`
Codes []string `json:",omitempty"`
Addresses []Address `json:"addresses,omitempty"`
}

// A shared test data for all template test
var structEnv = map[string]any{
"results": Person{
Name: "Aditya",
Address: Address{
Address: &Address{
City: "Kathmandu",
},
},
Expand Down Expand Up @@ -58,6 +65,7 @@ var junitEnv = JunitTestSuites{
Totals: Totals{
Passed: 1,
},
Suites: []JunitTestSuite{{Name: "hi", Totals: Totals{Failed: 2}}},
}

type SQLDetails struct {
Expand Down Expand Up @@ -112,7 +120,11 @@ func TestGomplate(t *testing.T) {
{map[string]interface{}{"old": "1.2.3", "new": "1.2.3"}, "{{ .old | semverCompare .new }}", "true"},
{map[string]interface{}{"old": "1.2.3", "new": "1.2.4"}, "{{ .old | semverCompare .new }}", "false"},
{structEnv, `{{.results.name}} {{.results.Address.city_name}}`, "Aditya Kathmandu"},
{map[string]any{"results": junitEnv}, `{{.results.passed}}`, "1"},
{
map[string]any{"results": junitEnv},
`{{.results.passed}}{{ range $r := .results.suites}}{{$r.name}} ✅ {{$r.passed}} ❌ {{$r.failed}} in 🕑 {{$r.duration}}{{end}}`,
"1hi ✅ 0 ❌ 2 in 🕑 0",
},
{
map[string]any{
"results": SQLDetails{
Expand Down Expand Up @@ -184,3 +196,79 @@ func TestCel(t *testing.T) {
})
}
}

func Test_serialize(t *testing.T) {
tests := []struct {
name string
in map[string]any
want map[string]any
wantErr bool
}{
{name: "nil", in: nil, want: nil, wantErr: false},
{name: "empty", in: map[string]any{}, want: map[string]any{}, wantErr: false},
{
name: "simple - no struct tags",
in: map[string]any{"r": NoStructTag{Name: "Kathmandu", UPPER: "u"}},
want: map[string]any{"r": map[string]any{"Name": "Kathmandu", "UPPER": "u"}},
wantErr: false,
},
{name: "simple - struct tags", in: map[string]any{"r": Address{City: "Kathmandu"}}, want: map[string]any{"r": map[string]any{"city_name": "Kathmandu"}}, wantErr: false},
{
name: "nested struct",
in: map[string]any{"r": Person{Name: "Aditya", Address: &Address{City: "Kathmandu"}}},
want: map[string]any{"r": map[string]any{"name": "Aditya", "Address": map[string]any{"city_name": "Kathmandu"}}},
wantErr: false,
},
{
name: "slice of struct",
in: map[string]any{
"r": []Address{
{City: "Kathmandu"},
{City: "Lalitpur"},
},
},
want: map[string]any{
"r": []map[string]any{
{"city_name": "Kathmandu"},
{"city_name": "Lalitpur"},
},
},
wantErr: false,
},
{
name: "nested slice of struct",
in: map[string]any{
"r": Person{
Name: "Aditya",
Addresses: []Address{
{City: "Kathmandu"},
{City: "Lalitpur"},
},
},
},
want: map[string]any{
"r": map[string]any{
"name": "Aditya",
"addresses": []map[string]any{
{"city_name": "Kathmandu"},
{"city_name": "Lalitpur"},
},
},
},
wantErr: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := serialize(tt.in)
if (err != nil) != tt.wantErr {
t.Errorf("serialize() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("serialize() = %v, want %v", got, tt.want)
}
})
}
}