diff --git a/data_test.go b/data_test.go index d4f2000d1..965a2494d 100644 --- a/data_test.go +++ b/data_test.go @@ -130,13 +130,13 @@ func TestDatasource(t *testing.T) { data := &Data{ Sources: sources, } - expected := map[string]interface{}{"hello": "world"} + expected := map[string]interface{}{"hello": map[interface{}]interface{}{"cruel": "world"}} actual := data.Datasource("foo") - assert.Equal(t, expected["hello"], actual["hello"]) + assert.Equal(t, expected, actual) } - test("json", "application/json", `{"hello":"world"}`) - test("yml", "application/yaml", `hello: world`) + test("json", "application/json", `{"hello":{"cruel":"world"}}`) + test("yml", "application/yaml", "hello:\n cruel: world\n") } func TestDatasourceExists(t *testing.T) { diff --git a/gomplate_test.go b/gomplate_test.go index e35e66606..1d55751b6 100644 --- a/gomplate_test.go +++ b/gomplate_test.go @@ -125,8 +125,9 @@ func TestHasTemplate(t *testing.T) { "has": ty.Has, }, } - assert.Equal(t, "true", testTemplate(g, `{{has ("foo: true" | yaml) "foo"}}`)) - assert.Equal(t, "false", testTemplate(g, `{{has ("foo: true" | yaml) "bar"}}`)) + assert.Equal(t, "true", testTemplate(g, `{{has ("foo:\n bar: true" | yaml) "foo"}}`)) + assert.Equal(t, "true", testTemplate(g, `{{has ("foo:\n bar: true" | yaml).foo "bar"}}`)) + assert.Equal(t, "false", testTemplate(g, `{{has ("foo: true" | yaml) "bah"}}`)) tmpl := `{{- $data := yaml "foo: bar\nbaz: qux\n" }} {{- if (has $data "baz") }} {{- $data.baz }} diff --git a/test/integration/datasources_file.bats b/test/integration/datasources_file.bats index 1a3ad8242..9e22a834f 100644 --- a/test/integration/datasources_file.bats +++ b/test/integration/datasources_file.bats @@ -13,17 +13,17 @@ function teardown () { } @test "supports json datasource file" { - echo '{"foo": "bar"}' > $tmpdir/config.json - gomplate -d config=$tmpdir/config.json -i '{{(datasource "config").foo}}' + echo '{"foo": {"bar": "baz"}}' > $tmpdir/config.json + gomplate -d config=$tmpdir/config.json -i '{{(datasource "config").foo.bar}}' [ "$status" -eq 0 ] - [[ "${output}" == "bar" ]] + [[ "${output}" == "baz" ]] } @test "supports YAML datasource file" { - echo 'foo: bar' > $tmpdir/config.yml - gomplate -d config=$tmpdir/config.yml -i '{{(datasource "config").foo}}' + echo -e 'foo:\n bar: baz' > $tmpdir/config.yml + gomplate -d config=$tmpdir/config.yml -i '{{(datasource "config").foo.bar}}' [ "$status" -eq 0 ] - [[ "${output}" == "bar" ]] + [[ "${output}" == "baz" ]] } @test "ds alias" { diff --git a/test/integration/typeconv_funcs.bats b/test/integration/typeconv_funcs.bats new file mode 100644 index 000000000..9127915e7 --- /dev/null +++ b/test/integration/typeconv_funcs.bats @@ -0,0 +1,19 @@ +#!/usr/bin/env bats + +load helper + +tmpdir=$(mktemp -u) + +function setup () { + mkdir -p $tmpdir +} + +function teardown () { + rm -rf $tmpdir || true +} + +@test "'has' can handle sub-maps in nested maps" { + gomplate -d config=$tmpdir/config.yml -i '{{ has ("foo:\n bar:\n baz: qux" | yaml).foo.bar "baz"}}' + [ "$status" -eq 0 ] + [[ "${output}" == "true" ]] +} diff --git a/typeconv.go b/typeconv.go index 273486996..8995de8a9 100644 --- a/typeconv.go +++ b/typeconv.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "log" + "reflect" "strconv" "strings" @@ -43,13 +44,13 @@ func unmarshalArray(obj []interface{}, in string, f func([]byte, interface{}) er // JSON - Unmarshal a JSON Object func (t *TypeConv) JSON(in string) map[string]interface{} { obj := make(map[string]interface{}) - return unmarshalObj(obj, in, json.Unmarshal) + return unmarshalObj(obj, in, yaml.Unmarshal) } // JSONArray - Unmarshal a JSON Array func (t *TypeConv) JSONArray(in string) []interface{} { obj := make([]interface{}, 1) - return unmarshalArray(obj, in, json.Unmarshal) + return unmarshalArray(obj, in, yaml.Unmarshal) } // YAML - Unmarshal a YAML Object @@ -102,9 +103,15 @@ func (t *TypeConv) Join(a []interface{}, sep string) string { } // Has determines whether or not a given object has a property with the given key -func (t *TypeConv) Has(in map[string]interface{}, key string) bool { - _, ok := in[key] - return ok +func (t *TypeConv) Has(in interface{}, key string) bool { + av := reflect.ValueOf(in) + kv := reflect.ValueOf(key) + + if av.Kind() == reflect.Map { + return av.MapIndex(kv).IsValid() + } + + return false } func toString(in interface{}) string { diff --git a/typeconv_test.go b/typeconv_test.go index 7f07d08e3..ca88c79f5 100644 --- a/typeconv_test.go +++ b/typeconv_test.go @@ -27,7 +27,7 @@ func TestBool(t *testing.T) { func TestUnmarshalObj(t *testing.T) { ty := new(TypeConv) expected := map[string]interface{}{ - "foo": "bar", + "foo": map[interface{}]interface{}{"bar": "baz"}, "one": 1.0, "true": true, } @@ -37,8 +37,9 @@ func TestUnmarshalObj(t *testing.T) { assert.Equal(t, expected["one"], actual["one"]) assert.Equal(t, expected["true"], actual["true"]) } - test(ty.JSON(`{"foo":"bar","one":1.0,"true":true}`)) - test(ty.YAML(`foo: bar + test(ty.JSON(`{"foo":{"bar":"baz"},"one":1.0,"true":true}`)) + test(ty.YAML(`foo: + bar: baz one: 1.0 true: true `)) @@ -130,8 +131,12 @@ func TestHas(t *testing.T) { in := map[string]interface{}{ "foo": "bar", + "baz": map[string]interface{}{ + "qux": "quux", + }, } assert.True(t, ty.Has(in, "foo")) assert.False(t, ty.Has(in, "bar")) + assert.True(t, ty.Has(in["baz"], "qux")) }