diff --git a/codelab/BUILD b/codelab/BUILD index b80219f21..b257f251c 100644 --- a/codelab/BUILD +++ b/codelab/BUILD @@ -69,7 +69,6 @@ cc_library( hdrs = ["exercise2.h"], deps = [ "//eval/public:activation", - "//eval/public:activation_bind_helper", "//eval/public:builtin_func_registrar", "//eval/public:cel_expr_builder_factory", "//eval/public:cel_expression", @@ -111,3 +110,82 @@ cc_test( "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", ], ) + +cc_library( + name = "cel_compiler", + hdrs = ["cel_compiler.h"], + deps = [ + "//checker:validation_result", + "//common:ast_proto", + "//compiler", + "//internal:status_macros", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_cel_spec//proto/cel/expr:checked_cc_proto", + ], +) + +cc_test( + name = "cel_compiler_test", + srcs = ["cel_compiler_test.cc"], + deps = [ + ":cel_compiler", + "//common:decl", + "//common:type", + "//compiler", + "//compiler:compiler_factory", + "//compiler:standard_library", + "//eval/public:activation", + "//eval/public:activation_bind_helper", + "//eval/public:builtin_func_registrar", + "//eval/public:cel_expr_builder_factory", + "//eval/public:cel_function_adapter", + "//eval/public:cel_value", + "//eval/public/testing:matchers", + "//internal:testing", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:status_matchers", + "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", + "@com_google_protobuf//:protobuf", + ], +) + +cc_library( + name = "exercise4", + srcs = ["exercise4.cc"], + hdrs = ["exercise4.h"], + deps = [ + ":cel_compiler", + "//compiler", + "//compiler:compiler_factory", + "//compiler:standard_library", + "//eval/public:activation", + "//eval/public:activation_bind_helper", + "//eval/public:builtin_func_registrar", + "//eval/public:cel_expr_builder_factory", + "//eval/public:cel_expression", + "//eval/public:cel_options", + "//eval/public:cel_value", + "//internal:status_macros", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_cel_spec//proto/cel/expr:checked_cc_proto", + "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", + "@com_google_protobuf//:protobuf", + ], +) + +cc_test( + name = "exercise4_test", + srcs = ["exercise4_test.cc"], + tags = EXERCISE_TEST_TAGS, + deps = [ + ":exercise4", + "//internal:testing", + "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", + "@com_google_protobuf//:protobuf", + ], +) diff --git a/codelab/cel_compiler.h b/codelab/cel_compiler.h new file mode 100644 index 000000000..0ff2f699b --- /dev/null +++ b/codelab/cel_compiler.h @@ -0,0 +1,47 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#ifndef THIRD_PARTY_CEL_CPP_CODELAB_COMPILER_H_ +#define THIRD_PARTY_CEL_CPP_CODELAB_COMPILER_H_ + +#include "cel/expr/checked.pb.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "checker/validation_result.h" +#include "common/ast_proto.h" +#include "compiler/compiler.h" +#include "internal/status_macros.h" + +namespace cel_codelab { + +// Helper for compiling expression and converting to proto. +// +// Simplifies error handling for brevity in the codelab. +inline absl::StatusOr CompileToCheckedExpr( + const cel::Compiler& compiler, absl::string_view expr) { + CEL_ASSIGN_OR_RETURN(cel::ValidationResult result, compiler.Compile(expr)); + + if (!result.IsValid() || result.GetAst() == nullptr) { + return absl::InvalidArgumentError(result.FormatError()); + } + + cel::expr::CheckedExpr pb; + CEL_RETURN_IF_ERROR(cel::AstToCheckedExpr(*result.GetAst(), &pb)); + return pb; +}; + +} // namespace cel_codelab + +#endif // THIRD_PARTY_CEL_CPP_CODELAB_COMPILER_H_ diff --git a/codelab/cel_compiler_test.cc b/codelab/cel_compiler_test.cc new file mode 100644 index 000000000..635b4d54d --- /dev/null +++ b/codelab/cel_compiler_test.cc @@ -0,0 +1,146 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#include "codelab/cel_compiler.h" + +#include +#include + +#include "google/rpc/context/attribute_context.pb.h" +#include "absl/log/absl_check.h" +#include "absl/status/status.h" +#include "absl/status/status_matchers.h" +#include "common/decl.h" +#include "common/type.h" +#include "compiler/compiler.h" +#include "compiler/compiler_factory.h" +#include "compiler/standard_library.h" +#include "eval/public/activation.h" +#include "eval/public/activation_bind_helper.h" +#include "eval/public/builtin_func_registrar.h" +#include "eval/public/cel_expr_builder_factory.h" +#include "eval/public/cel_function_adapter.h" +#include "eval/public/cel_value.h" +#include "eval/public/testing/matchers.h" +#include "internal/testing.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel_codelab { +namespace { + +using ::absl_testing::IsOk; +using ::absl_testing::StatusIs; +using ::cel::BoolType; +using ::cel::MakeFunctionDecl; +using ::cel::MakeOverloadDecl; +using ::cel::MakeVariableDecl; +using ::cel::StringType; +using ::google::api::expr::runtime::Activation; +using ::google::api::expr::runtime::BindProtoToActivation; +using ::google::api::expr::runtime::CelValue; +using ::google::api::expr::runtime::CreateCelExpressionBuilder; +using ::google::api::expr::runtime::FunctionAdapter; +using ::google::api::expr::runtime::RegisterBuiltinFunctions; +using ::google::api::expr::runtime::test::IsCelBool; +using ::google::rpc::context::AttributeContext; +using ::testing::HasSubstr; + +std::unique_ptr MakeDefaultCompilerBuilder() { + google::protobuf::LinkMessageReflection(); + auto builder = + cel::NewCompilerBuilder(google::protobuf::DescriptorPool::generated_pool()); + ABSL_CHECK_OK(builder.status()); + + ABSL_CHECK_OK((*builder)->AddLibrary(cel::StandardCompilerLibrary())); + ABSL_CHECK_OK((*builder)->GetCheckerBuilder().AddContextDeclaration( + "google.rpc.context.AttributeContext")); + + return std::move(builder).value(); +} + +TEST(DefaultCompiler, Basic) { + ASSERT_OK_AND_ASSIGN(auto compiler, MakeDefaultCompilerBuilder()->Build()); + EXPECT_THAT(compiler->Compile("1 < 2").status(), IsOk()); +} + +TEST(DefaultCompiler, AddFunctionDecl) { + auto builder = MakeDefaultCompilerBuilder(); + ASSERT_OK_AND_ASSIGN( + cel::FunctionDecl decl, + MakeFunctionDecl("IpMatch", + MakeOverloadDecl("IpMatch_string_string", BoolType(), + StringType(), StringType()))); + EXPECT_THAT(builder->GetCheckerBuilder().AddFunction(decl), IsOk()); + + ASSERT_OK_AND_ASSIGN(auto compiler, builder->Build()); + + EXPECT_THAT(CompileToCheckedExpr( + *compiler, "IpMatch('255.255.255.255', '255.255.255.255')") + .status(), + IsOk()); + EXPECT_THAT( + CompileToCheckedExpr(*compiler, "IpMatch('255.255.255.255', 123436)") + .status(), + StatusIs(absl::StatusCode::kInvalidArgument, + HasSubstr("no matching overload"))); +} + +TEST(DefaultCompiler, EndToEnd) { + google::protobuf::Arena arena; + + auto compiler_builder = MakeDefaultCompilerBuilder(); + ASSERT_OK_AND_ASSIGN( + cel::FunctionDecl func_decl, + MakeFunctionDecl("MyFunc", MakeOverloadDecl("MyFunc", BoolType()))); + ASSERT_THAT(compiler_builder->GetCheckerBuilder().AddFunction(func_decl), + IsOk()); + + ASSERT_THAT(compiler_builder->GetCheckerBuilder().AddVariable( + MakeVariableDecl("my_var", BoolType())), + IsOk()); + + ASSERT_OK_AND_ASSIGN(auto compiler, compiler_builder->Build()); + + ASSERT_OK_AND_ASSIGN( + auto expr, + CompileToCheckedExpr( + *compiler, + "(my_var || MyFunc()) && request.host == 'www.google.com'")); + + auto builder = + CreateCelExpressionBuilder(google::protobuf::DescriptorPool::generated_pool(), + google::protobuf::MessageFactory::generated_factory()); + ASSERT_THAT(RegisterBuiltinFunctions(builder->GetRegistry()), IsOk()); + ASSERT_THAT(FunctionAdapter::CreateAndRegister( + "MyFunc", false, [](google::protobuf::Arena*) { return true; }, + builder->GetRegistry()), + IsOk()); + + ASSERT_OK_AND_ASSIGN(auto plan, builder->CreateExpression(&expr)); + + AttributeContext context; + context.mutable_request()->set_host("www.google.com"); + Activation activation; + ASSERT_THAT(BindProtoToActivation(&context, &arena, &activation), IsOk()); + activation.InsertValue("my_var", CelValue::CreateBool(false)); + + ASSERT_OK_AND_ASSIGN(CelValue result, plan->Evaluate(activation, &arena)); + + EXPECT_THAT(result, IsCelBool(true)); +} + +} // namespace +} // namespace cel_codelab diff --git a/codelab/exercise4.cc b/codelab/exercise4.cc new file mode 100644 index 000000000..cf02a88bd --- /dev/null +++ b/codelab/exercise4.cc @@ -0,0 +1,132 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#include "codelab/exercise4.h" + +#include + +#include "cel/expr/checked.pb.h" +#include "google/rpc/context/attribute_context.pb.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "codelab/cel_compiler.h" +#include "compiler/compiler.h" +#include "compiler/compiler_factory.h" +#include "compiler/standard_library.h" +#include "eval/public/activation.h" +#include "eval/public/activation_bind_helper.h" +#include "eval/public/builtin_func_registrar.h" +#include "eval/public/cel_expr_builder_factory.h" +#include "eval/public/cel_expression.h" +#include "eval/public/cel_options.h" +#include "eval/public/cel_value.h" +#include "internal/status_macros.h" +#include "google/protobuf/arena.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" + +namespace cel_codelab { +namespace { + +using ::cel::expr::CheckedExpr; +using ::google::api::expr::runtime::Activation; +using ::google::api::expr::runtime::BindProtoToActivation; +using ::google::api::expr::runtime::CelError; +using ::google::api::expr::runtime::CelExpressionBuilder; +using ::google::api::expr::runtime::CelValue; +using ::google::api::expr::runtime::CreateCelExpressionBuilder; +using ::google::api::expr::runtime::InterpreterOptions; +using ::google::api::expr::runtime::RegisterBuiltinFunctions; +using ::google::rpc::context::AttributeContext; + +absl::StatusOr> MakeConfiguredCompiler() { + // Setup for handling for protobuf types. + // Using the generated descriptor pool is simpler to configure, but often + // adds more types than necessary. + google::protobuf::LinkMessageReflection(); + CEL_ASSIGN_OR_RETURN( + std::unique_ptr builder, + cel::NewCompilerBuilder(google::protobuf::DescriptorPool::generated_pool())); + CEL_RETURN_IF_ERROR(builder->AddLibrary(cel::StandardCompilerLibrary())); + // Adds fields of AttributeContext as variables. + CEL_RETURN_IF_ERROR(builder->GetCheckerBuilder().AddContextDeclaration( + AttributeContext::descriptor()->full_name())); + + // Codelab part 1: + // Add a declaration for the map.contains(string, V) function. + // Hint: use cel::MakeFunctionDecl and cel::TypeCheckerBuilder::MergeFunction. + return builder->Build(); +} + +class Evaluator { + public: + Evaluator() { + builder_ = CreateCelExpressionBuilder( + google::protobuf::DescriptorPool::generated_pool(), + google::protobuf::MessageFactory::generated_factory(), options_); + } + + absl::Status SetupEvaluatorEnvironment() { + CEL_RETURN_IF_ERROR(RegisterBuiltinFunctions(builder_->GetRegistry())); + // Codelab part 2: + // Register the map.contains(string, value) function. + // Hint: use `CelFunctionAdapter::CreateAndRegister` to adapt from a free + // function ContainsExtensionFunction. + return absl::OkStatus(); + } + + absl::StatusOr Evaluate(const CheckedExpr& expr, + const AttributeContext& context) { + Activation activation; + CEL_RETURN_IF_ERROR(BindProtoToActivation(&context, &arena_, &activation)); + CEL_ASSIGN_OR_RETURN(auto plan, builder_->CreateExpression(&expr)); + CEL_ASSIGN_OR_RETURN(CelValue result, plan->Evaluate(activation, &arena_)); + + if (bool value; result.GetValue(&value)) { + return value; + } else if (const CelError * value; result.GetValue(&value)) { + return *value; + } else { + return absl::InvalidArgumentError( + absl::StrCat("unexpected return type: ", result.DebugString())); + } + } + + private: + google::protobuf::Arena arena_; + std::unique_ptr builder_; + InterpreterOptions options_; +}; + +} // namespace + +absl::StatusOr EvaluateWithExtensionFunction( + absl::string_view expr, const AttributeContext& context) { + // Prepare a checked expression. + CEL_ASSIGN_OR_RETURN(std::unique_ptr compiler, + MakeConfiguredCompiler()); + CEL_ASSIGN_OR_RETURN(auto checked_expr, + CompileToCheckedExpr(*compiler, expr)); + + // Prepare an evaluation environment. + Evaluator evaluator; + CEL_RETURN_IF_ERROR(evaluator.SetupEvaluatorEnvironment()); + + // Evaluate a checked expression against a particular activation + return evaluator.Evaluate(checked_expr, context); +} + +} // namespace cel_codelab diff --git a/codelab/exercise4.h b/codelab/exercise4.h new file mode 100644 index 000000000..d015cebfb --- /dev/null +++ b/codelab/exercise4.h @@ -0,0 +1,34 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#ifndef THIRD_PARTY_CEL_CPP_CODELAB_EXERCISE4_H_ +#define THIRD_PARTY_CEL_CPP_CODELAB_EXERCISE4_H_ + +#include "google/rpc/context/attribute_context.pb.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" + +namespace cel_codelab { + +// Compile and evaluate an expression with google.rpc.context.AttributeContext +// as context. +// The environment includes the custom map member function +// .contains(string, string). +absl::StatusOr EvaluateWithExtensionFunction( + absl::string_view cel_expr, + const google::rpc::context::AttributeContext& context); + +} // namespace cel_codelab + +#endif // THIRD_PARTY_CEL_CPP_CODELAB_EXERCISE4_H_ diff --git a/codelab/exercise4_test.cc b/codelab/exercise4_test.cc new file mode 100644 index 000000000..f2f2044fa --- /dev/null +++ b/codelab/exercise4_test.cc @@ -0,0 +1,80 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#include "codelab/exercise4.h" + +#include "google/protobuf/struct.pb.h" +#include "google/rpc/context/attribute_context.pb.h" +#include "internal/testing.h" +#include "google/protobuf/text_format.h" + +namespace cel_codelab { +namespace { + +using ::absl_testing::IsOkAndHolds; +using ::google::rpc::context::AttributeContext; + +TEST(EvaluateWithExtensionFunction, Baseline) { + AttributeContext context; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"(request { + path: "/" + auth { + claims { + fields { + key: "group" + value {string_value: "admin"} + } + } + } + })", + &context)); + EXPECT_THAT(EvaluateWithExtensionFunction("request.path == '/'", context), + IsOkAndHolds(true)); +} + +TEST(EvaluateWithExtensionFunction, ContainsTrue) { + AttributeContext context; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"(request { + path: "/" + auth { + claims { + fields { + key: "group" + value {string_value: "admin"} + } + } + } + })", + &context)); + EXPECT_THAT(EvaluateWithExtensionFunction( + "request.auth.claims.contains('group', 'admin')", context), + IsOkAndHolds(true)); +} + +TEST(EvaluateWithExtensionFunction, ContainsFalse) { + AttributeContext context; + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + R"(request { + path: "/" + })", + &context)); + EXPECT_THAT(EvaluateWithExtensionFunction( + "request.auth.claims.contains('group', 'admin')", context), + IsOkAndHolds(false)); +} + +} // namespace +} // namespace cel_codelab diff --git a/codelab/solutions/BUILD b/codelab/solutions/BUILD index a85f0f668..60cfbf592 100644 --- a/codelab/solutions/BUILD +++ b/codelab/solutions/BUILD @@ -93,3 +93,44 @@ cc_test( "@com_google_protobuf//:protobuf", ], ) + +cc_library( + name = "exercise4", + srcs = ["exercise4.cc"], + hdrs = ["//codelab:exercise4.h"], + deps = [ + "//codelab:cel_compiler", + "//common:decl", + "//common:type", + "//compiler", + "//compiler:compiler_factory", + "//compiler:standard_library", + "//eval/public:activation", + "//eval/public:activation_bind_helper", + "//eval/public:builtin_func_registrar", + "//eval/public:cel_expr_builder_factory", + "//eval/public:cel_expression", + "//eval/public:cel_function_adapter", + "//eval/public:cel_options", + "//eval/public:cel_value", + "//internal:status_macros", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + "@com_google_cel_spec//proto/cel/expr:checked_cc_proto", + "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", + "@com_google_protobuf//:protobuf", + ], +) + +cc_test( + name = "exercise4_test", + srcs = ["//codelab:exercise4_test.cc"], + deps = [ + ":exercise4", + "//internal:testing", + "@com_google_googleapis//google/rpc/context:attribute_context_cc_proto", + "@com_google_protobuf//:protobuf", + ], +) diff --git a/codelab/solutions/exercise4.cc b/codelab/solutions/exercise4.cc index 924393b1c..f56789a4d 100644 --- a/codelab/solutions/exercise4.cc +++ b/codelab/solutions/exercise4.cc @@ -11,19 +11,24 @@ // 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. + #include "codelab/exercise4.h" #include -#include -#include #include "cel/expr/checked.pb.h" +#include "google/rpc/context/attribute_context.pb.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "codelab/cel_compiler.h" +#include "common/decl.h" +#include "common/type.h" +#include "compiler/compiler.h" +#include "compiler/compiler_factory.h" +#include "compiler/standard_library.h" #include "eval/public/activation.h" #include "eval/public/activation_bind_helper.h" #include "eval/public/builtin_func_registrar.h" @@ -34,14 +39,17 @@ #include "eval/public/cel_value.h" #include "internal/status_macros.h" #include "google/protobuf/arena.h" -#include "google/protobuf/text_format.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/message.h" -namespace google::api::expr::codelab { +namespace cel_codelab { namespace { +using ::cel::expr::CheckedExpr; using ::google::api::expr::runtime::Activation; using ::google::api::expr::runtime::BindProtoToActivation; using ::google::api::expr::runtime::CelError; +using ::google::api::expr::runtime::CelExpressionBuilder; using ::google::api::expr::runtime::CelMap; using ::google::api::expr::runtime::CelValue; using ::google::api::expr::runtime::CreateCelExpressionBuilder; @@ -50,61 +58,68 @@ using ::google::api::expr::runtime::InterpreterOptions; using ::google::api::expr::runtime::RegisterBuiltinFunctions; using ::google::rpc::context::AttributeContext; -absl::StatusOr ContainsExtensionFunction( - google::protobuf::Arena* arena, const CelMap* map, CelValue::StringHolder key, - CelValue::StringHolder expected_value) { +// Handle the parametric type overload with a single generic CelValue overload. +absl::StatusOr ContainsExtensionFunction(google::protobuf::Arena* arena, + const CelMap* map, + CelValue::StringHolder key, + const CelValue& value) { absl::optional entry = (*map)[CelValue::CreateString(key)]; - if (entry.has_value()) { - if (CelValue::StringHolder entry_value; entry->GetValue(&entry_value)) { - return entry_value.value() == expected_value.value(); - } + if (!entry.has_value()) { + return false; + } + if (value.IsInt64() && entry->IsInt64()) { + return value.Int64OrDie() == entry->Int64OrDie(); + } else if (value.IsString() && entry->IsString()) { + return value.StringOrDie().value() == entry->StringOrDie().value(); } return false; } -absl::StatusOr> MakeConfiguredCompiler() { - std::vector declarations; +absl::StatusOr> MakeConfiguredCompiler() { + // Setup for handling for protobuf types. + // Using the generated descriptor pool is simpler to configure, but often + // adds more types than necessary. + google::protobuf::LinkMessageReflection(); + CEL_ASSIGN_OR_RETURN( + std::unique_ptr builder, + cel::NewCompilerBuilder(google::protobuf::DescriptorPool::generated_pool())); + CEL_RETURN_IF_ERROR(builder->AddLibrary(cel::StandardCompilerLibrary())); + // Adds fields of AttributeContext as variables. + CEL_RETURN_IF_ERROR(builder->GetCheckerBuilder().AddContextDeclaration( + AttributeContext::descriptor()->full_name())); + // Codelab part 1: - // Add a declaration for the map.contains(string, string) function. - bool success = google::protobuf::TextFormat::ParseFromString( - R"pb( - name: "contains" - function { - overloads { - overload_id: "map_contains_string_string" - result_type { primitive: BOOL } - is_instance_function: true - params { - map_type { - key_type { primitive: STRING } - value_type { dyn {} } - } - } - params { primitive: STRING } - params { primitive: STRING } - } - })pb", - &declarations.emplace_back()); - if (!success) { - return absl::InternalError( - "Failed to parse Decl textproto in type check environment setup."); - } - return CreateCodelabCompiler(declarations); + // Add a declaration for the map.contains(string, V) function. + auto& checker_builder = builder->GetCheckerBuilder(); + CEL_ASSIGN_OR_RETURN( + cel::FunctionDecl decl, + cel::MakeFunctionDecl( + "contains", + cel::MakeMemberOverloadDecl( + "map_contains_string_string", cel::BoolType(), + cel::MapType(checker_builder.arena(), cel::StringType(), + cel::TypeParamType("V")), + cel::StringType(), cel::TypeParamType("V")))); + CEL_RETURN_IF_ERROR(checker_builder.MergeFunction(decl)); + return builder->Build(); } class Evaluator { public: - Evaluator() { builder_ = CreateCelExpressionBuilder(options_); } + Evaluator() { + builder_ = CreateCelExpressionBuilder( + google::protobuf::DescriptorPool::generated_pool(), + google::protobuf::MessageFactory::generated_factory(), options_); + } absl::Status SetupEvaluatorEnvironment() { CEL_RETURN_IF_ERROR(RegisterBuiltinFunctions(builder_->GetRegistry())); // Codelab part 2: // Register the map.contains(string, string) function. - // Hint: use `CelFunctionAdapter::CreateAndRegister` to adapt from - // ContainsExtensionFunction. - using AdapterT = - FunctionAdapter, const CelMap*, - CelValue::StringHolder, CelValue::StringHolder>; + // Hint: use `FunctionAdapter::CreateAndRegister` to adapt from a free + // function ContainsExtensionFunction. + using AdapterT = FunctionAdapter, const CelMap*, + CelValue::StringHolder, CelValue>; CEL_RETURN_IF_ERROR(AdapterT::CreateAndRegister( "contains", /*receiver_style=*/true, &ContainsExtensionFunction, builder_->GetRegistry())); @@ -130,7 +145,7 @@ class Evaluator { private: google::protobuf::Arena arena_; - std::unique_ptr builder_; + std::unique_ptr builder_; InterpreterOptions options_; }; @@ -139,17 +154,17 @@ class Evaluator { absl::StatusOr EvaluateWithExtensionFunction( absl::string_view expr, const AttributeContext& context) { // Prepare a checked expression. - // Prepare a checked expression. - CEL_ASSIGN_OR_RETURN(std::unique_ptr compiler, + CEL_ASSIGN_OR_RETURN(std::unique_ptr compiler, MakeConfiguredCompiler()); - CEL_ASSIGN_OR_RETURN(auto checked_expr, compiler->Compile(expr)); + CEL_ASSIGN_OR_RETURN(auto checked_expr, + CompileToCheckedExpr(*compiler, expr)); // Prepare an evaluation environment. Evaluator evaluator; CEL_RETURN_IF_ERROR(evaluator.SetupEvaluatorEnvironment()); - // Evaluate a checked expression against a particular activation; + // Evaluate a checked expression against a particular activation return evaluator.Evaluate(checked_expr, context); } -} // namespace google::api::expr::codelab +} // namespace cel_codelab