Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

execute processor condition template #1352

Merged
merged 11 commits into from
Feb 6, 2024
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21.1
require (
buf.build/gen/go/grpc-ecosystem/grpc-gateway/protocolbuffers/go v1.32.0-20231027202514-3f42134f4c56.1
github.com/Masterminds/semver/v3 v3.2.1
github.com/Masterminds/sprig/v3 v3.2.3
github.com/NYTimes/gziphandler v1.1.1
github.com/antchfx/jsonquery v1.3.3
github.com/bufbuild/buf v1.29.0
Expand Down Expand Up @@ -71,7 +72,6 @@ require (
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect
Expand Down
67 changes: 67 additions & 0 deletions pkg/processor/template_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright © 2024 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package processor

import (
"bytes"
"fmt"
"strconv"
"text/template"

"github.com/Masterminds/sprig/v3"
"github.com/conduitio/conduit/pkg/record"
)

// TemplateUtils provides methods to parse, execute go templates for provided records, and return the boolean value of the output.
type TemplateUtils struct {
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
lovromazgon marked this conversation as resolved.
Show resolved Hide resolved
condition string
rec record.Record
tmpl *template.Template
}

func NewTemplateUtils(condition string, rec record.Record) *TemplateUtils {
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
return &TemplateUtils{
condition: condition,
rec: rec,
}
}

// parse parses the provided template with injecting `sprig` functions, returns an error if parsing failed.
func (t *TemplateUtils) parse() error {
tmpl, err := template.New("").Funcs(sprig.FuncMap()).Parse(t.condition)
if err != nil {
return err
}
t.tmpl = tmpl
return nil
}

// execute executes the template for the provided record, and parses the template output into a boolean, returns an error
// if output is not a boolean, or if template is nil (should be called after TemplateUtils.parse).
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
func (t *TemplateUtils) execute() (bool, error) {
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
if t.tmpl == nil {
return false, fmt.Errorf("template is nil, make sure to parse it first by calling TemplateUtils.parse()")
}
var b bytes.Buffer
err := t.tmpl.Execute(&b, t.rec)
if err != nil {
return false, err
}
output, err := strconv.ParseBool(b.String())
if err != nil {
return false, fmt.Errorf("error converting the condition go-template output to boolean, %w", err)
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
}
return output, nil
}
64 changes: 64 additions & 0 deletions pkg/processor/template_utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright © 2024 Meroxa, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package processor

import (
"testing"

"github.com/conduitio/conduit/pkg/record"
"github.com/matryer/is"
)

func Test_TemplateUtils_ExecuteTrue(t *testing.T) {
is := is.New(t)
condition := "{{ eq .Metadata.key \"val\" }}"
maha-hajja marked this conversation as resolved.
Show resolved Hide resolved
rec := record.Record{
Position: record.Position("position-out"),
Metadata: record.Metadata{"key": "val"},
}
tmpl := NewTemplateUtils(condition, rec)
is.NoErr(tmpl.parse())
res, err := tmpl.execute()
is.NoErr(err)
is.True(res)
}

func Test_TemplateUtils_ExecuteFalse(t *testing.T) {
is := is.New(t)
condition := "{{ eq .Metadata.key \"wrongVal\" }}"
rec := record.Record{
Position: record.Position("position-out"),
Metadata: record.Metadata{"key": "val"},
}
tmpl := NewTemplateUtils(condition, rec)
is.NoErr(tmpl.parse())
res, err := tmpl.execute()
is.NoErr(err)
is.True(res == false)
}

func Test_TemplateUtils_NonBooleanValue(t *testing.T) {
is := is.New(t)
condition := "{{ printf \"hi\" }}"
rec := record.Record{
Position: record.Position("position-out"),
Metadata: record.Metadata{"key": "val"},
}
tmpl := NewTemplateUtils(condition, rec)
is.NoErr(tmpl.parse())
res, err := tmpl.execute()
is.True(err != nil)
is.True(res == false)
}
Loading