Skip to content

Commit 95faaa8

Browse files
committed
Preserve FieldMask as a message in the CelValue runtime path
BaseProtoCelValueConverter.fromWellKnownProto converts FieldMask to a comma-separated string of its paths. That's correct in JSON-assignment contexts (handled separately by CelProtoJsonAdapter), but it breaks CEL expressions that access fields on the FieldMask itself — e.g. `fieldMask.paths` — because the string has no field `paths`. The CEL spec's WKT conversion table does not list FieldMask. cel-go treats it as a regular message, as does cel-java's legacy ProtoLiteAdapter.adaptValueToWellKnownProto for non-JSON contexts. Override fromWellKnownProto in ProtoCelValueConverter to preserve FieldMask as a ProtoMessageValue. JSON-assignment paths (e.g. TestAllTypes{single_value: FieldMask{...}}) are unaffected because EvalCreateStruct and CelValueRuntimeTypeProvider.createMessage both unwrap StructValue results before the outer assignment runs, and the outer assignment goes through CelProtoJsonAdapter.adaptValueToJsonValue which handles FieldMask → string conversion at that layer.
1 parent 98ce0bf commit 95faaa8

2 files changed

Lines changed: 21 additions & 0 deletions

File tree

common/src/main/java/dev/cel/common/values/ProtoCelValueConverter.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ protected Object fromWellKnownProto(MessageLiteOrBuilder msg, WellKnownProto wel
7171
"Unpacking failed for message: " + message.getDescriptorForType().getFullName(), e);
7272
}
7373
return toRuntimeValue(unpackedMessage);
74+
case FIELD_MASK:
75+
return ProtoMessageValue.create(
76+
(Message) message, celDescriptorPool, this, celOptions.enableJsonFieldNames());
7477
default:
7578
return super.fromWellKnownProto(message, wellKnownProto);
7679
}

common/src/test/java/dev/cel/common/values/ProtoMessageValueTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,24 @@ public void selectField_durationOutOfRange_success(int seconds, int nanos) {
303303
.isEqualTo(Duration.ofSeconds(seconds, nanos));
304304
}
305305

306+
@Test
307+
public void selectField_fieldMask_returnsProtoMessageValue() {
308+
TestAllTypes testAllTypes =
309+
TestAllTypes.newBuilder()
310+
.setFieldMask(
311+
com.google.protobuf.FieldMask.newBuilder().addPaths("foo").addPaths("bar"))
312+
.build();
313+
314+
ProtoMessageValue protoMessageValue =
315+
ProtoMessageValue.create(
316+
testAllTypes, DefaultDescriptorPool.INSTANCE, PROTO_CEL_VALUE_CONVERTER, false);
317+
318+
Object selected = protoMessageValue.select("field_mask");
319+
assertThat(selected).isInstanceOf(ProtoMessageValue.class);
320+
assertThat(((ProtoMessageValue) selected).select("paths"))
321+
.isEqualTo(ImmutableList.of("foo", "bar"));
322+
}
323+
306324
@SuppressWarnings("ImmutableEnumChecker") // Test only
307325
private enum SelectFieldJsonValueTestCase {
308326
NULL(Value.newBuilder().build(), NullValue.NULL_VALUE),

0 commit comments

Comments
 (0)