Skip to content

Commit 67b96ad

Browse files
l46kokcopybara-github
authored andcommitted
Add ProtoMessageValue
PiperOrigin-RevId: 584672011
1 parent 86b38a5 commit 67b96ad

File tree

6 files changed

+262
-1
lines changed

6 files changed

+262
-1
lines changed

common/src/main/java/dev/cel/common/internal/CelDescriptorPool.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ public interface CelDescriptorPool {
3434
/** Finds the descriptor by fully qualified message type. */
3535
Optional<Descriptor> findDescriptor(String name);
3636

37-
/** Finds the corresponding field descriptor for an extension field on a message. */
37+
/**
38+
* Finds the corresponding field descriptor for an extension field on a message. The field name
39+
* must be fully-qualified.
40+
*/
3841
Optional<FieldDescriptor> findExtensionDescriptor(
3942
Descriptor containingDescriptor, String fieldName);
4043

common/src/main/java/dev/cel/common/values/BUILD.bazel

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ CEL_VALUES_SOURCES = [
3030
"UintValue.java",
3131
]
3232

33+
# keep sorted
34+
PROTO_MESSAGE_VALUE_SOURCES = [
35+
"ProtoMessageValue.java",
36+
]
37+
3338
java_library(
3439
name = "cel_value",
3540
srcs = ["CelValue.java"],
@@ -70,3 +75,22 @@ java_library(
7075
"@maven//:com_google_guava_guava",
7176
],
7277
)
78+
79+
java_library(
80+
name = "proto_message_value",
81+
srcs = PROTO_MESSAGE_VALUE_SOURCES,
82+
tags = [
83+
],
84+
deps = [
85+
":cel_value",
86+
":values",
87+
"//:auto_value",
88+
"//common/internal:cel_descriptor_pools",
89+
"//common/types",
90+
"//common/types:type_providers",
91+
"@maven//:com_google_errorprone_error_prone_annotations",
92+
"@maven//:com_google_guava_guava",
93+
"@maven//:com_google_protobuf_protobuf_java",
94+
"@maven//:org_jspecify_jspecify",
95+
],
96+
)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2023 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.common.values;
16+
17+
import com.google.auto.value.AutoValue;
18+
import com.google.common.base.Preconditions;
19+
import com.google.errorprone.annotations.Immutable;
20+
import com.google.protobuf.Descriptors.Descriptor;
21+
import com.google.protobuf.Descriptors.FieldDescriptor;
22+
import com.google.protobuf.Message;
23+
import dev.cel.common.internal.CelDescriptorPool;
24+
import dev.cel.common.types.CelType;
25+
import dev.cel.common.types.StructTypeReference;
26+
27+
/** ProtoMessageValue is a struct value with protobuf support. */
28+
@AutoValue
29+
@Immutable
30+
public abstract class ProtoMessageValue extends StructValue {
31+
32+
@Override
33+
public abstract Message value();
34+
35+
@Override
36+
public abstract CelType celType();
37+
38+
abstract CelDescriptorPool celDescriptorPool();
39+
40+
@Override
41+
public boolean isZeroValue() {
42+
return value().getDefaultInstanceForType().equals(value());
43+
}
44+
45+
@Override
46+
public boolean hasField(String fieldName) {
47+
FieldDescriptor fieldDescriptor =
48+
findField(celDescriptorPool(), value().getDescriptorForType(), fieldName);
49+
50+
if (fieldDescriptor.isRepeated()) {
51+
return value().getRepeatedFieldCount(fieldDescriptor) > 0;
52+
}
53+
54+
return value().hasField(fieldDescriptor);
55+
}
56+
57+
@Override
58+
public CelValue select(String fieldName) {
59+
throw new UnsupportedOperationException("Not implemented yet");
60+
}
61+
62+
public static ProtoMessageValue create(Message value, CelDescriptorPool celDescriptorPool) {
63+
Preconditions.checkNotNull(value);
64+
Preconditions.checkNotNull(celDescriptorPool);
65+
return new AutoValue_ProtoMessageValue(
66+
value,
67+
StructTypeReference.create(value.getDescriptorForType().getFullName()),
68+
celDescriptorPool);
69+
}
70+
71+
private FieldDescriptor findField(
72+
CelDescriptorPool celDescriptorPool, Descriptor descriptor, String fieldName) {
73+
FieldDescriptor fieldDescriptor = descriptor.findFieldByName(fieldName);
74+
if (fieldDescriptor != null) {
75+
return fieldDescriptor;
76+
}
77+
78+
return celDescriptorPool
79+
.findExtensionDescriptor(descriptor, fieldName)
80+
.orElseThrow(
81+
() ->
82+
new IllegalArgumentException(
83+
String.format(
84+
"field '%s' is not declared in message '%s'",
85+
fieldName, descriptor.getFullName())));
86+
}
87+
}

common/src/test/java/dev/cel/common/values/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ java_library(
88
srcs = glob(["*.java"]),
99
deps = [
1010
"//:java_truth",
11+
"//common",
12+
"//common/internal:cel_descriptor_pools",
13+
"//common/resources/testdata/proto2:messages_extensions_proto2_java_proto",
14+
"//common/resources/testdata/proto2:messages_proto2_java_proto",
15+
"//common/resources/testdata/proto2:test_all_types_java_proto",
1116
"//common/types",
1217
"//common/types:type_providers",
1318
"//common/values",
1419
"//common/values:cel_byte_string",
1520
"//common/values:cel_value",
21+
"//common/values:proto_message_value",
1622
"@maven//:com_google_guava_guava",
1723
"@maven//:com_google_guava_guava_testlib",
1824
"@maven//:com_google_testparameterinjector_test_parameter_injector",
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright 2023 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.common.values;
16+
17+
import static com.google.common.truth.Truth.assertThat;
18+
import static org.junit.Assert.assertThrows;
19+
20+
import com.google.common.collect.ImmutableList;
21+
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
22+
import dev.cel.common.CelDescriptorUtil;
23+
import dev.cel.common.internal.CelDescriptorPool;
24+
import dev.cel.common.internal.DefaultDescriptorPool;
25+
import dev.cel.common.types.StructTypeReference;
26+
import dev.cel.testing.testdata.proto2.MessagesProto2Extensions;
27+
import dev.cel.testing.testdata.proto2.Proto2Message;
28+
import dev.cel.testing.testdata.proto2.TestAllTypesProto.TestAllTypes;
29+
import org.junit.Test;
30+
import org.junit.runner.RunWith;
31+
32+
@RunWith(TestParameterInjector.class)
33+
public final class ProtoMessageValueTest {
34+
35+
@Test
36+
public void emptyProtoMessage() {
37+
ProtoMessageValue protoMessageValue =
38+
ProtoMessageValue.create(TestAllTypes.getDefaultInstance(), DefaultDescriptorPool.INSTANCE);
39+
40+
assertThat(protoMessageValue.value()).isEqualTo(TestAllTypes.getDefaultInstance());
41+
assertThat(protoMessageValue.isZeroValue()).isTrue();
42+
}
43+
44+
@Test
45+
public void constructProtoMessage() {
46+
TestAllTypes testAllTypes =
47+
TestAllTypes.newBuilder().setSingleBool(true).setSingleInt64(5L).build();
48+
ProtoMessageValue protoMessageValue =
49+
ProtoMessageValue.create(testAllTypes, DefaultDescriptorPool.INSTANCE);
50+
51+
assertThat(protoMessageValue.value()).isEqualTo(testAllTypes);
52+
assertThat(protoMessageValue.isZeroValue()).isFalse();
53+
}
54+
55+
@Test
56+
public void hasField_fieldIsSet_success() {
57+
TestAllTypes testAllTypes =
58+
TestAllTypes.newBuilder()
59+
.setSingleBool(true)
60+
.setSingleInt64(5L)
61+
.addRepeatedInt64(5L)
62+
.build();
63+
ProtoMessageValue protoMessageValue =
64+
ProtoMessageValue.create(testAllTypes, DefaultDescriptorPool.INSTANCE);
65+
66+
assertThat(protoMessageValue.hasField("single_bool")).isTrue();
67+
assertThat(protoMessageValue.hasField("single_int64")).isTrue();
68+
assertThat(protoMessageValue.hasField("repeated_int64")).isTrue();
69+
}
70+
71+
@Test
72+
public void hasField_fieldIsUnset_success() {
73+
ProtoMessageValue protoMessageValue =
74+
ProtoMessageValue.create(TestAllTypes.getDefaultInstance(), DefaultDescriptorPool.INSTANCE);
75+
76+
assertThat(protoMessageValue.hasField("single_int32")).isFalse();
77+
assertThat(protoMessageValue.hasField("single_uint64")).isFalse();
78+
assertThat(protoMessageValue.hasField("repeated_int32")).isFalse();
79+
}
80+
81+
@Test
82+
public void hasField_fieldIsUndeclared_throwsException() {
83+
ProtoMessageValue protoMessageValue =
84+
ProtoMessageValue.create(TestAllTypes.getDefaultInstance(), DefaultDescriptorPool.INSTANCE);
85+
86+
IllegalArgumentException exception =
87+
assertThrows(IllegalArgumentException.class, () -> protoMessageValue.hasField("bogus"));
88+
assertThat(exception)
89+
.hasMessageThat()
90+
.isEqualTo(
91+
"field 'bogus' is not declared in message"
92+
+ " 'dev.cel.testing.testdata.proto2.TestAllTypes'");
93+
}
94+
95+
@Test
96+
public void hasField_extensionField_success() {
97+
CelDescriptorPool descriptorPool =
98+
DefaultDescriptorPool.create(
99+
CelDescriptorUtil.getAllDescriptorsFromFileDescriptor(
100+
ImmutableList.of(MessagesProto2Extensions.getDescriptor())));
101+
Proto2Message proto2Message =
102+
Proto2Message.newBuilder().setExtension(MessagesProto2Extensions.int32Ext, 1).build();
103+
104+
ProtoMessageValue protoMessageValue = ProtoMessageValue.create(proto2Message, descriptorPool);
105+
106+
assertThat(protoMessageValue.hasField("dev.cel.testing.testdata.proto2.int32_ext")).isTrue();
107+
}
108+
109+
@Test
110+
public void hasField_extensionField_throwsWhenDescriptorMissing() {
111+
Proto2Message proto2Message =
112+
Proto2Message.newBuilder().setExtension(MessagesProto2Extensions.int32Ext, 1).build();
113+
114+
ProtoMessageValue protoMessageValue =
115+
ProtoMessageValue.create(proto2Message, DefaultDescriptorPool.INSTANCE);
116+
117+
IllegalArgumentException exception =
118+
assertThrows(
119+
IllegalArgumentException.class,
120+
() -> protoMessageValue.hasField("dev.cel.testing.testdata.proto2.int32_ext"));
121+
assertThat(exception)
122+
.hasMessageThat()
123+
.isEqualTo(
124+
"field 'dev.cel.testing.testdata.proto2.int32_ext' is not declared in message"
125+
+ " 'dev.cel.testing.testdata.proto2.Proto2Message'");
126+
}
127+
128+
@Test
129+
public void celTypeTest() {
130+
ProtoMessageValue protoMessageValue =
131+
ProtoMessageValue.create(TestAllTypes.getDefaultInstance(), DefaultDescriptorPool.INSTANCE);
132+
133+
assertThat(protoMessageValue.celType())
134+
.isEqualTo(StructTypeReference.create(TestAllTypes.getDescriptor().getFullName()));
135+
}
136+
}

common/values/BUILD.bazel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,8 @@ java_library(
1717
name = "cel_byte_string",
1818
exports = ["//common/src/main/java/dev/cel/common/values:cel_byte_string"],
1919
)
20+
21+
java_library(
22+
name = "proto_message_value",
23+
exports = ["//common/src/main/java/dev/cel/common/values:proto_message_value"],
24+
)

0 commit comments

Comments
 (0)