From 2adb96bdc7261389ddde0408fd9e6b689c73adc0 Mon Sep 17 00:00:00 2001 From: Dave Henderson Date: Mon, 6 Mar 2017 20:04:15 -0500 Subject: [PATCH] Adding 'has' func to determine if an object has a named key Signed-off-by: Dave Henderson --- README.md | 35 +++++++++++++++++++++++++++++++++++ main.go | 1 + main_test.go | 24 ++++++++++++++++++++++++ typeconv.go | 6 ++++++ typeconv_test.go | 11 +++++++++++ 5 files changed, 77 insertions(+) diff --git a/README.md b/README.md index 6969686b5..a9c6a41d3 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,8 @@ Gomplate is an alternative that will let you process templates which also includ - [Example](#example) - [`trim`](#trim) - [Example](#example) + - [`has`](#has) + - [Example](#example) - [`json`](#json) - [Example](#example) - [`jsonArray`](#jsonarray) @@ -389,6 +391,39 @@ $ FOO=" world " | gomplate < input.tmpl Hello, world! ``` +#### `has` + +Has reports whether or not a given object has a property with the given key. Can be used with `if` to prevent the template from trying to access a non-existent property in an object. + +##### Example + +_Let's say we're using a Vault datasource..._ + +_`input.tmpl`:_ +```go +{{ $secret := datasource "vault" "mysecret" -}} +The secret is ' +{{- if (has $secret "value") }} +{{- $secret.value }} +{{- else }} +{{- $secret | toYAML }} +{{- end }}' +``` + +If the `secret/foo/mysecret` secret in Vault has a property named `value` set to `supersecret`: + +```console +$ gomplate -d vault:///secret/foo < input.tmpl +The secret is 'supersecret' +``` + +On the other hand, if there is no `value` property: + +```console +$ gomplate -d vault:///secret/foo < input.tmpl +The secret is 'foo: bar' +``` + #### `json` Converts a JSON string into an object. Only works for JSON Objects (not Arrays or other valid JSON types). This can be used to access properties of JSON objects. diff --git a/main.go b/main.go index 8f801d7f5..e1b8c472f 100644 --- a/main.go +++ b/main.go @@ -50,6 +50,7 @@ func NewGomplate(data *Data) *Gomplate { funcMap: template.FuncMap{ "getenv": env.Getenv, "bool": typeconv.Bool, + "has": typeconv.Has, "json": typeconv.JSON, "jsonArray": typeconv.JSONArray, "yaml": typeconv.YAML, diff --git a/main_test.go b/main_test.go index 01af00dc8..773f34c6e 100644 --- a/main_test.go +++ b/main_test.go @@ -118,3 +118,27 @@ func TestSliceTemplates(t *testing.T) { assert.Equal(t, `[foo bar 42]`, testTemplate(g, `{{slice "foo" "bar" 42}}`)) assert.Equal(t, `helloworld`, testTemplate(g, `{{range slice "hello" "world"}}{{.}}{{end}}`)) } + +func TestHasTemplate(t *testing.T) { + ty := new(TypeConv) + g := &Gomplate{ + funcMap: template.FuncMap{ + "yaml": ty.YAML, + "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"}}`)) + tmpl := `{{- $data := yaml "foo: bar\nbaz: qux\n" }} +{{- if (has $data "baz") }} +{{- $data.baz }} +{{- end }}` + assert.Equal(t, "qux", testTemplate(g, tmpl)) + tmpl = `{{- $data := yaml "foo: bar\nbaz: qux\n" }} +{{- if (has $data "quux") }} +{{- $data.quux }} +{{- else }} +{{- $data.foo }} +{{- end }}` + assert.Equal(t, "bar", testTemplate(g, tmpl)) +} diff --git a/typeconv.go b/typeconv.go index 27bb5e96e..273486996 100644 --- a/typeconv.go +++ b/typeconv.go @@ -101,6 +101,12 @@ func (t *TypeConv) Join(a []interface{}, sep string) string { return strings.Join(b, sep) } +// 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 toString(in interface{}) string { if s, ok := in.(string); ok { return s diff --git a/typeconv_test.go b/typeconv_test.go index dafcb8669..7f07d08e3 100644 --- a/typeconv_test.go +++ b/typeconv_test.go @@ -124,3 +124,14 @@ func TestJoin(t *testing.T) { // and best-effort with weird types assert.Equal(t, "[foo],bar", ty.Join([]interface{}{[]string{"foo"}, "bar"}, ",")) } + +func TestHas(t *testing.T) { + ty := new(TypeConv) + + in := map[string]interface{}{ + "foo": "bar", + } + + assert.True(t, ty.Has(in, "foo")) + assert.False(t, ty.Has(in, "bar")) +}