Skip to content

Commit 3d5cfd6

Browse files
l46kokcopybara-github
authored andcommitted
Support for typename import aliases in policy compiler
PiperOrigin-RevId: 786809539
1 parent 712b265 commit 3d5cfd6

13 files changed

Lines changed: 156 additions & 12 deletions

File tree

checker/src/main/java/dev/cel/checker/CelCheckerBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ public interface CelCheckerBuilder {
4949
@CanIgnoreReturnValue
5050
CelCheckerBuilder setContainer(CelContainer container);
5151

52+
/** Retrieves the currently configured {@link CelContainer} in the builder. */
53+
CelContainer container();
54+
5255
/** Add variable and function {@code declarations} to the CEL environment. */
5356
@CanIgnoreReturnValue
5457
CelCheckerBuilder addDeclarations(Decl... declarations);

checker/src/main/java/dev/cel/checker/CelCheckerLegacyImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,11 @@ public CelCheckerBuilder setContainer(CelContainer container) {
210210
return this;
211211
}
212212

213+
@Override
214+
public CelContainer container() {
215+
return this.container;
216+
}
217+
213218
@Override
214219
public CelCheckerBuilder addDeclarations(Decl... declarations) {
215220
checkNotNull(declarations);

common/src/main/java/dev/cel/common/CelContainer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ public Builder addAbbreviations(String... qualifiedNames) {
108108
* <p>If there is ever a case where an identifier could be in both the container and as an
109109
* abbreviation, the abbreviation wins as this will ensure that the meaning of a program is
110110
* preserved between compilations even as the container evolves.
111+
*
112+
* @throws IllegalArgumentException If qualifiedName is invalid per above specification.
111113
*/
112114
@CanIgnoreReturnValue
113115
public Builder addAbbreviations(ImmutableSet<String> qualifiedNames) {
@@ -250,6 +252,8 @@ public ImmutableSet<String> resolveCandidateNames(String typeName) {
250252
return candidates.add(typeName).build();
251253
}
252254

255+
public abstract Builder toBuilder();
256+
253257
public static Builder newBuilder() {
254258
return new AutoValue_CelContainer.Builder().setName("");
255259
}

policy/src/main/java/dev/cel/policy/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ java_library(
206206
"//common:cel_ast",
207207
"//common:cel_source",
208208
"//common:compiler_common",
209+
"//common:container",
209210
"//common:source_location",
210211
"//common/ast",
211212
"//common/formats:value_string",

policy/src/main/java/dev/cel/policy/CelPolicy.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public abstract class CelPolicy {
4646

4747
public abstract ImmutableMap<String, Object> metadata();
4848

49+
public abstract ImmutableList<Import> imports();
50+
4951
/** Creates a new builder to construct a {@link CelPolicy} instance. */
5052
public static Builder newBuilder() {
5153
return new AutoValue_CelPolicy.Builder()
@@ -74,6 +76,16 @@ public abstract static class Builder {
7476

7577
public abstract Builder setMetadata(ImmutableMap<String, Object> value);
7678

79+
abstract ImmutableList.Builder<Import> importsBuilder();
80+
81+
abstract Builder setImports(ImmutableList<Import> value);
82+
83+
@CanIgnoreReturnValue
84+
public Builder addImport(Import value) {
85+
importsBuilder().add(value);
86+
return this;
87+
}
88+
7789
@CanIgnoreReturnValue
7890
public Builder putMetadata(String key, Object value) {
7991
metadataBuilder().put(key, value);
@@ -278,4 +290,16 @@ public static Builder newBuilder() {
278290
return new AutoValue_CelPolicy_Variable.Builder();
279291
}
280292
}
293+
294+
/** Import represents an imported type name which is aliased within CEL expressions. */
295+
@AutoValue
296+
public abstract static class Import {
297+
public abstract long id();
298+
299+
public abstract ValueString name();
300+
301+
public static Import create(long id, ValueString name) {
302+
return new AutoValue_CelPolicy_Import(id, name);
303+
}
304+
}
281305
}

policy/src/main/java/dev/cel/policy/CelPolicyCompilerImpl.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.google.errorprone.annotations.CanIgnoreReturnValue;
2222
import dev.cel.bundle.Cel;
2323
import dev.cel.common.CelAbstractSyntaxTree;
24+
import dev.cel.common.CelContainer;
2425
import dev.cel.common.CelIssue;
2526
import dev.cel.common.CelSource;
2627
import dev.cel.common.CelSourceLocation;
@@ -39,6 +40,7 @@
3940
import dev.cel.policy.CelCompiledRule.CelCompiledMatch.Result;
4041
import dev.cel.policy.CelCompiledRule.CelCompiledMatch.Result.Kind;
4142
import dev.cel.policy.CelCompiledRule.CelCompiledVariable;
43+
import dev.cel.policy.CelPolicy.Import;
4244
import dev.cel.policy.CelPolicy.Match;
4345
import dev.cel.policy.CelPolicy.Variable;
4446
import dev.cel.policy.RuleComposer.RuleCompositionException;
@@ -63,7 +65,28 @@ final class CelPolicyCompilerImpl implements CelPolicyCompiler {
6365
@Override
6466
public CelCompiledRule compileRule(CelPolicy policy) throws CelPolicyValidationException {
6567
CompilerContext compilerContext = new CompilerContext(policy.policySource());
66-
CelCompiledRule compiledRule = compileRuleImpl(policy.rule(), cel, compilerContext);
68+
69+
Cel extendedCel = this.cel;
70+
71+
if (!policy.imports().isEmpty()) {
72+
CelContainer.Builder containerBuilder =
73+
extendedCel.toCheckerBuilder().container().toBuilder();
74+
75+
for (Import imp : policy.imports()) {
76+
try {
77+
containerBuilder.addAbbreviations(imp.name().value());
78+
} catch (IllegalArgumentException e) {
79+
compilerContext.addIssue(
80+
imp.id(),
81+
CelIssue.formatError(
82+
1, 0, String.format("Error configuring import: %s", e.getMessage())));
83+
}
84+
}
85+
86+
extendedCel = extendedCel.toCelBuilder().setContainer(containerBuilder.build()).build();
87+
}
88+
89+
CelCompiledRule compiledRule = compileRuleImpl(policy.rule(), extendedCel, compilerContext);
6790
if (compilerContext.hasError()) {
6891
throw new CelPolicyValidationException(compilerContext.getIssueString());
6992
}

policy/src/main/java/dev/cel/policy/CelPolicyYamlParser.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import dev.cel.common.formats.YamlHelper.YamlNodeType;
2828
import dev.cel.common.formats.YamlParserContextImpl;
2929
import dev.cel.common.internal.CelCodePointArray;
30+
import dev.cel.policy.CelPolicy.Import;
3031
import dev.cel.policy.CelPolicy.Match;
3132
import dev.cel.policy.CelPolicy.Match.Result;
3233
import dev.cel.policy.CelPolicy.Variable;
@@ -118,6 +119,9 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
118119
Node valueNode = nodeTuple.getValueNode();
119120
String fieldName = ((ScalarNode) keyNode).getValue();
120121
switch (fieldName) {
122+
case "imports":
123+
parseImports(policyBuilder, ctx, valueNode);
124+
break;
121125
case "name":
122126
policyBuilder.setName(ctx.newValueString(valueNode));
123127
break;
@@ -141,6 +145,51 @@ public CelPolicy parsePolicy(PolicyParserContext<Node> ctx, Node node) {
141145
.build();
142146
}
143147

148+
private void parseImports(
149+
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
150+
long id = ctx.collectMetadata(node);
151+
if (!assertYamlType(ctx, id, node, YamlNodeType.LIST)) {
152+
return;
153+
}
154+
155+
SequenceNode importListNode = (SequenceNode) node;
156+
for (Node importNode : importListNode.getValue()) {
157+
parseImport(policyBuilder, ctx, importNode);
158+
}
159+
}
160+
161+
private void parseImport(
162+
CelPolicy.Builder policyBuilder, PolicyParserContext<Node> ctx, Node node) {
163+
long importId = ctx.collectMetadata(node);
164+
if (!assertYamlType(ctx, importId, node, YamlNodeType.MAP)) {
165+
return;
166+
}
167+
168+
MappingNode mappingNode = (MappingNode) node;
169+
for (NodeTuple nodeTuple : mappingNode.getValue()) {
170+
Node key = nodeTuple.getKeyNode();
171+
long keyId = ctx.collectMetadata(key);
172+
if (!assertYamlType(ctx, keyId, key, YamlNodeType.STRING, YamlNodeType.TEXT)) {
173+
continue;
174+
}
175+
176+
String fieldName = ((ScalarNode) key).getValue();
177+
if (!fieldName.equals("name")) {
178+
ctx.reportError(
179+
keyId, String.format("Invalid import key: %s, expected 'name'", fieldName));
180+
continue;
181+
}
182+
183+
Node value = nodeTuple.getValueNode();
184+
long valueId = ctx.collectMetadata(value);
185+
if (!assertYamlType(ctx, valueId, value, YamlNodeType.STRING, YamlNodeType.TEXT)) {
186+
continue;
187+
}
188+
189+
policyBuilder.addImport(Import.create(valueId, ctx.newValueString(value)));
190+
}
191+
}
192+
144193
@Override
145194
public CelPolicy.Rule parseRule(
146195
PolicyParserContext<Node> ctx, CelPolicy.Builder policyBuilder, Node node) {

policy/src/test/java/dev/cel/policy/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ java_library(
1919
"//common:options",
2020
"//common/formats:value_string",
2121
"//common/internal",
22+
"//common/resources/testdata/proto3:standalone_global_enum_java_proto",
2223
"//compiler",
2324
"//extensions:optional_library",
2425
"//parser:macro",

policy/src/test/java/dev/cel/policy/CelPolicyCompilerImplTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import dev.cel.policy.PolicyTestHelper.TestYamlPolicy;
4444
import dev.cel.runtime.CelFunctionBinding;
4545
import dev.cel.runtime.CelLateFunctionBindings;
46+
import dev.cel.testing.testdata.proto3.StandaloneGlobalEnum;
4647
import java.io.IOException;
4748
import java.util.Map;
4849
import java.util.Optional;
@@ -290,6 +291,7 @@ private static Cel newCel() {
290291
.setStandardMacros(CelStandardMacro.STANDARD_MACROS)
291292
.addCompilerLibraries(CelOptionalLibrary.INSTANCE)
292293
.addRuntimeLibraries(CelOptionalLibrary.INSTANCE)
294+
.addFileTypes(StandaloneGlobalEnum.getDescriptor().getFile())
293295
.addMessageTypes(TestAllTypes.getDescriptor())
294296
.setOptions(CEL_OPTIONS)
295297
.addFunctionBindings(

policy/src/test/java/dev/cel/policy/PolicyTestHelper.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,13 @@ enum TestYamlPolicy {
106106
PB(
107107
"pb",
108108
true,
109-
"(spec.single_int32 > 10) ? optional.of(\"invalid spec, got single_int32=\" +"
110-
+ " string(spec.single_int32) + \", wanted <= 10\") : optional.none()"),
109+
"(spec.single_int32 > TestAllTypes{single_int64: 10}.single_int64) ? optional.of(\"invalid"
110+
+ " spec, got single_int32=\" + string(spec.single_int32) + \", wanted <= 10\") :"
111+
+ " ((spec.standalone_enum == cel.expr.conformance.proto3.TestAllTypes.NestedEnum.BAR"
112+
+ " || dev.cel.testing.testdata.proto3.StandaloneGlobalEnum.SGAR =="
113+
+ " dev.cel.testing.testdata.proto3.StandaloneGlobalEnum.SGOO) ? optional.of(\"invalid"
114+
+ " spec, neither nested nor imported enums may refer to BAR or SBAR\") :"
115+
+ " optional.none())"),
111116
LIMITS(
112117
"limits",
113118
true,

0 commit comments

Comments
 (0)