Skip to content

Commit 4a62900

Browse files
committed
fix: templating: allow templating to render empty string as result
Signed-off-by: Romain Beuque <[email protected]>
1 parent e2b89be commit 4a62900

File tree

3 files changed

+47
-12
lines changed

3 files changed

+47
-12
lines changed

engine/engine_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/loopfz/gadgeto/zesty"
1717
"github.com/ovh/configstore"
1818
"github.com/stretchr/testify/assert"
19+
"github.com/stretchr/testify/require"
1920

2021
"github.com/ovh/utask"
2122
"github.com/ovh/utask/db"
@@ -625,6 +626,26 @@ func TestBaseOutput(t *testing.T) {
625626
assert.Equal(t, "bar", output["foo"])
626627
}
627628

629+
func TestEmptyStringInput(t *testing.T) {
630+
input := map[string]interface{}{
631+
"quantity": -2.3,
632+
"foo": "",
633+
}
634+
res, err := createResolution("input.yaml", input, nil)
635+
assert.NotNil(t, res)
636+
assert.Nil(t, err)
637+
638+
res, err = runResolution(res)
639+
640+
require.Nilf(t, err, "got error %s", err)
641+
require.NotNil(t, res)
642+
assert.Equal(t, resolution.StateDone, res.State)
643+
assert.Equal(t, step.StateDone, res.Steps["stepOne"].State)
644+
645+
output := res.Steps["stepOne"].Output.(map[string]interface{})
646+
assert.Equal(t, "", output["foo"])
647+
}
648+
628649
func TestScriptPlugin(t *testing.T) {
629650
argv := "world"
630651
res, err := createResolution("execScript.yaml", map[string]interface{}{"argv": argv}, nil)

engine/step/apply.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ package step
33
import (
44
"bytes"
55
"encoding/json"
6+
"errors"
67
"reflect"
78

89
"github.com/ovh/utask/engine/values"
910
)
1011

12+
var (
13+
errNotTemplatable = errors.New("value given is not templatable")
14+
)
15+
1116
func resolveObject(val *values.Values, objson json.RawMessage, item interface{}, stepName string) ([]byte, error) {
1217
obj, err := rawResolveObject(val, objson, item, stepName)
1318
if err != nil {
@@ -61,12 +66,14 @@ func applyMap(val *values.Values, v reflect.Value, item interface{}, stepName st
6166
applySlice(val, mv, item, stepName)
6267
case reflect.String:
6368
newValue, err := applyString(val, mv, item, stepName)
64-
if err != nil {
65-
return err
66-
}
67-
68-
if newValue != "" {
69+
switch err {
70+
case nil:
6971
v.SetMapIndex(iter.Key(), reflect.ValueOf(newValue))
72+
case errNotTemplatable:
73+
// current value Kind is string, but actual type could not be string (e.g. json.Number)
74+
// in that case, we should keep the actual value as it will never be templated
75+
default:
76+
return err
7077
}
7178
}
7279
}
@@ -90,12 +97,14 @@ func applySlice(val *values.Values, v reflect.Value, item interface{}, stepName
9097
applySlice(val, elem, item, stepName)
9198
case reflect.String:
9299
newValue, err := applyString(val, elem, item, stepName)
93-
if err != nil {
94-
return err
95-
}
96-
97-
if newValue != "" {
100+
switch err {
101+
case nil:
98102
mv.Set(reflect.ValueOf(newValue))
103+
case errNotTemplatable:
104+
// current value Kind is string, but actual type could not be string (e.g. json.Number)
105+
// in that case, we should keep the actual value as it will never be templated
106+
default:
107+
return err
99108
}
100109
}
101110
}
@@ -107,7 +116,7 @@ func applyString(val *values.Values, v reflect.Value, item interface{}, stepName
107116
strval := v.Interface()
108117
str, ok := strval.(string)
109118
if !ok {
110-
return "", nil
119+
return "", errNotTemplatable
111120
}
112121
resolved, err := val.Apply(str, item, stepName)
113122
if err != nil {

engine/templates_tests/input.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ inputs:
88
collection: false
99
type: number
1010
optional: false
11+
- name: foo
12+
description: A string value
13+
type: string
14+
optional: true
15+
default: ""
1116
steps:
1217
stepOne:
1318
description: first step
1419
idempotent: true
1520
retry_pattern: seconds
1621
action:
1722
type: echo
18-
configuration: {output: {value: "{{.input.quantity}}"}}
23+
configuration: {output: {value: "{{.input.quantity}}", foo: "{{.input.foo}}"}}

0 commit comments

Comments
 (0)