Skip to content

Commit f411c02

Browse files
TristonianJonescopybara-github
authored andcommitted
Support for CelEnvironment to YAML
PiperOrigin-RevId: 750833300
1 parent bf3c392 commit f411c02

File tree

8 files changed

+296
-1
lines changed

8 files changed

+296
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ java_library(
8989
name = "environment_yaml_parser",
9090
srcs = [
9191
"CelEnvironmentYamlParser.java",
92+
"CelEnvironmentYamlSerializer.java",
9293
],
9394
tags = [
9495
],

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public static Builder newBuilder() {
154154
.setVariables(ImmutableSet.of())
155155
.setFunctions(ImmutableSet.of());
156156
}
157-
157+
158158
/** Extends the provided {@link CelCompiler} environment with this configuration. */
159159
public CelCompiler extend(CelCompiler celCompiler, CelOptions celOptions)
160160
throws CelEnvironmentException {
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.bundle;
16+
17+
import com.google.common.collect.ImmutableList;
18+
import com.google.common.collect.ImmutableMap;
19+
import org.yaml.snakeyaml.DumperOptions;
20+
import org.yaml.snakeyaml.Yaml;
21+
import org.yaml.snakeyaml.nodes.Node;
22+
import org.yaml.snakeyaml.representer.Represent;
23+
import org.yaml.snakeyaml.representer.Representer;
24+
25+
/** Serializes a CelEnvironment into a YAML file. */
26+
public class CelEnvironmentYamlSerializer extends Representer {
27+
28+
private static DumperOptions initDumperOptions() {
29+
DumperOptions options = new DumperOptions();
30+
options.setIndent(2);
31+
options.setPrettyFlow(true);
32+
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
33+
return options;
34+
}
35+
36+
private static final DumperOptions YAML_OPTIONS = initDumperOptions();
37+
38+
public CelEnvironmentYamlSerializer() {
39+
super(YAML_OPTIONS);
40+
this.multiRepresenters.put(CelEnvironment.class, new RepresentCelEnvironment());
41+
this.multiRepresenters.put(CelEnvironment.VariableDecl.class, new RepresentVariableDecl());
42+
this.multiRepresenters.put(CelEnvironment.FunctionDecl.class, new RepresentFunctionDecl());
43+
this.multiRepresenters.put(CelEnvironment.OverloadDecl.class, new RepresentOverloadDecl());
44+
this.multiRepresenters.put(CelEnvironment.TypeDecl.class, new RepresentTypeDecl());
45+
this.multiRepresenters.put(
46+
CelEnvironment.ExtensionConfig.class, new RepresentExtensionConfig());
47+
}
48+
49+
public String toYaml(CelEnvironment environment) {
50+
Yaml yaml = new Yaml(this, YAML_OPTIONS);
51+
return yaml.dump(environment);
52+
}
53+
54+
private final class RepresentCelEnvironment implements Represent {
55+
@Override
56+
public Node representData(Object data) {
57+
CelEnvironment environment = (CelEnvironment) data;
58+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
59+
configMap.put("name", environment.name());
60+
if (!environment.description().isEmpty()) {
61+
configMap.put("description", environment.description());
62+
}
63+
if (!environment.container().isEmpty()) {
64+
configMap.put("container", environment.container());
65+
}
66+
if (!environment.extensions().isEmpty()) {
67+
configMap.put("extensions", environment.extensions().asList());
68+
}
69+
if (!environment.variables().isEmpty()) {
70+
configMap.put("variables", environment.variables().asList());
71+
}
72+
if (!environment.functions().isEmpty()) {
73+
configMap.put("functions", environment.functions().asList());
74+
}
75+
return represent(configMap.buildOrThrow());
76+
}
77+
}
78+
79+
private final class RepresentExtensionConfig implements Represent {
80+
@Override
81+
public Node representData(Object data) {
82+
CelEnvironment.ExtensionConfig extension = (CelEnvironment.ExtensionConfig) data;
83+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
84+
configMap.put("name", extension.name());
85+
if (extension.version() > 0 && extension.version() != Integer.MAX_VALUE) {
86+
configMap.put("version", extension.version());
87+
}
88+
return represent(configMap.buildOrThrow());
89+
}
90+
}
91+
92+
private final class RepresentVariableDecl implements Represent {
93+
@Override
94+
public Node representData(Object data) {
95+
CelEnvironment.VariableDecl variable = (CelEnvironment.VariableDecl) data;
96+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
97+
configMap.put("name", variable.name()).put("type_name", variable.type().name());
98+
if (!variable.type().params().isEmpty()) {
99+
configMap.put("params", variable.type().params());
100+
}
101+
return represent(configMap.buildOrThrow());
102+
}
103+
}
104+
105+
private final class RepresentFunctionDecl implements Represent {
106+
@Override
107+
public Node representData(Object data) {
108+
CelEnvironment.FunctionDecl function = (CelEnvironment.FunctionDecl) data;
109+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
110+
configMap
111+
.put("name", function.name())
112+
.put("overloads", ImmutableList.copyOf(function.overloads()));
113+
return represent(configMap.buildOrThrow());
114+
}
115+
}
116+
117+
private final class RepresentOverloadDecl implements Represent {
118+
@Override
119+
public Node representData(Object data) {
120+
CelEnvironment.OverloadDecl overload = (CelEnvironment.OverloadDecl) data;
121+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
122+
configMap.put("id", overload.id());
123+
if (overload.target().isPresent()) {
124+
configMap.put("target", overload.target().get());
125+
}
126+
configMap.put("args", overload.arguments()).put("return", overload.returnType());
127+
return represent(configMap.buildOrThrow());
128+
}
129+
}
130+
131+
private final class RepresentTypeDecl implements Represent {
132+
@Override
133+
public Node representData(Object data) {
134+
CelEnvironment.TypeDecl type = (CelEnvironment.TypeDecl) data;
135+
ImmutableMap.Builder<String, Object> configMap = new ImmutableMap.Builder<>();
136+
configMap.put("type_name", type.name());
137+
if (!type.params().isEmpty()) {
138+
configMap.put("params", type.params());
139+
}
140+
if (type.isTypeParam()) {
141+
configMap.put("is_type_param", type.isTypeParam());
142+
}
143+
return represent(configMap.buildOrThrow());
144+
}
145+
}
146+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ java_library(
1010
testonly = True,
1111
srcs = glob(["*Test.java"]),
1212
resources = [
13+
"//testing/environment:dump_env",
1314
"//testing/environment:extended_env",
1415
],
1516
deps = [
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package dev.cel.bundle;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
20+
import com.google.common.base.Ascii;
21+
import com.google.common.collect.ImmutableList;
22+
import com.google.common.collect.ImmutableSet;
23+
import com.google.common.io.Resources;
24+
import dev.cel.bundle.CelEnvironment.ExtensionConfig;
25+
import dev.cel.bundle.CelEnvironment.FunctionDecl;
26+
import dev.cel.bundle.CelEnvironment.OverloadDecl;
27+
import dev.cel.bundle.CelEnvironment.TypeDecl;
28+
import dev.cel.bundle.CelEnvironment.VariableDecl;
29+
import java.io.IOException;
30+
import java.net.URL;
31+
import org.junit.Test;
32+
import org.junit.runner.RunWith;
33+
import org.junit.runners.JUnit4;
34+
35+
@RunWith(JUnit4.class)
36+
public final class CelEnvironmentYamlSerializerTest {
37+
38+
@Test
39+
public void toYaml_success() throws Exception {
40+
CelEnvironment environment =
41+
CelEnvironment.newBuilder()
42+
.setName("dump_env")
43+
.setDescription("dump_env description")
44+
.setContainer("test.container")
45+
.addExtensions(
46+
ImmutableSet.of(
47+
ExtensionConfig.of("bindings"),
48+
ExtensionConfig.of("encoders"),
49+
ExtensionConfig.of("lists"),
50+
ExtensionConfig.of("math"),
51+
ExtensionConfig.of("optional"),
52+
ExtensionConfig.of("protos"),
53+
ExtensionConfig.of("sets"),
54+
ExtensionConfig.of("strings")))
55+
.setVariables(
56+
ImmutableSet.of(
57+
VariableDecl.create(
58+
"request", TypeDecl.create("google.rpc.context.AttributeContext.Request")),
59+
VariableDecl.create(
60+
"map_var",
61+
TypeDecl.newBuilder()
62+
.setName("map")
63+
.addParams(TypeDecl.create("string"))
64+
.addParams(TypeDecl.create("string"))
65+
.build())))
66+
.setFunctions(
67+
ImmutableSet.of(
68+
FunctionDecl.create(
69+
"coalesce",
70+
ImmutableSet.of(
71+
OverloadDecl.newBuilder()
72+
.setId("coalesce_null_int")
73+
.setTarget(TypeDecl.create("google.protobuf.Int64Value"))
74+
.setArguments(ImmutableList.of(TypeDecl.create("int")))
75+
.setReturnType(TypeDecl.create("int"))
76+
.build()))))
77+
.build();
78+
79+
CelEnvironmentYamlSerializer serializer = new CelEnvironmentYamlSerializer();
80+
String yamlOutput = serializer.toYaml(environment);
81+
try {
82+
String yamlFileContent = readFile("environment/dump_env.yaml");
83+
assertThat(yamlFileContent).endsWith(yamlOutput);
84+
} catch (IOException e) {
85+
throw new RuntimeException(e);
86+
}
87+
}
88+
89+
private static String readFile(String path) throws IOException {
90+
URL url = Resources.getResource(Ascii.toLowerCase(path));
91+
return Resources.toString(url, UTF_8);
92+
}
93+
}

testing/environment/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ package(
44
default_visibility = ["//:internal"],
55
)
66

7+
alias(
8+
name = "dump_env",
9+
actual = "//testing/src/test/resources/environment:dump_env",
10+
)
11+
712
alias(
813
name = "extended_env",
914
actual = "//testing/src/test/resources/environment:extended_env",

testing/src/test/resources/environment/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ package(
88
],
99
)
1010

11+
filegroup(
12+
name = "dump_env",
13+
srcs = ["dump_env.yaml"],
14+
)
15+
1116
filegroup(
1217
name = "extended_env",
1318
srcs = ["extended_env.yaml"],
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
name: dump_env
16+
description: dump_env description
17+
container: test.container
18+
extensions:
19+
- name: bindings
20+
- name: encoders
21+
- name: lists
22+
- name: math
23+
- name: optional
24+
- name: protos
25+
- name: sets
26+
- name: strings
27+
variables:
28+
- name: request
29+
type_name: google.rpc.context.AttributeContext.Request
30+
- name: map_var
31+
type_name: map
32+
params:
33+
- type_name: string
34+
- type_name: string
35+
functions:
36+
- name: coalesce
37+
overloads:
38+
- id: coalesce_null_int
39+
target:
40+
type_name: google.protobuf.Int64Value
41+
args:
42+
- type_name: int
43+
return:
44+
type_name: int

0 commit comments

Comments
 (0)