Skip to content

Commit 228f121

Browse files
jnthntatumcopybara-github
authored andcommitted
Add support for context types env.yaml
Add support for declaring a protobuf context message whose top level fields are declared as variables in the CEL environment. PiperOrigin-RevId: 930613750
1 parent d2a8a78 commit 228f121

6 files changed

Lines changed: 102 additions & 0 deletions

File tree

env/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ cc_library(
130130
"@com_google_absl//absl/base:no_destructor",
131131
"@com_google_absl//absl/container:flat_hash_map",
132132
"@com_google_absl//absl/container:flat_hash_set",
133+
"@com_google_absl//absl/log:absl_check",
133134
"@com_google_absl//absl/status",
134135
"@com_google_absl//absl/status:statusor",
135136
"@com_google_absl//absl/strings",

env/config.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class Config {
3232
void SetName(std::string name) { name_ = std::move(name); }
3333
std::string GetName() const { return name_; }
3434

35+
void SetContextType(std::string context_type) {
36+
context_type_ = std::move(context_type);
37+
}
38+
std::string GetContextType() const { return context_type_; }
39+
3540
struct ContainerConfig {
3641
std::string name;
3742
std::vector<std::string> abbreviations;
@@ -150,6 +155,7 @@ class Config {
150155

151156
private:
152157
std::string name_;
158+
std::string context_type_;
153159
ContainerConfig container_config_;
154160
std::vector<ExtensionConfig> extension_configs_;
155161
StandardLibraryConfig standard_library_config_;

env/env.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ absl::StatusOr<std::unique_ptr<CompilerBuilder>> Env::NewCompilerBuilder() {
138138
for (const auto& abbr : config_.GetContainerConfig().abbreviations) {
139139
CEL_RETURN_IF_ERROR(container.AddAbbreviation(abbr));
140140
}
141+
142+
if (!config_.GetContextType().empty()) {
143+
CEL_RETURN_IF_ERROR(
144+
checker_builder.AddContextDeclaration(config_.GetContextType()));
145+
}
141146
for (const auto& alias : config_.GetContainerConfig().aliases) {
142147
CEL_RETURN_IF_ERROR(container.AddAlias(alias.alias, alias.qualified_name));
143148
}

env/env_test.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,25 @@ TEST(ContainerConfigTest, ContainerConfigWithAliases) {
344344
EXPECT_THAT(result.GetIssues(), IsEmpty()) << result.FormatError();
345345
}
346346

347+
TEST(ContextVariableConfigTest, Basic) {
348+
Env env;
349+
env.SetDescriptorPool(internal::GetSharedTestingDescriptorPool());
350+
Config config;
351+
config.SetContextType("cel.expr.conformance.proto3.TestAllTypes");
352+
env.SetConfig(config);
353+
354+
ASSERT_OK_AND_ASSIGN(std::unique_ptr<Compiler> compiler, env.NewCompiler());
355+
356+
// Top-level fields of TestAllTypes like "single_int32" should resolve
357+
// successfully.
358+
ASSERT_OK_AND_ASSIGN(auto result, compiler->Compile("single_int32 > 10"));
359+
EXPECT_THAT(result.GetIssues(), IsEmpty());
360+
361+
ASSERT_OK_AND_ASSIGN(auto result_invalid,
362+
compiler->Compile("non_existent_field > 10"));
363+
EXPECT_THAT(result_invalid.GetIssues(), Not(IsEmpty()));
364+
}
365+
347366
struct VariableConfigWithValueTestCase {
348367
Config::VariableConfig variable_config;
349368
std::string validate_type_expr;

env/env_yaml.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "absl/base/no_destructor.h"
2727
#include "absl/container/flat_hash_map.h"
2828
#include "absl/container/flat_hash_set.h"
29+
#include "absl/log/absl_check.h"
2930
#include "absl/status/status.h"
3031
#include "absl/status/statusor.h"
3132
#include "absl/strings/escaping.h"
@@ -1245,6 +1246,34 @@ void EmitFunctionConfigs(const Config& env_config, YAML::Emitter& out,
12451246
}
12461247
out << YAML::EndSeq;
12471248
}
1249+
1250+
absl::Status ParseContextVariableConfig(Config& config, absl::string_view yaml,
1251+
const YAML::Node& root) {
1252+
const YAML::Node context_variable = root["context_variable"];
1253+
if (!context_variable.IsDefined()) {
1254+
return absl::OkStatus();
1255+
}
1256+
if (!context_variable.IsMap()) {
1257+
return YamlError(yaml, context_variable,
1258+
"Node 'context_variable' is not a map");
1259+
}
1260+
1261+
const YAML::Node type_name = context_variable["type_name"];
1262+
const YAML::Node type = context_variable["type"];
1263+
const YAML::Node* type_node = nullptr;
1264+
if (type.IsDefined() && type.IsScalar()) {
1265+
type_node = &type;
1266+
} else if (type_name.IsDefined() && type_name.IsScalar()) {
1267+
type_node = &type_name;
1268+
} else {
1269+
return YamlError(yaml, context_variable,
1270+
"Node 'context_variable' does not have a valid type");
1271+
}
1272+
ABSL_DCHECK(type_node != nullptr);
1273+
config.SetContextType(GetString(yaml, *type_node));
1274+
return absl::OkStatus();
1275+
}
1276+
12481277
} // namespace
12491278

12501279
absl::StatusOr<Config> EnvConfigFromYaml(const std::string& yaml) {
@@ -1263,6 +1292,7 @@ absl::StatusOr<Config> EnvConfigFromYaml(const std::string& yaml) {
12631292
CEL_RETURN_IF_ERROR(ParseContainerConfig(config, yaml, root));
12641293
CEL_RETURN_IF_ERROR(ParseExtensionConfigs(config, yaml, root));
12651294
CEL_RETURN_IF_ERROR(ParseStandardLibraryConfig(config, yaml, root));
1295+
CEL_RETURN_IF_ERROR(ParseContextVariableConfig(config, yaml, root));
12661296
CEL_RETURN_IF_ERROR(ParseVariableConfigs(config, yaml, root));
12671297
CEL_RETURN_IF_ERROR(ParseFunctionConfigs(config, yaml, root));
12681298
return config;

env/env_yaml_test.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,47 @@ TEST(EnvYamlTest, ParseVariableConfigWithTypeParams) {
216216
EXPECT_THAT(type_info.params[1].params, IsEmpty());
217217
}
218218

219+
TEST(EnvYamlTest, ParseContextVariableConfig) {
220+
ASSERT_OK_AND_ASSIGN(Config config, EnvConfigFromYaml(R"yaml(
221+
context_variable:
222+
type_name: "cel.expr.conformance.proto3.TestAllTypes"
223+
)yaml"));
224+
225+
EXPECT_EQ(config.GetContextType(),
226+
"cel.expr.conformance.proto3.TestAllTypes");
227+
}
228+
229+
TEST(EnvYamlTest, ParseContextVariableConfigAlternativeSyntax) {
230+
ASSERT_OK_AND_ASSIGN(Config config, EnvConfigFromYaml(R"yaml(
231+
context_variable:
232+
type: "cel.expr.conformance.proto3.TestAllTypes"
233+
)yaml"));
234+
235+
EXPECT_EQ(config.GetContextType(),
236+
"cel.expr.conformance.proto3.TestAllTypes");
237+
}
238+
239+
TEST(EnvYamlTest, ParseContextVariableMalformedContextVariable) {
240+
EXPECT_THAT(EnvConfigFromYaml(R"yaml(
241+
context_variable: 123
242+
243+
)yaml"),
244+
StatusIs(absl::StatusCode::kInvalidArgument,
245+
HasSubstr("Node 'context_variable' is not a map")));
246+
}
247+
248+
TEST(EnvYamlTest, ParseContextVariableMalformedContextVariable2) {
249+
EXPECT_THAT(
250+
EnvConfigFromYaml(R"yaml(
251+
context_variable:
252+
type:
253+
foo: bar
254+
)yaml"),
255+
StatusIs(
256+
absl::StatusCode::kInvalidArgument,
257+
HasSubstr("Node 'context_variable' does not have a valid type")));
258+
}
259+
219260
TEST(EnvYamlTest, ParseVariableConfigWithTypeParamsLegacySyntax) {
220261
ASSERT_OK_AND_ASSIGN(Config config, EnvConfigFromYaml(R"yaml(
221262
variables:

0 commit comments

Comments
 (0)