-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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