Skip to content

Instantly share code, notes, and snippets.

@flowchartsman
Forked from LOZORD/json_test.go
Created November 2, 2022 20:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save flowchartsman/ef577dc99414b3cea33109c695f5f97a to your computer and use it in GitHub Desktop.
Save flowchartsman/ef577dc99414b3cea33109c695f5f97a to your computer and use it in GitHub Desktop.
An example of using a native JSON-friendly Go struct as a type and input in a CEL program.
package test
import (
"bytes"
"encoding/json"
"testing"
"github.com/golang/protobuf/jsonpb"
structpb "github.com/golang/protobuf/ptypes/struct"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker/decls"
)
type Payload struct {
Strs []string `json:"strs"`
Data map[string]string `json:"data"`
}
type MyStruct struct {
Num int64 `json:"num"`
Str string `json:"str"`
Payload Payload `json:"payload"`
}
func TestCELStructJSON(t *testing.T) {
for _, tc := range []struct {
name string
filter string
myStruct *MyStruct
wantMatch bool
}{{
name: "simple match",
filter: `myStruct.str == "hello" && "world" in myStruct.payload.data`,
myStruct: &MyStruct{Num: 10, Str: "hello", Payload: Payload{Data: map[string]string{"world": "foobar"}}},
wantMatch: true,
}, {
name: "simple mismatch",
filter: `myStruct.num > 9000 && "banana" in myStruct.payload.strs`,
myStruct: &MyStruct{Num: 9001, Str: "blah", Payload: Payload{Strs: []string{"kiwi", "orange"}, Data: map[string]string{"mars": "goober"}}},
wantMatch: false,
}} {
t.Run(tc.name, func(t *testing.T) {
// First build the CEL program.
ds := cel.Declarations(
decls.NewIdent("myStruct", decls.NewMapType(decls.String, decls.Dyn), nil),
)
env, err := cel.NewEnv(ds)
if err != nil {
t.Fatal(err)
}
prs, iss := env.Parse(tc.filter)
if iss != nil && iss.Err() != nil {
t.Fatal(iss.Err())
}
chk, iss := env.Check(prs)
if iss != nil && iss.Err() != nil {
t.Fatal(iss.Err())
}
prg, err := env.Program(chk)
if err != nil {
t.Fatal(err)
}
// Now, get the input in the correct format (conversion: Go struct -> JSON -> structpb).
j, err := json.Marshal(tc.myStruct)
if err != nil {
t.Fatal(err)
}
var spb structpb.Struct
if err := jsonpb.Unmarshal(bytes.NewBuffer(j), &spb); err != nil {
t.Fatal(err)
}
// Now, evaluate the program and check the output.
val, _, err := prg.Eval(map[string]interface{}{"myStruct": &spb})
if err != nil {
t.Fatal(err)
}
gotMatch, ok := val.Value().(bool)
if !ok {
t.Fatalf("failed to convert %+v to bool", val)
}
if gotMatch != tc.wantMatch {
t.Errorf("expected cel(%q, %s) to be %v", tc.filter, string(j), tc.wantMatch)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment