Skip to content

Commit

Permalink
Added:
Browse files Browse the repository at this point in the history
- Initial implementation of a user defined function.
- TCK tests for user defined functions.
- Instructions on using user defined functions in README file.

Signed-off-by: Dimitar Georgievski <dgeorgievski@gmail.com>
  • Loading branch information
dgeorgievski committed May 3, 2024
1 parent f97061a commit 97049a8
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 1 deletion.
48 changes: 48 additions & 0 deletions sql/v2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,54 @@ expression, err := cesqlparser.Parse("subject = 'Hello world'")
res, err := expression.Evaluate(event)
```

Add a user defined function
```go
import (
cesql "github.com/cloudevents/sdk-go/sql/v2"
cefn "github.com/cloudevents/sdk-go/sql/v2/function"
cesqlparser "github.com/cloudevents/sdk-go/sql/v2/parser"
ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime"
cloudevents "github.com/cloudevents/sdk-go/v2"
)

// Create a test event
event := cloudevents.NewEvent()
event.SetID("aaaa-bbbb-dddd")
event.SetSource("https://my-source")
event.SetType("dev.tekton.event")

// Create and add a new user defined function
var HasPrefixFunction cesql.Function = cefn.NewFunction(
"HASPREFIX",
[]cesql.Type{cesql.StringType, cesql.StringType},
nil,
func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
prefix := i[1].(string)

return strings.HasPrefix(str, prefix), nil
},
)

err := ceruntime.AddFunction(HasPrefixFunction)

// parse the expression
expression, err := cesqlparser.Parse("HASPREFIX(type, 'dev.tekton.event')")
if err != nil {
fmt.Println("parser err: ", err)
os.Exit(1)
}

// Evalute the expression with the test event
res, err := expression.Evaluate(event)

if res.(bool) {
fmt.Println("Event type has the prefix")
} else {
fmt.Println("Event type doesn't have the prefix")
}
```

## Development guide

To regenerate the parser, make sure you have [ANTLR4 installed](https://github.com/antlr/antlr4/blob/master/doc/getting-started.md) and then run:
Expand Down
16 changes: 15 additions & 1 deletion sql/v2/function/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (
cloudevents "github.com/cloudevents/sdk-go/v2"
)

type FuncType func(cloudevents.Event, []interface{}) (interface{}, error)

type function struct {
name string
fixedArgs []cesql.Type
variadicArgs *cesql.Type
fn func(cloudevents.Event, []interface{}) (interface{}, error)
fn FuncType
}

func (f function) Name() string {
Expand All @@ -39,3 +41,15 @@ func (f function) ArgType(index int) *cesql.Type {
func (f function) Run(event cloudevents.Event, arguments []interface{}) (interface{}, error) {
return f.fn(event, arguments)
}

func NewFunction(name string,
fixedargs []cesql.Type,
variadicArgs *cesql.Type,
fn FuncType) cesql.Function {
return function{
name: name,
fixedArgs: fixedargs,
variadicArgs: variadicArgs,
fn: fn,
}
}
5 changes: 5 additions & 0 deletions sql/v2/runtime/functions_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ func (table functionTable) AddFunction(function cesql.Function) error {
}
}

// Adds user defined function
func AddFunction(fn cesql.Function) error {
return globalFunctionTable.AddFunction(fn)
}

func (table functionTable) ResolveFunction(name string, args int) cesql.Function {
item := table[strings.ToUpper(name)]
if item == nil {
Expand Down
70 changes: 70 additions & 0 deletions sql/v2/runtime/functions_resolver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Copyright 2021 The CloudEvents Authors
SPDX-License-Identifier: Apache-2.0
*/

package runtime

import (
"os"
"strings"
"testing"

cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/function"
cloudevents "github.com/cloudevents/sdk-go/v2"
)

var HasPrefixCustomFunction cesql.Function

func TestMain(m *testing.M) {
HasPrefixCustomFunction = function.NewFunction(
"HASPREFIX",
[]cesql.Type{cesql.StringType, cesql.StringType},
nil,
func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
prefix := i[1].(string)

return strings.HasPrefix(str, prefix), nil
},
)
os.Exit(m.Run())
}

func Test_functionTable_AddFunction(t *testing.T) {

type args struct {
function cesql.Function
}
tests := []struct {
name string
table functionTable
args args
wantErr bool
}{
{
name: "Add custom fixedArgs func",
table: globalFunctionTable,
args: args{
function: HasPrefixCustomFunction,
},
wantErr: false,
},
{
name: "Fail add custom fixedArgs func if it exists",
table: globalFunctionTable,
args: args{
function: HasPrefixCustomFunction,
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := tt.table.AddFunction(tt.args.function); (err != nil) != tt.wantErr {
t.Errorf("functionTable.AddFunction() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
27 changes: 27 additions & 0 deletions sql/v2/test/tck/user_defined_functions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: User defined functions
tests:
- name: HASPREFIX (1)
expression: "HASPREFIX('abcdef', 'ab')"
result: true
- name: HASPREFIX (2)
expression: "HASPREFIX('abcdef', 'abcdef')"
result: true
- name: HASPREFIX (3)
expression: "HASPREFIX('abcdef', '')"
result: true
- name: HASPREFIX (4)
expression: "HASPREFIX('abcdef', 'gh')"
result: false
- name: HASPREFIX (5)
expression: "HASPREFIX('abcdef', 'abcdefg')"
result: false

- name: KONKAT (1)
expression: "KONKAT('a', 'b', 'c')"
result: abc
- name: KONKAT (2)
expression: "KONKAT()"
result: ""
- name: KONKAT (3)
expression: "KONKAT('a')"
result: "a"
38 changes: 38 additions & 0 deletions sql/v2/test/tck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import (
"os"
"path"
"runtime"
"strings"
"testing"

"github.com/stretchr/testify/require"
"sigs.k8s.io/yaml"

cesql "github.com/cloudevents/sdk-go/sql/v2"
"github.com/cloudevents/sdk-go/sql/v2/function"
"github.com/cloudevents/sdk-go/sql/v2/parser"
ceruntime "github.com/cloudevents/sdk-go/sql/v2/runtime"
cloudevents "github.com/cloudevents/sdk-go/v2"
"github.com/cloudevents/sdk-go/v2/binding/spec"
"github.com/cloudevents/sdk-go/v2/event"
Expand All @@ -41,6 +45,33 @@ var TCKFileNames = []string{
"string_builtin_functions",
"sub_expression",
"subscriptions_api_recreations",
"user_defined_functions",
}

var TCKUserDefinedFunctions = []cesql.Function{
function.NewFunction(
"HASPREFIX",
[]cesql.Type{cesql.StringType, cesql.StringType},
nil,
func(event cloudevents.Event, i []interface{}) (interface{}, error) {
str := i[0].(string)
prefix := i[1].(string)

return strings.HasPrefix(str, prefix), nil
},
),
function.NewFunction(
"KONKAT",
[]cesql.Type{},
cesql.TypePtr(cesql.StringType),
func(event cloudevents.Event, i []interface{}) (interface{}, error) {
var sb strings.Builder
for _, v := range i {
sb.WriteString(v.(string))
}
return sb.String(), nil
},
),
}

type ErrorType string
Expand Down Expand Up @@ -70,6 +101,13 @@ type TckTestCase struct {
EventOverrides map[string]interface{} `json:"eventOverrides"`
}

func TestMain(m *testing.M) {
for _, f := range TCKUserDefinedFunctions {
ceruntime.AddFunction(f)
}
os.Exit(m.Run())
}

func (tc TckTestCase) InputEvent(t *testing.T) cloudevents.Event {
var inputEvent cloudevents.Event
if tc.Event != nil {
Expand Down

0 comments on commit 97049a8

Please sign in to comment.