Skip to content

Commit e28f56a

Browse files
l46kokcopybara-github
authored andcommitted
Fix JSON conversions involving FieldMask and Empty
PiperOrigin-RevId: 738109734
1 parent d439d13 commit e28f56a

File tree

6 files changed

+47
-12
lines changed

6 files changed

+47
-12
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,24 @@
1414

1515
package dev.cel.common;
1616

17+
import com.google.common.base.CaseFormat;
18+
import com.google.common.base.Joiner;
1719
import com.google.common.primitives.UnsignedLong;
1820
import com.google.errorprone.annotations.Immutable;
1921
import com.google.protobuf.ByteString;
2022
import com.google.protobuf.Duration;
23+
import com.google.protobuf.Empty;
24+
import com.google.protobuf.FieldMask;
2125
import com.google.protobuf.ListValue;
2226
import com.google.protobuf.NullValue;
2327
import com.google.protobuf.Struct;
2428
import com.google.protobuf.Timestamp;
2529
import com.google.protobuf.Value;
2630
import com.google.protobuf.util.Durations;
2731
import com.google.protobuf.util.Timestamps;
32+
import java.util.ArrayList;
2833
import java.util.Base64;
34+
import java.util.List;
2935
import java.util.Map;
3036

3137
/**
@@ -113,11 +119,36 @@ public static Value adaptValueToJsonValue(Object value) {
113119
String duration = Durations.toString((Duration) value);
114120
return json.setStringValue(duration).build();
115121
}
122+
if (value instanceof FieldMask) {
123+
String fieldMaskStr = toJsonString((FieldMask) value);
124+
return json.setStringValue(fieldMaskStr).build();
125+
}
126+
if (value instanceof Empty) {
127+
// google.protobuf.Empty is just an empty json map {}
128+
return json.setStructValue(Struct.getDefaultInstance()).build();
129+
}
116130

117131
throw new IllegalArgumentException(
118132
String.format("Value %s cannot be adapted to a JSON Value.", value));
119133
}
120134

135+
/**
136+
* Joins the field mask's paths into a single string with commas. This logic is copied from
137+
* Protobuf's FieldMaskUtil.java, which we cannot directly use here due to its dependency to
138+
* descriptors.
139+
*/
140+
private static String toJsonString(FieldMask fieldMask) {
141+
List<String> paths = new ArrayList<>(fieldMask.getPathsCount());
142+
143+
for (String path : fieldMask.getPathsList()) {
144+
if (!path.isEmpty()) {
145+
paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path));
146+
}
147+
}
148+
149+
return Joiner.on(",").join(paths);
150+
}
151+
121152
/**
122153
* Adapts an iterable to a JSON list value.
123154
*

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,11 +290,6 @@ public Message adaptValueToProto(Object value, String protoTypeName) {
290290
}
291291

292292
switch (wellKnownProto) {
293-
// TODO: These should be converted properly to their primitive types. This is
294-
// here to retain compatibility for time being.
295-
case FIELD_MASK:
296-
case EMPTY:
297-
return (Message) value;
298293
case ANY_VALUE:
299294
if (value instanceof Message) {
300295
protoTypeName = ((Message) value).getDescriptorForType().getFullName();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public static String getJavaPackageName(FileDescriptor fileDescriptor) {
9191
FileOptions options = fileDescriptor.getFile().getOptions();
9292
StringBuilder javaPackageName = new StringBuilder();
9393
if (options.hasJavaPackage()) {
94-
javaPackageName.append(fileDescriptor.getFile().getOptions().getJavaPackage());
94+
javaPackageName.append(options.getJavaPackage());
9595
} else {
9696
javaPackageName
9797
// CEL-Internal-1

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,15 @@ public MessageLite adaptValueToWellKnownProto(Object value, WellKnownProto wellK
9595
return (Duration) value;
9696
case TIMESTAMP:
9797
return (Timestamp) value;
98+
case EMPTY:
99+
case FIELD_MASK:
100+
// These two WKTs are typically used in context of JSON conversions, in which they are
101+
// automatically unwrapped into equivalent primitive types.
102+
// In other cases, just return the original message itself.
103+
return (MessageLite) value;
98104
default:
99-
throw new IllegalArgumentException("Unexpceted wellKnownProto kind: " + wellKnownProto);
105+
throw new IllegalArgumentException(
106+
"Unexpected wellKnownProto kind: " + wellKnownProto + " for value: " + value);
100107
}
101108
}
102109

common/src/test/java/dev/cel/common/internal/ProtoAdapterTest.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import com.google.protobuf.BytesValue;
2727
import com.google.protobuf.DoubleValue;
2828
import com.google.protobuf.Duration;
29+
import com.google.protobuf.Empty;
30+
import com.google.protobuf.FieldMask;
2931
import com.google.protobuf.FloatValue;
3032
import com.google.protobuf.Int64Value;
3133
import com.google.protobuf.ListValue;
@@ -134,9 +136,13 @@ public static List<Object[]> data() {
134136
Timestamp.newBuilder().setSeconds(123).build(),
135137
Any.pack(Timestamp.newBuilder().setSeconds(123).build()),
136138
},
137-
// Adaption support for the most current CelOptions.
138139
{UnsignedLong.valueOf(1L), UInt64Value.of(1L)},
139140
{UnsignedLong.valueOf(1L), Any.pack(UInt64Value.of(1L))},
141+
{Empty.getDefaultInstance(), Empty.getDefaultInstance()},
142+
{
143+
FieldMask.newBuilder().addPaths("foo").build(),
144+
FieldMask.newBuilder().addPaths("foo").build()
145+
}
140146
});
141147
}
142148

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ _TESTS_TO_SKIP = [
9191
"timestamps/timestamp_arithmetic/add_time_to_duration_nanos_negative",
9292

9393
# Skip until fixed.
94-
"wrappers/field_mask/to_json",
95-
"wrappers/empty/to_json",
96-
"wrappers/duration/to_json",
97-
"wrappers/timestamp/to_json",
9894
"fields/qualified_identifier_resolution/map_value_repeat_key_heterogeneous",
9995
# TODO: Add strings.format and strings.quote.
10096
"string_ext/quote",

0 commit comments

Comments
 (0)