Skip to content

Commit 0a8afcc

Browse files
dmitriplotnikovcopybara-github
authored andcommitted
Add support for container aliases to CelEnvironment
PiperOrigin-RevId: 843882538
1 parent a11aa1e commit 0a8afcc

File tree

11 files changed

+373
-29
lines changed

11 files changed

+373
-29
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ java_library(
7373
"//common/types:type_providers",
7474
"//compiler:compiler_builder",
7575
"//extensions",
76-
"//extensions:optional_library",
7776
"//parser:macro",
7877
"//runtime",
7978
"@maven//:com_google_errorprone_error_prone_annotations",
@@ -103,6 +102,7 @@ java_library(
103102
":environment",
104103
":environment_exception",
105104
"//common:compiler_common",
105+
"//common:container",
106106
"//common/formats:file_source",
107107
"//common/formats:parser_context",
108108
"//common/formats:yaml_helper",

bundle/src/main/java/dev/cel/bundle/CelEnvironment.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,9 @@ public abstract class CelEnvironment {
8080
public abstract String name();
8181

8282
/**
83-
* An optional description of the config (example: location of the file containing the config
84-
* content).
83+
* Container, which captures default namespace and aliases for value resolution.
8584
*/
86-
public abstract String container();
85+
public abstract CelContainer container();
8786

8887
/**
8988
* An optional description of the environment (example: location of the file containing the config
@@ -124,7 +123,12 @@ public abstract static class Builder {
124123

125124
public abstract Builder setDescription(String description);
126125

127-
public abstract Builder setContainer(String container);
126+
public abstract Builder setContainer(CelContainer container);
127+
128+
@CanIgnoreReturnValue
129+
public Builder setContainer(String container) {
130+
return setContainer(CelContainer.ofName(container));
131+
}
128132

129133
@CanIgnoreReturnValue
130134
public Builder addExtensions(ExtensionConfig... extensions) {
@@ -182,7 +186,7 @@ public static Builder newBuilder() {
182186
return new AutoValue_CelEnvironment.Builder()
183187
.setName("")
184188
.setDescription("")
185-
.setContainer("")
189+
.setContainer(CelContainer.ofName(""))
186190
.setVariables(ImmutableSet.of())
187191
.setFunctions(ImmutableSet.of());
188192
}
@@ -195,8 +199,8 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
195199
CelCompilerBuilder compilerBuilder =
196200
celCompiler
197201
.toCompilerBuilder()
202+
.setContainer(container())
198203
.setTypeProvider(celTypeProvider)
199-
.setContainer(CelContainer.ofName(container()))
200204
.addVarDeclarations(
201205
variables().stream()
202206
.map(v -> v.toCelVarDecl(celTypeProvider))
@@ -206,10 +210,6 @@ public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
206210
.map(f -> f.toCelFunctionDecl(celTypeProvider))
207211
.collect(toImmutableList()));
208212

209-
if (!container().isEmpty()) {
210-
compilerBuilder.setContainer(CelContainer.ofName(container()));
211-
}
212-
213213
addAllCompilerExtensions(compilerBuilder, celOptions);
214214

215215
applyStandardLibrarySubset(compilerBuilder);
@@ -683,6 +683,38 @@ public static ExtensionConfig latest(String name) {
683683
}
684684
}
685685

686+
@AutoValue
687+
abstract static class Alias {
688+
abstract String alias();
689+
690+
abstract String qualifiedName();
691+
692+
static Builder newBuilder() {
693+
return new AutoValue_CelEnvironment_Alias.Builder();
694+
}
695+
696+
@AutoValue.Builder
697+
abstract static class Builder implements RequiredFieldsChecker {
698+
699+
abstract Optional<String> alias();
700+
701+
abstract Optional<String> qualifiedName();
702+
703+
abstract Builder setAlias(String alias);
704+
705+
abstract Builder setQualifiedName(String qualifiedName);
706+
707+
abstract Alias build();
708+
709+
@Override
710+
public ImmutableList<RequiredField> requiredFields() {
711+
return ImmutableList.of(
712+
RequiredField.of("alias", this::alias),
713+
RequiredField.of("qualified_name", this::qualifiedName));
714+
}
715+
}
716+
}
717+
686718
@VisibleForTesting
687719
enum CanonicalCelExtension {
688720
BINDINGS((options, version) -> CelExtensions.bindings()),

bundle/src/main/java/dev/cel/bundle/CelEnvironmentExporter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ public static CelEnvironmentExporter.Builder newBuilder() {
161161
* </ul>
162162
*/
163163
public CelEnvironment export(Cel cel) {
164+
CelEnvironment.Builder envBuilder =
165+
CelEnvironment.newBuilder().setContainer(cel.toCheckerBuilder().container());
166+
164167
// Inventory is a full set of declarations and macros that are found in the configuration of
165168
// the supplied CEL instance.
166169
//
@@ -171,8 +174,6 @@ public CelEnvironment export(Cel cel) {
171174

172175
Set<Object> inventory = new HashSet<>();
173176
collectInventory(inventory, cel);
174-
175-
CelEnvironment.Builder envBuilder = CelEnvironment.newBuilder();
176177
addExtensionConfigsAndRemoveFromInventory(envBuilder, inventory);
177178
addStandardLibrarySubsetAndRemoveFromInventory(envBuilder, inventory);
178179
addCustomDecls(envBuilder, inventory);

bundle/src/main/java/dev/cel/bundle/CelEnvironmentYamlParser.java

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.common.collect.ImmutableList;
2828
import com.google.common.collect.ImmutableSet;
2929
import com.google.errorprone.annotations.CanIgnoreReturnValue;
30+
import dev.cel.bundle.CelEnvironment.Alias;
3031
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
3132
import dev.cel.bundle.CelEnvironment.FunctionDecl;
3233
import dev.cel.bundle.CelEnvironment.LibrarySubset;
@@ -35,6 +36,7 @@
3536
import dev.cel.bundle.CelEnvironment.OverloadDecl;
3637
import dev.cel.bundle.CelEnvironment.TypeDecl;
3738
import dev.cel.bundle.CelEnvironment.VariableDecl;
39+
import dev.cel.common.CelContainer;
3840
import dev.cel.common.CelIssue;
3941
import dev.cel.common.formats.CelFileSource;
4042
import dev.cel.common.formats.ParserContext;
@@ -64,6 +66,8 @@ public final class CelEnvironmentYamlParser {
6466
private static final ExtensionConfig ERROR_EXTENSION_DECL = ExtensionConfig.of(ERROR);
6567
private static final FunctionSelector ERROR_FUNCTION_SELECTOR =
6668
FunctionSelector.create(ERROR, ImmutableSet.of());
69+
private static final Alias ERROR_ALIAS =
70+
Alias.newBuilder().setAlias(ERROR).setQualifiedName(ERROR).build();
6771

6872
/** Generates a new instance of {@code CelEnvironmentYamlParser}. */
6973
public static CelEnvironmentYamlParser newInstance() {
@@ -88,6 +92,124 @@ public CelEnvironment parse(String environmentYamlSource, String description)
8892
return parser.parseYaml(environmentYamlSource, description);
8993
}
9094

95+
private CelContainer parseContainer(ParserContext<Node> ctx, Node node) {
96+
long valueId = ctx.collectMetadata(node);
97+
// Syntax variant 1: "container: `str`"
98+
if (validateYamlType(node, YamlNodeType.STRING, YamlNodeType.TEXT)) {
99+
return CelContainer.ofName(newString(ctx, node));
100+
}
101+
102+
// Syntax variant 2:
103+
// container
104+
// name: str
105+
// abbreviations:
106+
// - a1
107+
// - a2
108+
// aliases:
109+
// - alias: a1
110+
// qualified_name: q1
111+
// - alias: a2
112+
// qualified_name: q2
113+
if (!assertYamlType(ctx, valueId, node, YamlNodeType.MAP)) {
114+
return CelContainer.ofName(ERROR);
115+
}
116+
117+
CelContainer.Builder builder = CelContainer.newBuilder();
118+
MappingNode variableMap = (MappingNode) node;
119+
for (NodeTuple nodeTuple : variableMap.getValue()) {
120+
Node keyNode = nodeTuple.getKeyNode();
121+
long keyId = ctx.collectMetadata(keyNode);
122+
Node valueNode = nodeTuple.getValueNode();
123+
String keyName = ((ScalarNode) keyNode).getValue();
124+
switch (keyName) {
125+
case "name":
126+
builder.setName(newString(ctx, valueNode));
127+
break;
128+
case "aliases":
129+
ImmutableSet<Alias> aliases = parseAliases(ctx, valueNode);
130+
for (Alias alias : aliases) {
131+
builder.addAlias(alias.alias(), alias.qualifiedName());
132+
}
133+
break;
134+
case "abbreviations":
135+
builder.addAbbreviations(parseAbbreviations(ctx, valueNode));
136+
break;
137+
default:
138+
ctx.reportError(keyId, String.format("Unsupported container tag: %s", keyName));
139+
break;
140+
}
141+
}
142+
143+
return builder.build();
144+
}
145+
146+
private ImmutableSet<Alias> parseAliases(ParserContext<Node> ctx, Node node) {
147+
ImmutableSet.Builder<Alias> aliasSetBuilder = ImmutableSet.builder();
148+
long valueId = ctx.collectMetadata(node);
149+
if (!assertYamlType(ctx, valueId, node, YamlNodeType.LIST)) {
150+
return aliasSetBuilder.build();
151+
}
152+
153+
SequenceNode variableListNode = (SequenceNode) node;
154+
for (Node elementNode : variableListNode.getValue()) {
155+
aliasSetBuilder.add(parseAlias(ctx, elementNode));
156+
}
157+
158+
return aliasSetBuilder.build();
159+
}
160+
161+
private Alias parseAlias(ParserContext<Node> ctx, Node node) {
162+
long id = ctx.collectMetadata(node);
163+
if (!assertYamlType(ctx, id, node, YamlNodeType.MAP)) {
164+
return ERROR_ALIAS;
165+
}
166+
167+
Alias.Builder builder = Alias.newBuilder();
168+
MappingNode attrMap = (MappingNode) node;
169+
for (NodeTuple nodeTuple : attrMap.getValue()) {
170+
Node keyNode = nodeTuple.getKeyNode();
171+
long keyId = ctx.collectMetadata(keyNode);
172+
Node valueNode = nodeTuple.getValueNode();
173+
String keyName = ((ScalarNode) keyNode).getValue();
174+
switch (keyName) {
175+
case "alias":
176+
builder.setAlias(newString(ctx, valueNode));
177+
break;
178+
case "qualified_name":
179+
builder.setQualifiedName(newString(ctx, valueNode));
180+
break;
181+
default:
182+
ctx.reportError(keyId, String.format("Unsupported alias tag: %s", keyName));
183+
break;
184+
}
185+
}
186+
187+
if (!assertRequiredFields(ctx, id, builder.getMissingRequiredFieldNames())) {
188+
return ERROR_ALIAS;
189+
}
190+
191+
return builder.build();
192+
}
193+
194+
private ImmutableSet<String> parseAbbreviations(ParserContext<Node> ctx, Node node) {
195+
long valueId = ctx.collectMetadata(node);
196+
if (!assertYamlType(ctx, valueId, node, YamlNodeType.LIST)) {
197+
return ImmutableSet.of(ERROR);
198+
}
199+
200+
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
201+
SequenceNode nameListNode = (SequenceNode) node;
202+
for (Node elementNode : nameListNode.getValue()) {
203+
long elementId = ctx.collectMetadata(elementNode);
204+
if (!assertYamlType(ctx, elementId, elementNode, YamlNodeType.STRING)) {
205+
return ImmutableSet.of(ERROR);
206+
}
207+
208+
builder.add(((ScalarNode) elementNode).getValue());
209+
}
210+
return builder.build();
211+
}
212+
91213
private ImmutableSet<VariableDecl> parseVariables(ParserContext<Node> ctx, Node node) {
92214
long valueId = ctx.collectMetadata(node);
93215
ImmutableSet.Builder<VariableDecl> variableSetBuilder = ImmutableSet.builder();
@@ -620,7 +742,7 @@ private CelEnvironment.Builder parseConfig(ParserContext<Node> ctx, Node node) {
620742
builder.setDescription(newString(ctx, valueNode));
621743
break;
622744
case "container":
623-
builder.setContainer(newString(ctx, valueNode));
745+
builder.setContainer(parseContainer(ctx, valueNode));
624746
break;
625747
case "variables":
626748
builder.setVariables(parseVariables(ctx, valueNode));

bundle/src/main/java/dev/cel/bundle/CelEnvironmentYamlSerializer.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@
1616

1717
import com.google.common.collect.ImmutableList;
1818
import com.google.common.collect.ImmutableMap;
19+
import dev.cel.bundle.CelEnvironment.Alias;
1920
import dev.cel.bundle.CelEnvironment.LibrarySubset;
2021
import dev.cel.bundle.CelEnvironment.LibrarySubset.FunctionSelector;
2122
import dev.cel.bundle.CelEnvironment.LibrarySubset.OverloadSelector;
23+
import dev.cel.common.CelContainer;
2224
import java.util.Comparator;
25+
import java.util.Map;
2326
import org.yaml.snakeyaml.DumperOptions;
2427
import org.yaml.snakeyaml.Yaml;
2528
import org.yaml.snakeyaml.nodes.Node;
@@ -55,6 +58,8 @@ private CelEnvironmentYamlSerializer() {
5558
CelEnvironment.LibrarySubset.FunctionSelector.class, new RepresentFunctionSelector());
5659
this.multiRepresenters.put(
5760
CelEnvironment.LibrarySubset.OverloadSelector.class, new RepresentOverloadSelector());
61+
this.multiRepresenters.put(CelEnvironment.Alias.class, new RepresentAlias());
62+
this.multiRepresenters.put(CelContainer.class, new RepresentContainer());
5863
}
5964

6065
public static String toYaml(CelEnvironment environment) {
@@ -72,7 +77,9 @@ public Node representData(Object data) {
7277
if (!environment.description().isEmpty()) {
7378
configMap.put("description", environment.description());
7479
}
75-
if (!environment.container().isEmpty()) {
80+
if (!environment.container().name().isEmpty()
81+
|| !environment.container().abbreviations().isEmpty()
82+
|| !environment.container().aliases().isEmpty()) {
7683
configMap.put("container", environment.container());
7784
}
7885
if (!environment.extensions().isEmpty()) {
@@ -91,6 +98,43 @@ public Node representData(Object data) {
9198
}
9299
}
93100

101+
private final class RepresentContainer implements Represent {
102+
103+
@Override
104+
public Node representData(Object data) {
105+
CelContainer container = (CelContainer) data;
106+
ImmutableMap.Builder<String, Object> configMap = ImmutableMap.builder();
107+
if (!container.name().isEmpty()) {
108+
configMap.put("name", container.name());
109+
}
110+
if (!container.abbreviations().isEmpty()) {
111+
configMap.put("abbreviations", container.abbreviations());
112+
}
113+
if (!container.aliases().isEmpty()) {
114+
ImmutableList.Builder<Alias> aliases = ImmutableList.builder();
115+
for (Map.Entry<String, String> entry : container.aliases().entrySet()) {
116+
aliases.add(
117+
Alias.newBuilder()
118+
.setAlias(entry.getKey())
119+
.setQualifiedName(entry.getValue())
120+
.build());
121+
}
122+
configMap.put("aliases", aliases.build());
123+
}
124+
return represent(configMap.buildOrThrow());
125+
}
126+
}
127+
128+
private final class RepresentAlias implements Represent {
129+
130+
@Override
131+
public Node representData(Object data) {
132+
Alias alias = (Alias) data;
133+
return represent(
134+
ImmutableMap.of("alias", alias.alias(), "qualified_name", alias.qualifiedName()));
135+
}
136+
}
137+
94138
private final class RepresentExtensionConfig implements Represent {
95139
@Override
96140
public Node representData(Object data) {

0 commit comments

Comments
 (0)