diff --git a/README.md b/README.md index 75ba111d..38604838 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,8 @@ The following templating functions are available: | **`evalCache`** | Evaluates the value of a template variable, and cache for future usage (to avoid further computation) | ``{{evalCache `var1`}}`` | | **`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"}`}}`` | | **`mustFromJson`** | Similar to **`fromJson`**, but will return an error in case the JSON is invalid. A common usecase consists of returning a JSON stringified data structure from a JavaScript expression (object, array), and use one of its members in the template. Example: ``{{(eval `myExpression` \| fromJson).myArr}}`` or ``{{(eval `myExpression` \| fromJson).myObj}}`` | ``{{mustFromJson `{"a":"b"}`}}`` | +| **`b64RawEnc`** | Encode a string to a b64 raw encoded string as defined in [RFC 4648 section 3.2](https://www.rfc-editor.org/rfc/rfc4648.html#section-3.2). Example: ``{{eval `myString` \| b64RawEnc}}`` | ``{{b64RawEnc `a nice string`}}`` | +| **`b64RawDec`** | Decode a b64 raw encoded string as defined in [RFC 4648 section 3.2](https://www.rfc-editor.org/rfc/rfc4648.html#section-3.2) to a decoded string. Example: ``{{eval `cmF3IG1lc3NhZ2U` \| b64RawDec}}`` | ``{{b64RawDec cmF3IG1lc3NhZ2U`}}`` | ### Basic properties diff --git a/engine/engine_test.go b/engine/engine_test.go index b3ddbc08..55146dc7 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -1355,3 +1355,18 @@ func TestResolveCallback(t *testing.T) { // callback has been created, waiting for its resolution assert.Equal(t, resolution.StateWaiting, res.State) } + +func TestB64RawEncodeDecode(t *testing.T) { + res, err := createResolution("rawb64EncodingDecoding.yaml", nil, nil) + assert.NotNil(t, res) + assert.Nil(t, err) + + res, err = runResolution(res) + assert.NotNil(t, res) + assert.Nil(t, err) + assert.Equal(t, resolution.StateDone, res.State) + + output := res.Steps["stepOne"].Output.(map[string]interface{}) + assert.Equal(t, "cmF3IG1lc3NhZ2U", output["a"]) + assert.Equal(t, "raw message", output["b"]) +} diff --git a/engine/templates_tests/rawb64EncodingDecoding.yaml b/engine/templates_tests/rawb64EncodingDecoding.yaml new file mode 100644 index 00000000..2e4c898e --- /dev/null +++ b/engine/templates_tests/rawb64EncodingDecoding.yaml @@ -0,0 +1,24 @@ +name: rawb64EncodingDecoding +description: Ensure that b64 encoding and decoding can be used in template +title_format: "[test] correct b64 raw encoding and decoding" +auto_runnable: true + + + +variables: + - name: rawDecoded + expression: |- + "raw message"; + - name: rawEncoded + expression: |- + "cmF3IG1lc3NhZ2U"; + +steps: + stepOne: + description: first step + action: + type: echo + configuration: + output: + a: '{{ eval `rawDecoded` | b64RawEnc }}' + b: '{{ eval `rawEncoded` | b64RawDec }}' \ No newline at end of file diff --git a/engine/values/values.go b/engine/values/values.go index c1844669..23ae50b2 100644 --- a/engine/values/values.go +++ b/engine/values/values.go @@ -2,6 +2,7 @@ package values import ( "bytes" + "encoding/base64" "encoding/json" "fmt" "reflect" @@ -77,6 +78,8 @@ func NewValues() *Values { v.funcMap["fromJson"] = v.fromJSON v.funcMap["mustFromJson"] = v.mustFromJSON v.funcMap["uuid"] = uuid.NewV4 + v.funcMap["b64RawEnc"] = v.b64RawEnc + v.funcMap["b64RawDec"] = v.b64RawDec return v } @@ -499,6 +502,18 @@ func (v *Values) mustFromJSON(s string) (reflect.Value, error) { return reflect.ValueOf(output), err } +func (v *Values) b64RawDec(s string) string { + data, err := base64.RawStdEncoding.DecodeString(s) + if err != nil { + return err.Error() + } + return string(data) +} + +func (v *Values) b64RawEnc(s string) string { + return base64.RawStdEncoding.EncodeToString([]byte(s)) +} + var errTimedOut = errors.New("Timed out variable evaluation") func evalUnsafe(exp []byte, delay time.Duration) (v otto.Value, err error) {