diff --git a/common/src/main/java/dev/cel/common/CelProtoJsonAdapter.java b/common/src/main/java/dev/cel/common/CelProtoJsonAdapter.java index 74c52f397..1e49b97b9 100644 --- a/common/src/main/java/dev/cel/common/CelProtoJsonAdapter.java +++ b/common/src/main/java/dev/cel/common/CelProtoJsonAdapter.java @@ -14,10 +14,14 @@ package dev.cel.common; +import com.google.common.base.CaseFormat; +import com.google.common.base.Joiner; import com.google.common.primitives.UnsignedLong; import com.google.errorprone.annotations.Immutable; import com.google.protobuf.ByteString; import com.google.protobuf.Duration; +import com.google.protobuf.Empty; +import com.google.protobuf.FieldMask; import com.google.protobuf.ListValue; import com.google.protobuf.NullValue; import com.google.protobuf.Struct; @@ -25,7 +29,9 @@ import com.google.protobuf.Value; import com.google.protobuf.util.Durations; import com.google.protobuf.util.Timestamps; +import java.util.ArrayList; import java.util.Base64; +import java.util.List; import java.util.Map; /** @@ -113,11 +119,36 @@ public static Value adaptValueToJsonValue(Object value) { String duration = Durations.toString((Duration) value); return json.setStringValue(duration).build(); } + if (value instanceof FieldMask) { + String fieldMaskStr = toJsonString((FieldMask) value); + return json.setStringValue(fieldMaskStr).build(); + } + if (value instanceof Empty) { + // google.protobuf.Empty is just an empty json map {} + return json.setStructValue(Struct.getDefaultInstance()).build(); + } throw new IllegalArgumentException( String.format("Value %s cannot be adapted to a JSON Value.", value)); } + /** + * Joins the field mask's paths into a single string with commas. + * This logic is copied from Protobuf's FieldMaskUtil.java, which we + * cannot directly use here due to its dependency to descriptors. + */ + private static String toJsonString(FieldMask fieldMask) { + List paths = new ArrayList<>(fieldMask.getPathsCount()); + + for (String path : fieldMask.getPathsList()) { + if (!path.isEmpty()) { + paths.add(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, path)); + } + } + + return Joiner.on(",").join(paths); + } + /** * Adapts an iterable to a JSON list value. * diff --git a/common/src/main/java/dev/cel/common/internal/BUILD.bazel b/common/src/main/java/dev/cel/common/internal/BUILD.bazel index bca6ec303..9733e0e29 100644 --- a/common/src/main/java/dev/cel/common/internal/BUILD.bazel +++ b/common/src/main/java/dev/cel/common/internal/BUILD.bazel @@ -174,6 +174,7 @@ java_library( "@maven//:com_google_errorprone_error_prone_annotations", "@maven//:com_google_guava_guava", "@maven//:com_google_protobuf_protobuf_java", + "@maven//:com_google_protobuf_protobuf_java_util", ], ) diff --git a/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java b/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java index b8aafecff..33d50de12 100644 --- a/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java +++ b/common/src/main/java/dev/cel/common/internal/ProtoLiteAdapter.java @@ -96,7 +96,7 @@ public MessageLite adaptValueToWellKnownProto(Object value, WellKnownProto wellK case TIMESTAMP: return (Timestamp) value; default: - throw new IllegalArgumentException("Unexpceted wellKnownProto kind: " + wellKnownProto); + throw new IllegalArgumentException("Unexpected wellKnownProto kind: " + wellKnownProto); } } diff --git a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel index 6025774aa..eae328a13 100644 --- a/conformance/src/test/java/dev/cel/conformance/BUILD.bazel +++ b/conformance/src/test/java/dev/cel/conformance/BUILD.bazel @@ -91,10 +91,6 @@ _TESTS_TO_SKIP = [ "timestamps/timestamp_arithmetic/add_time_to_duration_nanos_negative", # Skip until fixed. - "wrappers/field_mask/to_json", - "wrappers/empty/to_json", - "wrappers/duration/to_json", - "wrappers/timestamp/to_json", "fields/qualified_identifier_resolution/map_value_repeat_key_heterogeneous", # TODO: Add strings.format and strings.quote. "string_ext/quote",