Skip to content

Commit

Permalink
Provide an overload to hide all fields on a message with ProtoTypeMask
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 660903826
  • Loading branch information
l46kok authored and copybara-github committed Aug 9, 2024
1 parent 8fe0b7f commit 772b6e8
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 4 deletions.
47 changes: 47 additions & 0 deletions bundle/src/test/java/dev/cel/bundle/CelImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,53 @@ public void program_withProtoVars() throws Exception {
.isEqualTo(true);
}

@Test
public void program_withAllFieldsHidden_emptyMessageConstructionSuccess() throws Exception {
Cel cel =
standardCelBuilderWithMacros()
.addMessageTypes(AttributeContext.getDescriptor())
.setContainer("google.rpc.context.AttributeContext")
.addProtoTypeMasks(
ProtoTypeMask.ofAllFieldsHidden("google.rpc.context.AttributeContext"))
.build();

assertThat(cel.createProgram(cel.compile("AttributeContext{}").getAst()).eval())
.isEqualTo(AttributeContext.getDefaultInstance());
}

@Test
public void compile_withAllFieldsHidden_selectHiddenField_throws() throws Exception {
Cel cel =
standardCelBuilderWithMacros()
.addMessageTypes(AttributeContext.getDescriptor())
.setContainer("google.rpc.context.AttributeContext")
.addProtoTypeMasks(
ProtoTypeMask.ofAllFieldsHidden("google.rpc.context.AttributeContext"))
.build();

CelValidationException e =
assertThrows(
CelValidationException.class,
() -> cel.compile("AttributeContext{ request: AttributeContext.Request{} }").getAst());
assertThat(e).hasMessageThat().contains("undefined field 'request'");
}

@Test
public void compile_withAllFieldsHidden_selectHiddenFieldOnVar_throws() throws Exception {
Cel cel =
standardCelBuilderWithMacros()
.addMessageTypes(AttributeContext.getDescriptor())
.setContainer("google.rpc.context.AttributeContext")
.addProtoTypeMasks(
ProtoTypeMask.ofAllFieldsHidden("google.rpc.context.AttributeContext"))
.addVar("attr_ctx", StructTypeReference.create("google.rpc.context.AttributeContext"))
.build();

CelValidationException e =
assertThrows(CelValidationException.class, () -> cel.compile("attr_ctx.source").getAst());
assertThat(e).hasMessageThat().contains("undefined field 'source'");
}

