Skip to content

Commit acc23c0

Browse files
authored
templating: adding fieldFrom helper function to retrieve a field from a previous expression (#210)
`field` templating helper function allow to retrieve a key into a map[string], even if the key is not a valid templating keyword. `fieldFrom` provides the same feature, expect that it doesn't use the values map from the task/resolution/steps, but use the previous templating pipeline as a templating source. Closes #200 Signed-off-by: Romain Beuque <556072+rbeuque74@users.noreply.github.com>
1 parent 3fe8365 commit acc23c0

File tree

5 files changed

+76
-6
lines changed

5 files changed

+76
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ The following templating functions are available:
211211
| **`Golang`** | Builtin functions from Golang text template | [Doc](https://golang.org/pkg/text/template/#hdr-Actions) |
212212
| **`Sprig`** | Extended set of functions from the Sprig project | [Doc](https://masterminds.github.io/sprig/) |
213213
| **`field`** | Equivalent to the dot notation, for entries with forbidden characters | ``{{field `config` `foo.bar`}}`` |
214+
| **`fieldFrom`** | Equivalent to the dot notation, for entries with forbidden characters. It takes the previous template expression as source for the templating values. Example: ``{{ `{"foo.foo":"bar"}` | fromJson | fieldFrom `foo.foo` }}`` | ``{{expr | fieldFrom `config` `foo.bar`}}`` |
214215
| **`eval`** | Evaluates the value of a template variable | ``{{eval `var1`}}`` |
215216
| **`evalCache`** | Evaluates the value of a template variable, and cache for future usage (to avoid further computation) | ``{{evalCache `var1`}}`` |
216217
| **`fromJson`** | Decodes a JSON document into a structure. If the input cannot be decoded as JSON, the function will return an empty string | ``{{fromJson `{"a":"b"}`}}`` |

engine/values/values.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ func NewValues() *Values {
6767
}
6868
v.funcMap = sprig.FuncMap()
6969
v.funcMap["field"] = v.fieldTmpl
70+
v.funcMap["fieldFrom"] = fieldFromTmpl
7071
v.funcMap["eval"] = v.varEval
7172
v.funcMap["evalCache"] = v.varEvalCache
7273
v.funcMap["fromJson"] = v.fromJSON
@@ -308,9 +309,13 @@ func (v *Values) fieldTmpl(key ...string) reflect.Value {
308309
var i interface{}
309310

310311
i = map[string]interface{}(v.m)
312+
return fieldFn(i, key)
313+
}
314+
315+
func fieldFn(i interface{}, keys []string) reflect.Value {
311316
var ok bool
312317

313-
for _, k := range key {
318+
for _, k := range keys {
314319
switch i.(type) {
315320
case map[string]interface{}:
316321
i, ok = i.(map[string]interface{})[k]
@@ -329,6 +334,33 @@ func (v *Values) fieldTmpl(key ...string) reflect.Value {
329334
return reflect.ValueOf(i)
330335
}
331336

337+
func fieldFromTmpl(params ...interface{}) (reflect.Value, error) {
338+
if len(params) < 2 {
339+
return zero, errors.New("invalid number of parameters given")
340+
}
341+
var i interface{}
342+
var ok bool
343+
i, ok = params[len(params)-1].(map[string]interface{})
344+
if !ok {
345+
return zero, errors.New("unable to cast given data to a map[string]")
346+
}
347+
348+
keys := []string{}
349+
for j := range params {
350+
if j >= len(params)-1 {
351+
break
352+
}
353+
354+
item, ok := params[j].(string)
355+
if !ok {
356+
return zero, errors.New("foo")
357+
}
358+
keys = append(keys, item)
359+
}
360+
361+
return fieldFn(i, keys), nil
362+
}
363+
332364
func (v *Values) varEvalCache(varName string) (interface{}, error) {
333365
i, ok := v.GetVariables()[varName]
334366
if !ok {

engine/values/values_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package values_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/ghodss/yaml"
7+
"github.com/maxatome/go-testdeep/td"
8+
"github.com/ovh/utask/engine/values"
9+
)
10+
11+
func TestTmpl(t *testing.T) {
12+
input := `"step":
13+
"first":
14+
"output":
15+
"result":
16+
"my-payload": "{\"common-name\":\"utask.example.org\",\"id\":32,\"foo\":{\"bar\":1}}"`
17+
obj := map[string]map[string]map[string]map[string]interface{}{}
18+
err := yaml.Unmarshal([]byte(input), &obj)
19+
td.CmpNil(t, err)
20+
21+
v := values.NewValues()
22+
v.SetOutput("first", obj["step"]["first"]["output"])
23+
24+
output, err := v.Apply("{{ field `step` `first` `output` `result` `my-payload` }}", nil, "foo")
25+
td.CmpNil(t, err)
26+
td.Cmp(t, string(output), "{\"common-name\":\"utask.example.org\",\"id\":32,\"foo\":{\"bar\":1}}")
27+
28+
output, err = v.Apply("{{ field `step` `first` `output` `result` `my-payload` | fromJson | fieldFrom `common-name` }}", nil, "foo")
29+
td.CmpNil(t, err)
30+
td.Cmp(t, string(output), "utask.example.org")
31+
32+
output, err = v.Apply("{{ field `step` `first` `output` `result` `my-payload` | fromJson | fieldFrom `foo` `bar` }}", nil, "foo")
33+
td.CmpNil(t, err)
34+
td.Cmp(t, string(output), "1")
35+
36+
output, err = v.Apply("{{ `{\"common-name\":\"utask.example.org\",\"id\":32}` | fromJson | fieldFrom `invalid` | default `example.org` }}", nil, "foo")
37+
td.CmpNil(t, err)
38+
td.Cmp(t, string(output), "example.org")
39+
}

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ require (
2727
github.com/loopfz/gadgeto v0.10.1
2828
github.com/magiconair/properties v1.8.1 // indirect
2929
github.com/markusthoemmes/goautoneg v0.0.0-20190713162725-c6008fefa5b1
30-
github.com/maxatome/go-testdeep v1.6.0
30+
github.com/maxatome/go-testdeep v1.8.0
3131
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 // indirect
3232
github.com/mitchellh/reflectwalk v1.0.1 // indirect
3333
github.com/ovh/configstore v0.3.2

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK
225225
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
226226
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
227227
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
228-
github.com/maxatome/go-testdeep v1.6.0 h1:E75ovdjJakM2V620YTykpckTEINs+aZ6iYFBNzb1h0w=
229-
github.com/maxatome/go-testdeep v1.6.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
228+
github.com/maxatome/go-testdeep v1.8.0 h1:7QpuvRvyCUefU41jbEU1+k8N4DhuE6jbu/CvFeUUOhU=
229+
github.com/maxatome/go-testdeep v1.8.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
230230
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75 h1:cUVxyR+UfmdEAZGJ8IiKld1O0dbGotEnkMolG5hfMSY=
231231
github.com/miscreant/miscreant.go v0.0.0-20200214223636-26d376326b75/go.mod h1:pBbZyGwC5i16IBkjVKoy/sznA8jPD/K9iedwe1ESE6w=
232232
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
@@ -366,8 +366,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
366366
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
367367
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
368368
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
369-
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
370-
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
371369
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
372370
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
373371
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=

0 commit comments

Comments
 (0)