Skip to content

Commit

Permalink
Implement options for enabling CEL extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
l46kok committed Mar 8, 2023
1 parent 5285995 commit 181b468
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 8 deletions.
1 change: 1 addition & 0 deletions repl/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//cel:go_default_library",
"//test/proto2pb:go_default_library",
"@org_golang_google_genproto//googleapis/api/expr/v1alpha1:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
Expand Down
65 changes: 59 additions & 6 deletions repl/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,12 +431,7 @@ type Evaluator struct {

// NewEvaluator returns an inialized evaluator
func NewEvaluator() (*Evaluator, error) {
env, err := cel.NewEnv(
ext.Strings(),
ext.Protos(),
ext.Math(),
ext.Encoders(),
)
env, err := cel.NewEnv()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -685,6 +680,39 @@ func (o *containerOption) Option() cel.EnvOption {
return cel.Container(o.container)
}

// extensionOption implements optional for loading a specific extension into the environment (String, Math, Proto, Encoder)
type extensionOption struct {
extensionType string
option cel.EnvOption
}

func (o *extensionOption) String() string {
return fmt.Sprintf("%%option --extension '%s'", o.extensionType)
}

func (o extensionOption) Option() cel.EnvOption {
return o.option
}

func newExtensionOption(extType string) (*extensionOption, error) {
var extOption cel.EnvOption
extType = strings.ToLower(extType)
switch op := extType; op {
case "strings":
extOption = ext.Strings()
case "protos":
extOption = ext.Protos()
case "math":
extOption = ext.Math()
case "encoders":
extOption = ext.Encoders()
default:
return nil, fmt.Errorf("Unknown option: %s. Available options are: ['strings', 'protos', 'math', 'encoders']", op)
}

return &extensionOption{extensionType: extType, option: extOption}, nil
}

// setOption sets a number of options on the environment. returns an error if
// any of them fail.
func (e *Evaluator) setOption(args []string) error {
Expand All @@ -702,6 +730,12 @@ func (e *Evaluator) setOption(args []string) error {
if err != nil {
issues = append(issues, fmt.Sprintf("container: %v", err))
}
} else if arg == "--extension" {
err := e.loadExtensionOption(idx, args)
idx++
if err != nil {
issues = append(issues, fmt.Sprintf("extension: %v", err))
}
} else {
issues = append(issues, fmt.Sprintf("unsupported option '%s'", arg))
}
Expand All @@ -712,6 +746,25 @@ func (e *Evaluator) setOption(args []string) error {
return nil
}

func (e *Evaluator) loadExtensionOption(idx int, args []string) error {
if idx >= len(args) {
return fmt.Errorf("not enough args for extension")
}

extType := args[idx]
extensionOption, err := newExtensionOption(extType)
if err != nil {
return err
}

err = e.AddOption(extensionOption)
if err != nil {
return err
}

return nil
}

func loadFileDescriptorSet(path string, textfmt bool) (*descpb.FileDescriptorSet, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
Expand Down
74 changes: 74 additions & 0 deletions repl/evaluator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (

"github.com/google/cel-go/cel"

proto2pb "github.com/google/cel-go/test/proto2pb"
exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)

Expand Down Expand Up @@ -527,6 +528,78 @@ func TestProcess(t *testing.T) {
wantExit: false,
wantError: false,
},
{
name: "OptionExtensionStrings",
commands: []Cmder{
&simpleCmd{
cmd: "option",
args: []string{
"--extension",
"strings",
},
},
&evalCmd{
expr: "'test'.substring(2)",
},
},
wantText: "st : string",
wantExit: false,
wantError: false,
},
{
name: "OptionExtensionProtos",
commands: []Cmder{
&simpleCmd{
cmd: "option",
args: []string{
"--extension",
"protos",
},
},
&evalCmd{
expr: "proto.getExt(google.expr.proto2.test.ExampleType{}, google.expr.proto2.test.int32_ext) == 0",
},
},
wantText: "true : bool",
wantExit: false,
wantError: false,
},
{
name: "OptionExtensionMath",
commands: []Cmder{
&simpleCmd{
cmd: "option",
args: []string{
"--extension",
"math",
},
},
&evalCmd{
expr: "math.greatest(1,2)",
},
},
wantText: "2 : int",
wantExit: false,
wantError: false,
},
{
name: "OptionExtensionEncoders",
commands: []Cmder{
&simpleCmd{
cmd: "option",
args: []string{
"--extension",
"encoders",
},
},
&evalCmd{
expr: "base64.encode(b'hello')",
},
},
wantText: "aGVsbG8= : string",
wantExit: false,
wantError: false,
},
{
name: "LoadDescriptorsError",
commands: []Cmder{
Expand Down Expand Up @@ -651,6 +724,7 @@ func TestProcess(t *testing.T) {
if err != nil {
t.Fatalf("NewEvaluator returned error: %v, wanted nil", err)
}
eval.env, _ = eval.env.Extend(cel.Types(&proto2pb.ExampleType{}, &proto2pb.ExternalMessageType{}))
n := len(tc.commands)
for _, cmd := range tc.commands[:n-1] {
// only need output of last command
Expand Down
4 changes: 4 additions & 0 deletions repl/main/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,14 @@ may take string arguments.

`--container <string>` sets the expression container for name resolution.

`--extension <extensionType>` enables CEL extensions. Valid options are: `strings`, `protos`, `math`, `encoders`.

example:

`%option --container 'google.protobuf'`

`%option --extension 'strings'`

#### reset

`%reset` drops all options and let expressions, returning the evaluator to a
Expand Down
5 changes: 3 additions & 2 deletions test/proto2pb/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package(
"//ext:__subpackages__",
"//interpreter:__subpackages__",
"//parser:__subpackages__",
"//repl:__subpackages__",
"//server:__subpackages__",
"//test:__subpackages__",
],
Expand All @@ -24,13 +25,13 @@ go_library(
importpath = "github.com/google/cel-go/test/proto2pb",
deps = [
"@org_golang_google_protobuf//proto:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
"@org_golang_google_protobuf//types/known/anypb:go_default_library",
"@org_golang_google_protobuf//types/known/durationpb:go_default_library",
"@org_golang_google_protobuf//types/known/structpb:go_default_library",
"@org_golang_google_protobuf//types/known/timestamppb:go_default_library",
"@org_golang_google_protobuf//types/known/wrapperspb:go_default_library",
"@org_golang_google_protobuf//reflect/protoreflect:go_default_library",
"@org_golang_google_protobuf//runtime/protoimpl:go_default_library",
],
)

Expand Down

0 comments on commit 181b468

Please sign in to comment.