@Test
public void program_withNestedRestrictedProtoVars() throws Exception {
Cel cel =
Expand Down
22 changes: 22 additions & 0 deletions checker/src/main/java/dev/cel/checker/ProtoTypeMask.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ public abstract class ProtoTypeMask {
/** WILDCARD_FIELD indicates that all fields within the proto type are visible. */
static final String WILDCARD_FIELD = "*";

/** HIDDEN_FIELD indicates that all fields within the proto type are not visible. */
static final String HIDDEN_FIELD = "!";

private static final FieldMask HIDDEN_FIELD_MASK =
FieldMask.newBuilder().addPaths(HIDDEN_FIELD).build();

private static final FieldPath HIDDEN_FIELD_PATH = FieldPath.of(HIDDEN_FIELD);
private static final FieldMask WILDCARD_FIELD_MASK =
FieldMask.newBuilder().addPaths(WILDCARD_FIELD).build();
private static final FieldPath WILDCARD_FIELD_PATH = FieldPath.of(WILDCARD_FIELD);
Expand All @@ -52,6 +59,10 @@ boolean areAllFieldPathsExposed() {
return getFieldPathsExposed().stream().allMatch(fp -> fp.equals(WILDCARD_FIELD_PATH));
}

boolean areAllFieldPathsHidden() {
return getFieldPathsExposed().stream().allMatch(fp -> fp.equals(HIDDEN_FIELD_PATH));
}

public ProtoTypeMask withFieldsAsVariableDeclarations() {
return new AutoValue_ProtoTypeMask(getTypeName(), getFieldPathsExposed(), true);
}
Expand Down Expand Up @@ -98,6 +109,17 @@ public static ProtoTypeMask ofAllFields(String fullyQualifiedTypeName) {
return of(fullyQualifiedTypeName, WILDCARD_FIELD_MASK);
}

/**
* Construct a new {@code ProtoTypeMask} which hides all fields in the given {@code typeName} for
* use within CEL expressions.
*
* <p>The {@code typeName} should be a fully-qualified path, e.g., {@code
* "google.rpc.context.AttributeContext"}.
*/
public static ProtoTypeMask ofAllFieldsHidden(String fullyQualifiedTypeName) {
return of(fullyQualifiedTypeName, HIDDEN_FIELD_MASK);
}

/**
* FieldPath is the equivalent of a field selection represented within a {@link FieldMask#path}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,16 @@ private static ImmutableMap<String, CelType> computeVisibleFieldsMap(
CelTypeProvider delegateProvider, ImmutableSet<ProtoTypeMask> protoTypeMasks) {
Map<String, Set<String>> fieldMap = new HashMap<>();
for (ProtoTypeMask typeMask : protoTypeMasks) {
Optional<CelType> rootType = delegateProvider.findType(typeMask.getTypeName());
checkArgument(rootType.isPresent(), "message not registered: %s", typeMask.getTypeName());
String typeName = typeMask.getTypeName();
Optional<CelType> rootType = delegateProvider.findType(typeName);
checkArgument(rootType.isPresent(), "message not registered: %s", typeName);
if (typeMask.areAllFieldPathsExposed()) {
continue;
}
if (typeMask.areAllFieldPathsHidden()) {
fieldMap.put(typeName, ImmutableSet.of());
continue;
}
// Unroll the type(messageType) to just messageType.
CelType type = rootType.get();
checkArgument(type instanceof ProtoMessageType, "type is not a protobuf: %s", type.name());
Expand Down
8 changes: 8 additions & 0 deletions checker/src/test/java/dev/cel/checker/ProtoTypeMaskTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ public void ofTypeWithFieldMask_invalidMask() {
() -> ProtoTypeMask.of("test", FieldMask.newBuilder().addPaths("").build()));
}

@Test
public void ofAllFieldsHidden() {
ProtoTypeMask typeExpr = ProtoTypeMask.ofAllFieldsHidden("google.rpc.context.AttributeContext");
assertThat(typeExpr.areAllFieldPathsExposed()).isFalse();
assertThat(typeExpr.getFieldPathsExposed())
.containsExactly(FieldPath.of(ProtoTypeMask.HIDDEN_FIELD));
}

@Test
public void withFieldsAsVariableDeclarations() {
assertThat(ProtoTypeMask.ofAllFields("google.type.Expr").fieldsAreVariableDeclarations())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import dev.cel.common.types.ProtoMessageType;
import dev.cel.common.types.ProtoMessageTypeProvider;
import dev.cel.common.types.SimpleType;
import dev.cel.common.types.StructType.Field;
import java.util.Arrays;
import java.util.Optional;
import org.junit.Test;
Expand Down Expand Up @@ -72,7 +73,7 @@ public void lookupFieldNames_noProtoDecls() {
ProtoTypeMaskTypeProvider protoTypeMaskProvider =
new ProtoTypeMaskTypeProvider(celTypeProvider, ImmutableSet.of());
ProtoMessageType protoType = assertTypeFound(protoTypeMaskProvider, ATTRIBUTE_CONTEXT_TYPE);
assertThat(protoType.fields().stream().map(f -> f.name()).collect(toImmutableList()))
assertThat(protoType.fields().stream().map(Field::name).collect(toImmutableList()))
.containsExactly(
"resource",
"request",
Expand All @@ -87,6 +88,41 @@ public void lookupFieldNames_noProtoDecls() {
assertThat(protoType).isSameInstanceAs(origProtoType);
}

@Test
public void lookupFieldNames_allFieldsHidden() {
CelTypeProvider celTypeProvider =
new ProtoMessageTypeProvider(ImmutableSet.of(AttributeContext.getDescriptor()));
ProtoTypeMaskTypeProvider protoTypeMaskProvider =
new ProtoTypeMaskTypeProvider(
celTypeProvider,
ImmutableSet.of(ProtoTypeMask.ofAllFieldsHidden(ATTRIBUTE_CONTEXT_TYPE)));

ProtoMessageType protoType = assertTypeFound(protoTypeMaskProvider, ATTRIBUTE_CONTEXT_TYPE);
assertThat(protoType.fieldNames()).isEmpty();
ProtoMessageType origProtoType = assertTypeFound(celTypeProvider, ATTRIBUTE_CONTEXT_TYPE);
assertThat(protoType).isNotSameInstanceAs(origProtoType);
}

@Test
public void protoTypeMaskProvider_hiddenFieldSentinelCharOnSubPath_throws() {
CelTypeProvider celTypeProvider =
new ProtoMessageTypeProvider(ImmutableSet.of(AttributeContext.getDescriptor()));

IllegalArgumentException e =
assertThrows(
IllegalArgumentException.class,
() ->
new ProtoTypeMaskTypeProvider(
celTypeProvider,
ImmutableSet.of(
ProtoTypeMask.of(
"google.rpc.context.AttributeContext",
FieldMask.newBuilder().addPaths("resource.!").build()))));
assertThat(e)
.hasMessageThat()
.contains("message google.rpc.context.AttributeContext.Resource does not declare field: !");
}

@Test
public void lookupFieldNames_fullProtoDecl() {
CelTypeProvider celTypeProvider =
Expand Down Expand Up @@ -263,7 +299,7 @@ private ProtoMessageType assertTypeFound(CelTypeProvider celTypeProvider, String

private void assertTypeHasFields(ProtoMessageType protoType, ImmutableSet<String> fields) {
ImmutableSet<String> typeFieldNames =
protoType.fields().stream().map(f -> f.name()).collect(toImmutableSet());
protoType.fields().stream().map(Field::name).collect(toImmutableSet());
assertThat(typeFieldNames).containsExactlyElementsIn(fields);
}

Expand Down

0 comments on commit 772b6e8

Please sign in to comment.