diff --git a/README.md b/README.md index c56323c4..15875b4a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Conformance](https://github.com/bufbuild/protovalidate-java/actions/workflows/conformance.yaml/badge.svg)](https://github.com/bufbuild/protovalidate-java/actions/workflows/conformance.yaml) [![BSR](https://img.shields.io/badge/BSR-Module-0C65EC)][buf-mod] -[Protovalidate][protovalidate] provides standard annotations to validate common constraints on messages and fields, as well as the ability to use [CEL][cel] to write custom constraints. It's the next generation of [protoc-gen-validate][protoc-gen-validate], the only widely used validation library for Protobuf. +[Protovalidate][protovalidate] provides standard annotations to validate common rules on messages and fields, as well as the ability to use [CEL][cel] to write custom rules. It's the next generation of [protoc-gen-validate][protoc-gen-validate], the only widely used validation library for Protobuf. With Protovalidate, you can annotate your Protobuf messages with both standard and custom validation rules: diff --git a/conformance/expected-failures.yaml b/conformance/expected-failures.yaml index 622d9f62..27af47e8 100644 --- a/conformance/expected-failures.yaml +++ b/conformance/expected-failures.yaml @@ -1,109 +1,39 @@ -custom_constraints: +custom_rules: - field_expression/map/enum/invalid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum]:{val:{key:4 value:ENUM_ONE} val:{key:8 value:ENUM_UNSPECIFIED} val:{key:12 value:ENUM_ONE}} - # want: validation error (1 violation) - # 1. constraint_id: "field_expression.map.enum" - # message: "test message field_expression.map.enum" - # field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_MESSAGE} - # rule: "cel[0]" elements:{field_number:23 field_name:"cel" field_type:TYPE_MESSAGE index:0} - # got: compilation err: Failed to compile expression field_expression.map.enum: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k] == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum.ValEntry, !error!)' - # | this.all(k, this[k] == 1) - # | ................^ - field_expression/map/enum/valid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum]:{val:{key:4 value:ENUM_ONE} val:{key:8 value:ENUM_ONE}} - # want: valid - # got: compilation err: Failed to compile expression field_expression.map.enum: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k] == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapEnum.ValEntry, !error!)' - # | this.all(k, this[k] == 1) - # | ................^ - field_expression/map/message/invalid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage]:{val:{key:4 value:{a:1}} val:{key:8 value:{a:2}} val:{key:12 value:{a:1}}} - # want: validation error (1 violation) - # 1. constraint_id: "field_expression.map.message" - # message: "test message field_expression.map.message" - # field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_MESSAGE} - # rule: "cel[0]" elements:{field_number:23 field_name:"cel" field_type:TYPE_MESSAGE index:0} - # got: compilation err: Failed to compile expression field_expression.map.message: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k].a == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage.ValEntry, !error!)' - # | this.all(k, this[k].a == 1) - # | ................^ - field_expression/map/message/valid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage]:{val:{key:4 value:{a:1}} val:{key:8 value:{a:1}}} - # want: valid - # got: compilation err: Failed to compile expression field_expression.map.message: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k].a == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapMessage.ValEntry, !error!)' - # | this.all(k, this[k].a == 1) - # | ................^ - - field_expression/map/scalar/invalid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar]:{val:{key:1 value:1} val:{key:2 value:2} val:{key:3 value:1}} - # want: validation error (1 violation) - # 1. constraint_id: "field_expression.map.scalar" - # message: "test message field_expression.map.scalar" - # field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_MESSAGE} - # rule: "cel[0]" elements:{field_number:23 field_name:"cel" field_type:TYPE_MESSAGE index:0} - # got: compilation err: Failed to compile expression field_expression.map.scalar: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k] == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar.ValEntry, !error!)' - # | this.all(k, this[k] == 1) - # | ................^ - - field_expression/map/scalar/valid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar]:{val:{key:1 value:1}} - # want: valid - # got: compilation err: Failed to compile expression field_expression.map.scalar: - #ERROR: :1:1: expression of type 'buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar.ValEntry' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(k, this[k] == 1) - # | ^ - #ERROR: :1:17: found no matching overload for '_[_]' applied to '(buf.validate.conformance.cases.custom_constraints.FieldExpressionMapScalar.ValEntry, !error!)' - # | this.all(k, this[k] == 1) - # | ................^ - field_expression/repeated/enum/invalid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionRepeatedEnum]:{val:ENUM_ONE val:ENUM_UNSPECIFIED val:ENUM_ONE} - # want: validation error (1 violation) - # 1. constraint_id: "field_expression.repeated.enum" - # message: "test message field_expression.repeated.enum" - # field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_ENUM} - # rule: "cel[0]" elements:{field_number:23 field_name:"cel" field_type:TYPE_MESSAGE index:0} - # got: compilation err: Failed to compile expression field_expression.repeated.enum: - #ERROR: :1:1: expression of type 'int' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(e, e == 1) - # | ^ - field_expression/repeated/enum/valid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionRepeatedEnum]:{val:ENUM_ONE val:ENUM_ONE} - # want: valid - # got: compilation err: Failed to compile expression field_expression.repeated.enum: - #ERROR: :1:1: expression of type 'int' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(e, e == 1) - # | ^ - field_expression/repeated/scalar/invalid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionRepeatedScalar]:{val:1 val:2 val:1} - # want: validation error (1 violation) - # 1. constraint_id: "field_expression.repeated.scalar" - # message: "test message field_expression.repeated.scalar" - # field: "val" elements:{field_number:1 field_name:"val" field_type:TYPE_INT32} - # rule: "cel[0]" elements:{field_number:23 field_name:"cel" field_type:TYPE_MESSAGE index:0} - # got: compilation err: Failed to compile expression field_expression.repeated.scalar: - #ERROR: :1:1: expression of type 'int' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(e, e == 1) - # | ^ - field_expression/repeated/scalar/valid - # input: [type.googleapis.com/buf.validate.conformance.cases.custom_constraints.FieldExpressionRepeatedScalar]:{val:1 val:1} - # want: valid - # got: compilation err: Failed to compile expression field_expression.repeated.scalar: - #ERROR: :1:1: expression of type 'int' cannot be range of a comprehension (must be list, map, or dynamic) - # | this.all(e, e == 1) - # | ^ + - field_expression/map/bool/valid + - field_expression/map/bool/invalid + - field_expression/map/string/valid + - field_expression/map/string/invalid + - field_expression/map/int32/valid + - field_expression/map/int32/invalid + - field_expression/map/uint32/valid + - field_expression/map/uint32/invalid + - field_expression/map/int64/valid + - field_expression/map/int64/invalid + - field_expression/map/uint64/valid + - field_expression/map/uint64/invalid +kitchen_sink: + - field/embedded/invalid + - field/transitive/invalid + - many/all-non-message-fields/invalid + - field/invalid +standard_rules/repeated: + - items/in/invalid + - items/not_in/invalid +library/is_ip: + - version/6/invalid/ipv6/7h16_double_colon_1h16 + - version/6/invalid/ipv6/7h16_double_colon + - version/6/invalid/ipv6/double_colon_8h16 + - version/6/invalid/ipv6/1h16_double_colon_7h16 +standard_rules/well_known_types/duration: + - in/invalid + - not in/invalid + + diff --git a/conformance/src/test/java/build/buf/protovalidate/ValidatorTest.java b/conformance/src/test/java/build/buf/protovalidate/ValidatorTest.java index b6f8a2c7..e9929c91 100644 --- a/conformance/src/test/java/build/buf/protovalidate/ValidatorTest.java +++ b/conformance/src/test/java/build/buf/protovalidate/ValidatorTest.java @@ -41,10 +41,10 @@ import build.buf.validate.conformance.cases.TimestampConst; import build.buf.validate.conformance.cases.TimestampWithin; import build.buf.validate.conformance.cases.WrapperDouble; -import build.buf.validate.conformance.cases.custom_constraints.DynRuntimeError; -import build.buf.validate.conformance.cases.custom_constraints.FieldExpressionMultipleScalar; -import build.buf.validate.conformance.cases.custom_constraints.FieldExpressionNestedScalar; -import build.buf.validate.conformance.cases.custom_constraints.FieldExpressionScalar; +import build.buf.validate.conformance.cases.custom_rules.DynRuntimeError; +import build.buf.validate.conformance.cases.custom_rules.FieldExpressionMultipleScalar; +import build.buf.validate.conformance.cases.custom_rules.FieldExpressionNestedScalar; +import build.buf.validate.conformance.cases.custom_rules.FieldExpressionScalar; import com.google.protobuf.ByteString; import com.google.protobuf.DoubleValue; import com.google.protobuf.Duration; diff --git a/gradle.properties b/gradle.properties index 8f662cd7..36b02746 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Version of buf.build/bufbuild/protovalidate to use. -protovalidate.version = v0.10.4 +protovalidate.version = v0.11.0 # Arguments to the protovalidate-conformance CLI protovalidate.conformance.args = --strict_message --strict_error --expected_failures=expected-failures.yaml diff --git a/src/main/java/build/buf/protovalidate/AnyEvaluator.java b/src/main/java/build/buf/protovalidate/AnyEvaluator.java index a93777ff..a6b782ae 100644 --- a/src/main/java/build/buf/protovalidate/AnyEvaluator.java +++ b/src/main/java/build/buf/protovalidate/AnyEvaluator.java @@ -16,8 +16,8 @@ import build.buf.protovalidate.exceptions.ExecutionException; import build.buf.validate.AnyRules; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; +import build.buf.validate.FieldRules; import com.google.protobuf.Descriptors; import com.google.protobuf.Message; import java.util.ArrayList; @@ -33,7 +33,7 @@ * runtime. */ class AnyEvaluator implements Evaluator { - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; private final Descriptors.FieldDescriptor typeURLDescriptor; private final Set in; private final List inValue; @@ -41,7 +41,7 @@ class AnyEvaluator implements Evaluator { private final List notInValue; private static final Descriptors.FieldDescriptor ANY_DESCRIPTOR = - FieldConstraints.getDescriptor().findFieldByNumber(FieldConstraints.ANY_FIELD_NUMBER); + FieldRules.getDescriptor().findFieldByNumber(FieldRules.ANY_FIELD_NUMBER); private static final Descriptors.FieldDescriptor IN_DESCRIPTOR = AnyRules.getDescriptor().findFieldByNumber(AnyRules.IN_FIELD_NUMBER); @@ -67,7 +67,7 @@ class AnyEvaluator implements Evaluator { Descriptors.FieldDescriptor typeURLDescriptor, List in, List notIn) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); this.typeURLDescriptor = typeURLDescriptor; this.in = stringsToSet(in); this.inValue = in; @@ -76,39 +76,39 @@ class AnyEvaluator implements Evaluator { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { Message anyValue = val.messageValue(); if (anyValue == null) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } - List violationList = new ArrayList<>(); + List violationList = new ArrayList<>(); String typeURL = (String) anyValue.getField(typeURLDescriptor); if (!in.isEmpty() && !in.contains(typeURL)) { - ConstraintViolation.Builder violation = - ConstraintViolation.newBuilder() + RuleViolation.Builder violation = + RuleViolation.newBuilder() .addAllRulePathElements(helper.getRulePrefixElements()) .addAllRulePathElements(IN_RULE_PATH.getElementsList()) .addFirstFieldPathElement(helper.getFieldPathElement()) - .setConstraintId("any.in") + .setRuleId("any.in") .setMessage("type URL must be in the allow list") - .setFieldValue(new ConstraintViolation.FieldValue(val)) - .setRuleValue(new ConstraintViolation.FieldValue(this.inValue, IN_DESCRIPTOR)); + .setFieldValue(new RuleViolation.FieldValue(val)) + .setRuleValue(new RuleViolation.FieldValue(this.inValue, IN_DESCRIPTOR)); violationList.add(violation); if (failFast) { return violationList; } } if (!notIn.isEmpty() && notIn.contains(typeURL)) { - ConstraintViolation.Builder violation = - ConstraintViolation.newBuilder() + RuleViolation.Builder violation = + RuleViolation.newBuilder() .addAllRulePathElements(helper.getRulePrefixElements()) .addAllRulePathElements(NOT_IN_RULE_PATH.getElementsList()) .addFirstFieldPathElement(helper.getFieldPathElement()) - .setConstraintId("any.not_in") + .setRuleId("any.not_in") .setMessage("type URL must not be in the block list") - .setFieldValue(new ConstraintViolation.FieldValue(val)) - .setRuleValue(new ConstraintViolation.FieldValue(this.notInValue, NOT_IN_DESCRIPTOR)); + .setFieldValue(new RuleViolation.FieldValue(val)) + .setRuleValue(new RuleViolation.FieldValue(this.notInValue, NOT_IN_DESCRIPTOR)); violationList.add(violation); } return violationList; diff --git a/src/main/java/build/buf/protovalidate/CelPrograms.java b/src/main/java/build/buf/protovalidate/CelPrograms.java index 38bcc097..74ab7abf 100644 --- a/src/main/java/build/buf/protovalidate/CelPrograms.java +++ b/src/main/java/build/buf/protovalidate/CelPrograms.java @@ -21,7 +21,7 @@ /** Evaluator that executes a {@link CompiledProgram}. */ class CelPrograms implements Evaluator { - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; /** A list of {@link CompiledProgram} that will be executed against the input message. */ private final List programs; @@ -32,7 +32,7 @@ class CelPrograms implements Evaluator { * @param compiledPrograms The programs to execute. */ CelPrograms(@Nullable ValueEvaluator valueEvaluator, List compiledPrograms) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); this.programs = compiledPrograms; } @@ -42,12 +42,12 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { Variable activation = Variable.newThisVariable(val.value(Object.class)); - List violations = new ArrayList<>(); + List violations = new ArrayList<>(); for (CompiledProgram program : programs) { - ConstraintViolation.Builder violation = program.eval(val, activation); + RuleViolation.Builder violation = program.eval(val, activation); if (violation != null) { violations.add(violation); if (failFast) { diff --git a/src/main/java/build/buf/protovalidate/CompiledProgram.java b/src/main/java/build/buf/protovalidate/CompiledProgram.java index d9343ccc..3fac0d33 100644 --- a/src/main/java/build/buf/protovalidate/CompiledProgram.java +++ b/src/main/java/build/buf/protovalidate/CompiledProgram.java @@ -32,7 +32,7 @@ class CompiledProgram { /** The original expression that was compiled into the program from the proto file. */ private final Expression source; - /** The field path from FieldConstraints to the constraint rule value. */ + /** The field path from FieldRules to the rule value. */ @Nullable private final FieldPath rulePath; /** The rule value. */ @@ -43,7 +43,7 @@ class CompiledProgram { * * @param program The compiled CEL program. * @param source The original expression that was compiled into the program. - * @param rulePath The field path from the FieldConstraints to the rule value. + * @param rulePath The field path from the FieldRules to the rule value. * @param ruleValue The rule value. */ public CompiledProgram( @@ -63,7 +63,7 @@ public CompiledProgram( * violations. * @throws ExecutionException If the evaluation of the CEL program fails with an error. */ - public ConstraintViolation.@Nullable Builder eval(Value fieldValue, Variable bindings) + public RuleViolation.@Nullable Builder eval(Value fieldValue, Variable bindings) throws ExecutionException { Program.EvalResult evalResult = program.eval(bindings); Val val = evalResult.getVal(); @@ -75,33 +75,29 @@ public CompiledProgram( if ("".equals(value)) { return null; } - ConstraintViolation.Builder builder = - ConstraintViolation.newBuilder() - .setConstraintId(this.source.id) - .setMessage(value.toString()); + RuleViolation.Builder builder = + RuleViolation.newBuilder().setRuleId(this.source.id).setMessage(value.toString()); if (fieldValue.fieldDescriptor() != null) { - builder.setFieldValue(new ConstraintViolation.FieldValue(fieldValue)); + builder.setFieldValue(new RuleViolation.FieldValue(fieldValue)); } if (rulePath != null) { builder.addAllRulePathElements(rulePath.getElementsList()); } if (ruleValue != null && ruleValue.fieldDescriptor() != null) { - builder.setRuleValue(new ConstraintViolation.FieldValue(ruleValue)); + builder.setRuleValue(new RuleViolation.FieldValue(ruleValue)); } return builder; } else if (value instanceof Boolean) { if (val.booleanValue()) { return null; } - ConstraintViolation.Builder builder = - ConstraintViolation.newBuilder() - .setConstraintId(this.source.id) - .setMessage(this.source.message); + RuleViolation.Builder builder = + RuleViolation.newBuilder().setRuleId(this.source.id).setMessage(this.source.message); if (rulePath != null) { builder.addAllRulePathElements(rulePath.getElementsList()); } if (ruleValue != null && ruleValue.fieldDescriptor() != null) { - builder.setRuleValue(new ConstraintViolation.FieldValue(ruleValue)); + builder.setRuleValue(new RuleViolation.FieldValue(ruleValue)); } return builder; } else { diff --git a/src/main/java/build/buf/protovalidate/Config.java b/src/main/java/build/buf/protovalidate/Config.java index 559b5b80..7c9f3ea7 100644 --- a/src/main/java/build/buf/protovalidate/Config.java +++ b/src/main/java/build/buf/protovalidate/Config.java @@ -88,9 +88,9 @@ public ExtensionRegistry getExtensionRegistry() { } /** - * Checks if the configuration for allowing unknown constraint fields is enabled. + * Checks if the configuration for allowing unknown rule fields is enabled. * - * @return if allowing unknown constraint fields is enabled + * @return if allowing unknown rule fields is enabled */ public boolean isAllowingUnknownFields() { return allowUnknownFields; @@ -132,8 +132,8 @@ public Builder setDisableLazy(boolean disableLazy) { * Set the type registry for reparsing protobuf messages. This option should be set alongside * setExtensionRegistry to allow dynamic resolution of predefined rule extensions. It should be * set to a TypeRegistry with all the message types from your file descriptor set registered. By - * default, if any unknown field constraints are found, compilation of the constraints will - * fail; use setAllowUnknownFields to control this behavior. + * default, if any unknown field rules are found, compilation of the rules will fail; use + * setAllowUnknownFields to control this behavior. * *

Note that the message types for any extensions in setExtensionRegistry must be present in * the typeRegistry, and have an exactly-equal Descriptor. If the type registry is not set, the @@ -154,8 +154,8 @@ public Builder setTypeRegistry(TypeRegistry typeRegistry) { * Set the extension registry for resolving unknown extensions. This option should be set * alongside setTypeRegistry to allow dynamic resolution of predefined rule extensions. It * should be set to an ExtensionRegistry with all the extension types from your file descriptor - * set registered. By default, if any unknown field constraints are found, compilation of the - * constraints will fail; use setAllowUnknownFields to control this behavior. + * set registered. By default, if any unknown field rules are found, compilation of the rules + * will fail; use setAllowUnknownFields to control this behavior. * * @param extensionRegistry the extension registry to use * @return this builder @@ -166,13 +166,12 @@ public Builder setExtensionRegistry(ExtensionRegistry extensionRegistry) { } /** - * Set whether unknown constraint fields are allowed. If this setting is set to true, unknown - * standard predefined field constraints and predefined field constraint extensions will be - * ignored. This setting defaults to false, which will result in a CompilationException being - * thrown whenever an unknown field constraint is encountered. Setting this to true will cause - * some field constraints to be ignored; if the descriptor is dynamic, you can instead use - * setExtensionRegistry to provide dynamic type information that protovalidate can use to - * resolve the unknown fields. + * Set whether unknown rule fields are allowed. If this setting is set to true, unknown standard + * predefined field rules and predefined field rule extensions will be ignored. This setting + * defaults to false, which will result in a CompilationException being thrown whenever an + * unknown field rule is encountered. Setting this to true will cause some field rules to be + * ignored; if the descriptor is dynamic, you can instead use setExtensionRegistry to provide + * dynamic type information that protovalidate can use to resolve the unknown fields. * * @param allowUnknownFields setting to apply * @return this builder diff --git a/src/main/java/build/buf/protovalidate/DescriptorMappings.java b/src/main/java/build/buf/protovalidate/DescriptorMappings.java index 20dd9490..24345811 100644 --- a/src/main/java/build/buf/protovalidate/DescriptorMappings.java +++ b/src/main/java/build/buf/protovalidate/DescriptorMappings.java @@ -14,7 +14,7 @@ package build.buf.protovalidate; -import build.buf.validate.FieldConstraints; +import build.buf.validate.FieldRules; import com.google.api.expr.v1alpha1.Type; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.FieldDescriptor; @@ -28,104 +28,98 @@ * DescriptorMappings provides mappings between protocol buffer descriptors and CEL declarations. */ final class DescriptorMappings { - /** Provides a {@link Descriptor} for {@link FieldConstraints}. */ - static final Descriptor FIELD_CONSTRAINTS_DESC = FieldConstraints.getDescriptor(); + /** Provides a {@link Descriptor} for {@link FieldRules}. */ + static final Descriptor FIELD_RULES_DESC = FieldRules.getDescriptor(); - /** Provides the {@link OneofDescriptor} for the type union in {@link FieldConstraints}. */ - static final OneofDescriptor FIELD_CONSTRAINTS_ONEOF_DESC = - FIELD_CONSTRAINTS_DESC.getOneofs().get(0); + /** Provides the {@link OneofDescriptor} for the type union in {@link FieldRules}. */ + static final OneofDescriptor FIELD_RULES_ONEOF_DESC = FIELD_RULES_DESC.getOneofs().get(0); - /** Provides the {@link FieldDescriptor} for the map standard constraints. */ - static final FieldDescriptor MAP_FIELD_CONSTRAINTS_DESC = - FIELD_CONSTRAINTS_DESC.findFieldByName("map"); + /** Provides the {@link FieldDescriptor} for the map standard rules. */ + static final FieldDescriptor MAP_FIELD_RULES_DESC = FIELD_RULES_DESC.findFieldByName("map"); - /** Provides the {@link FieldDescriptor} for the repeated standard constraints. */ - static final FieldDescriptor REPEATED_FIELD_CONSTRAINTS_DESC = - FIELD_CONSTRAINTS_DESC.findFieldByName("repeated"); + /** Provides the {@link FieldDescriptor} for the repeated standard rules. */ + static final FieldDescriptor REPEATED_FIELD_RULES_DESC = + FIELD_RULES_DESC.findFieldByName("repeated"); - /** Maps protocol buffer field kinds to their expected field constraints. */ - static final Map EXPECTED_STANDARD_CONSTRAINTS = - new HashMap<>(); + /** Maps protocol buffer field kinds to their expected field rules. */ + static final Map EXPECTED_STANDARD_RULES = new HashMap<>(); /** - * Returns the {@link build.buf.validate.FieldConstraints} field that is expected for the given - * wrapper well-known type's full name. If ok is false, no standard constraints exist for that - * type. + * Returns the {@link build.buf.validate.FieldRules} field that is expected for the given wrapper + * well-known type's full name. If ok is false, no standard rules exist for that type. */ - static final Map EXPECTED_WKT_CONSTRAINTS = new HashMap<>(); + static final Map EXPECTED_WKT_RULES = new HashMap<>(); static { - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.FLOAT, FIELD_CONSTRAINTS_DESC.findFieldByName("float")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.DOUBLE, FIELD_CONSTRAINTS_DESC.findFieldByName("double")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.INT32, FIELD_CONSTRAINTS_DESC.findFieldByName("int32")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.INT64, FIELD_CONSTRAINTS_DESC.findFieldByName("int64")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.UINT32, FIELD_CONSTRAINTS_DESC.findFieldByName("uint32")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.UINT64, FIELD_CONSTRAINTS_DESC.findFieldByName("uint64")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.SINT32, FIELD_CONSTRAINTS_DESC.findFieldByName("sint32")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.SINT64, FIELD_CONSTRAINTS_DESC.findFieldByName("sint64")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.FIXED32, FIELD_CONSTRAINTS_DESC.findFieldByName("fixed32")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.FIXED64, FIELD_CONSTRAINTS_DESC.findFieldByName("fixed64")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.SFIXED32, FIELD_CONSTRAINTS_DESC.findFieldByName("sfixed32")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.SFIXED64, FIELD_CONSTRAINTS_DESC.findFieldByName("sfixed64")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.BOOL, FIELD_CONSTRAINTS_DESC.findFieldByName("bool")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.STRING, FIELD_CONSTRAINTS_DESC.findFieldByName("string")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.BYTES, FIELD_CONSTRAINTS_DESC.findFieldByName("bytes")); - EXPECTED_STANDARD_CONSTRAINTS.put( - FieldDescriptor.Type.ENUM, FIELD_CONSTRAINTS_DESC.findFieldByName("enum")); - - EXPECTED_WKT_CONSTRAINTS.put( - "google.protobuf.Any", FIELD_CONSTRAINTS_DESC.findFieldByName("any")); - EXPECTED_WKT_CONSTRAINTS.put( - "google.protobuf.Duration", FIELD_CONSTRAINTS_DESC.findFieldByName("duration")); - EXPECTED_WKT_CONSTRAINTS.put( - "google.protobuf.Timestamp", FIELD_CONSTRAINTS_DESC.findFieldByName("timestamp")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.FLOAT, FIELD_RULES_DESC.findFieldByName("float")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.DOUBLE, FIELD_RULES_DESC.findFieldByName("double")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.INT32, FIELD_RULES_DESC.findFieldByName("int32")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.INT64, FIELD_RULES_DESC.findFieldByName("int64")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.UINT32, FIELD_RULES_DESC.findFieldByName("uint32")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.UINT64, FIELD_RULES_DESC.findFieldByName("uint64")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.SINT32, FIELD_RULES_DESC.findFieldByName("sint32")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.SINT64, FIELD_RULES_DESC.findFieldByName("sint64")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.FIXED32, FIELD_RULES_DESC.findFieldByName("fixed32")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.FIXED64, FIELD_RULES_DESC.findFieldByName("fixed64")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.SFIXED32, FIELD_RULES_DESC.findFieldByName("sfixed32")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.SFIXED64, FIELD_RULES_DESC.findFieldByName("sfixed64")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.BOOL, FIELD_RULES_DESC.findFieldByName("bool")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.STRING, FIELD_RULES_DESC.findFieldByName("string")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.BYTES, FIELD_RULES_DESC.findFieldByName("bytes")); + EXPECTED_STANDARD_RULES.put( + FieldDescriptor.Type.ENUM, FIELD_RULES_DESC.findFieldByName("enum")); + + EXPECTED_WKT_RULES.put("google.protobuf.Any", FIELD_RULES_DESC.findFieldByName("any")); + EXPECTED_WKT_RULES.put( + "google.protobuf.Duration", FIELD_RULES_DESC.findFieldByName("duration")); + EXPECTED_WKT_RULES.put( + "google.protobuf.Timestamp", FIELD_RULES_DESC.findFieldByName("timestamp")); } private DescriptorMappings() {} /** - * Returns the {@link FieldConstraints} field that is expected for the given protocol buffer field - * kind. + * Returns the {@link FieldRules} field that is expected for the given protocol buffer field kind. * * @param fqn Fully qualified name of protobuf value wrapper. - * @return The constraints field descriptor for the specified wrapper fully qualified name. + * @return The rules field descriptor for the specified wrapper fully qualified name. */ @Nullable - public static FieldDescriptor expectedWrapperConstraints(String fqn) { + public static FieldDescriptor expectedWrapperRules(String fqn) { switch (fqn) { case "google.protobuf.BoolValue": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.BOOL); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.BOOL); case "google.protobuf.BytesValue": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.BYTES); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.BYTES); case "google.protobuf.DoubleValue": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.DOUBLE); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.DOUBLE); case "google.protobuf.FloatValue": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.FLOAT); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.FLOAT); case "google.protobuf.Int32Value": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.INT32); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.INT32); case "google.protobuf.Int64Value": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.INT64); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.INT64); case "google.protobuf.StringValue": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.STRING); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.STRING); case "google.protobuf.UInt32Value": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.UINT32); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.UINT32); case "google.protobuf.UInt64Value": - return EXPECTED_STANDARD_CONSTRAINTS.get(FieldDescriptor.Type.UINT64); + return EXPECTED_STANDARD_RULES.get(FieldDescriptor.Type.UINT64); default: return null; } @@ -172,22 +166,22 @@ public static Type protoKindToCELType(FieldDescriptor.Type kind) { } /** - * Produces the field descriptor from the {@link FieldConstraints} 'type' oneof that matches the + * Produces the field descriptor from the {@link FieldRules} 'type' oneof that matches the * provided target field descriptor. If the returned value is null, the field does not expect any - * standard constraints. + * standard rules. */ @Nullable - static FieldDescriptor getExpectedConstraintDescriptor( + static FieldDescriptor getExpectedRuleDescriptor( FieldDescriptor fieldDescriptor, boolean forItems) { if (fieldDescriptor.isMapField()) { - return DescriptorMappings.MAP_FIELD_CONSTRAINTS_DESC; + return DescriptorMappings.MAP_FIELD_RULES_DESC; } else if (fieldDescriptor.isRepeated() && !forItems) { - return DescriptorMappings.REPEATED_FIELD_CONSTRAINTS_DESC; + return DescriptorMappings.REPEATED_FIELD_RULES_DESC; } else if (fieldDescriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { - return DescriptorMappings.EXPECTED_WKT_CONSTRAINTS.get( + return DescriptorMappings.EXPECTED_WKT_RULES.get( fieldDescriptor.getMessageType().getFullName()); } else { - return DescriptorMappings.EXPECTED_STANDARD_CONSTRAINTS.get(fieldDescriptor.getType()); + return DescriptorMappings.EXPECTED_STANDARD_RULES.get(fieldDescriptor.getType()); } } diff --git a/src/main/java/build/buf/protovalidate/EmbeddedMessageEvaluator.java b/src/main/java/build/buf/protovalidate/EmbeddedMessageEvaluator.java index 297688e8..35b005a7 100644 --- a/src/main/java/build/buf/protovalidate/EmbeddedMessageEvaluator.java +++ b/src/main/java/build/buf/protovalidate/EmbeddedMessageEvaluator.java @@ -19,11 +19,11 @@ import java.util.List; class EmbeddedMessageEvaluator implements Evaluator { - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; private final MessageEvaluator messageEvaluator; EmbeddedMessageEvaluator(ValueEvaluator valueEvaluator, MessageEvaluator messageEvaluator) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); this.messageEvaluator = messageEvaluator; } @@ -33,7 +33,7 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { return FieldPathUtils.updatePaths( messageEvaluator.evaluate(val, failFast), diff --git a/src/main/java/build/buf/protovalidate/EnumEvaluator.java b/src/main/java/build/buf/protovalidate/EnumEvaluator.java index ec588d82..e2484983 100644 --- a/src/main/java/build/buf/protovalidate/EnumEvaluator.java +++ b/src/main/java/build/buf/protovalidate/EnumEvaluator.java @@ -16,8 +16,8 @@ import build.buf.protovalidate.exceptions.ExecutionException; import build.buf.validate.EnumRules; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; +import build.buf.validate.FieldRules; import com.google.protobuf.Descriptors; import java.util.Collections; import java.util.List; @@ -29,7 +29,7 @@ * check is handled outside CEL as enums are completely type erased to integers. */ class EnumEvaluator implements Evaluator { - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; /** Captures all the defined values for this enum */ private final Set values; @@ -41,8 +41,7 @@ class EnumEvaluator implements Evaluator { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.ENUM_FIELD_NUMBER))) + FieldRules.getDescriptor().findFieldByNumber(FieldRules.ENUM_FIELD_NUMBER))) .addElements(FieldPathUtils.fieldPathElement(DEFINED_ONLY_DESCRIPTOR)) .build(); @@ -53,7 +52,7 @@ class EnumEvaluator implements Evaluator { */ EnumEvaluator( ValueEvaluator valueEvaluator, List valueDescriptors) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); if (valueDescriptors.isEmpty()) { this.values = Collections.emptySet(); } else { @@ -78,23 +77,23 @@ public boolean tautology() { * @throws ExecutionException if an error occurs during the evaluation. */ @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { Descriptors.EnumValueDescriptor enumValue = val.value(Descriptors.EnumValueDescriptor.class); if (enumValue == null) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } if (!values.contains(enumValue.getNumber())) { return Collections.singletonList( - ConstraintViolation.newBuilder() + RuleViolation.newBuilder() .addAllRulePathElements(helper.getRulePrefixElements()) .addAllRulePathElements(DEFINED_ONLY_RULE_PATH.getElementsList()) .addFirstFieldPathElement(helper.getFieldPathElement()) - .setConstraintId("enum.defined_only") + .setRuleId("enum.defined_only") .setMessage("value must be one of the defined enum values") - .setFieldValue(new ConstraintViolation.FieldValue(val)) - .setRuleValue(new ConstraintViolation.FieldValue(true, DEFINED_ONLY_DESCRIPTOR))); + .setFieldValue(new RuleViolation.FieldValue(val)) + .setRuleValue(new RuleViolation.FieldValue(true, DEFINED_ONLY_DESCRIPTOR))); } - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } } diff --git a/src/main/java/build/buf/protovalidate/Evaluator.java b/src/main/java/build/buf/protovalidate/Evaluator.java index bb30a83c..a12e6c89 100644 --- a/src/main/java/build/buf/protovalidate/Evaluator.java +++ b/src/main/java/build/buf/protovalidate/Evaluator.java @@ -31,13 +31,12 @@ interface Evaluator { /** * Checks that the provided val is valid. Unless failFast is true, evaluation attempts to find all - * {@link ConstraintViolation} present in val instead of returning only the first {@link - * ConstraintViolation}. + * {@link RuleViolation} present in val instead of returning only the first {@link RuleViolation}. * * @param val The value to validate. * @param failFast If true, validation stops after the first failure. * @return The result of validation on the specified value. * @throws ExecutionException If evaluation fails to complete. */ - List evaluate(Value val, boolean failFast) throws ExecutionException; + List evaluate(Value val, boolean failFast) throws ExecutionException; } diff --git a/src/main/java/build/buf/protovalidate/EvaluatorBuilder.java b/src/main/java/build/buf/protovalidate/EvaluatorBuilder.java index 0d7040a8..7ec99a58 100644 --- a/src/main/java/build/buf/protovalidate/EvaluatorBuilder.java +++ b/src/main/java/build/buf/protovalidate/EvaluatorBuilder.java @@ -15,13 +15,13 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.CompilationException; -import build.buf.validate.Constraint; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; import build.buf.validate.FieldPathElement; +import build.buf.validate.FieldRules; import build.buf.validate.Ignore; -import build.buf.validate.MessageConstraints; -import build.buf.validate.OneofConstraints; +import build.buf.validate.MessageRules; +import build.buf.validate.OneofRules; +import build.buf.validate.Rule; import com.google.protobuf.ByteString; import com.google.protobuf.Descriptors; import com.google.protobuf.Descriptors.Descriptor; @@ -45,13 +45,13 @@ class EvaluatorBuilder { private static final FieldPathElement CEL_FIELD_PATH_ELEMENT = FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor().findFieldByNumber(FieldConstraints.CEL_FIELD_NUMBER)); + FieldRules.getDescriptor().findFieldByNumber(FieldRules.CEL_FIELD_NUMBER)); private volatile Map evaluatorCache = Collections.emptyMap(); private final Env env; private final boolean disableLazy; - private final ConstraintCache constraints; + private final RuleCache rules; /** * Constructs a new {@link EvaluatorBuilder}. @@ -62,7 +62,7 @@ class EvaluatorBuilder { public EvaluatorBuilder(Env env, Config config) { this.env = env; this.disableLazy = config.isDisableLazy(); - this.constraints = new ConstraintCache(env, config); + this.rules = new RuleCache(env, config); } /** @@ -99,7 +99,7 @@ private Evaluator build(Descriptor desc) throws CompilationException { } // Rebuild cache with this descriptor (and any of its dependencies). Map updatedCache = - new DescriptorCacheBuilder(env, constraints, evaluatorCache).build(desc); + new DescriptorCacheBuilder(env, rules, evaluatorCache).build(desc); evaluatorCache = updatedCache; eval = updatedCache.get(desc); if (eval == null) { @@ -111,15 +111,15 @@ private Evaluator build(Descriptor desc) throws CompilationException { } private static class DescriptorCacheBuilder { - private final ConstraintResolver resolver = new ConstraintResolver(); + private final RuleResolver resolver = new RuleResolver(); private final Env env; - private final ConstraintCache constraintCache; + private final RuleCache ruleCache; private final HashMap cache; private DescriptorCacheBuilder( - Env env, ConstraintCache constraintCache, Map previousCache) { + Env env, RuleCache ruleCache, Map previousCache) { this.env = Objects.requireNonNull(env, "env"); - this.constraintCache = Objects.requireNonNull(constraintCache, "constraintCache"); + this.ruleCache = Objects.requireNonNull(ruleCache, "ruleCache"); this.cache = new HashMap<>(previousCache); } @@ -129,7 +129,7 @@ private DescriptorCacheBuilder( * * @param descriptor Descriptor used to build the cache. * @return Unmodifiable map of descriptors to evaluators. - * @throws CompilationException If an error occurs compiling a constraint on the cache. + * @throws CompilationException If an error occurs compiling a rule on the cache. */ public Map build(Descriptor descriptor) throws CompilationException { @@ -153,12 +153,12 @@ private void buildMessage(Descriptor desc, MessageEvaluator msgEval) try { DynamicMessage defaultInstance = DynamicMessage.newBuilder(desc).buildPartial(); Descriptor descriptor = defaultInstance.getDescriptorForType(); - MessageConstraints msgConstraints = resolver.resolveMessageConstraints(descriptor); - if (msgConstraints.getDisabled()) { + MessageRules msgRules = resolver.resolveMessageRules(descriptor); + if (msgRules.getDisabled()) { return; } - processMessageExpressions(descriptor, msgConstraints, msgEval, defaultInstance); - processOneofConstraints(descriptor, msgEval); + processMessageExpressions(descriptor, msgRules, msgEval, defaultInstance); + processOneofRules(descriptor, msgEval); processFields(descriptor, msgEval); } catch (InvalidProtocolBufferException e) { throw new CompilationException( @@ -167,12 +167,9 @@ private void buildMessage(Descriptor desc, MessageEvaluator msgEval) } private void processMessageExpressions( - Descriptor desc, - MessageConstraints msgConstraints, - MessageEvaluator msgEval, - DynamicMessage message) + Descriptor desc, MessageRules msgRules, MessageEvaluator msgEval, DynamicMessage message) throws CompilationException { - List celList = msgConstraints.getCelList(); + List celList = msgRules.getCelList(); if (celList.isEmpty()) { return; } @@ -181,20 +178,19 @@ private void processMessageExpressions( EnvOption.types(message), EnvOption.declarations( Decls.newVar(Variable.THIS_NAME, Decls.newObjectType(desc.getFullName())))); - List compiledPrograms = compileConstraints(celList, finalEnv, false); + List compiledPrograms = compileRules(celList, finalEnv, false); if (compiledPrograms.isEmpty()) { throw new CompilationException("compile returned null"); } msgEval.append(new CelPrograms(null, compiledPrograms)); } - private void processOneofConstraints(Descriptor desc, MessageEvaluator msgEval) + private void processOneofRules(Descriptor desc, MessageEvaluator msgEval) throws InvalidProtocolBufferException, CompilationException { List oneofs = desc.getOneofs(); for (Descriptors.OneofDescriptor oneofDesc : oneofs) { - OneofConstraints oneofConstraints = resolver.resolveOneofConstraints(oneofDesc); - OneofEvaluator oneofEvaluatorEval = - new OneofEvaluator(oneofDesc, oneofConstraints.getRequired()); + OneofRules oneofRules = resolver.resolveOneofRules(oneofDesc); + OneofEvaluator oneofEvaluatorEval = new OneofEvaluator(oneofDesc, oneofRules.getRequired()); msgEval.append(oneofEvaluatorEval); } } @@ -204,18 +200,16 @@ private void processFields(Descriptor desc, MessageEvaluator msgEval) List fields = desc.getFields(); for (FieldDescriptor fieldDescriptor : fields) { FieldDescriptor descriptor = desc.findFieldByName(fieldDescriptor.getName()); - FieldConstraints fieldConstraints = resolver.resolveFieldConstraints(descriptor); - FieldEvaluator fldEval = buildField(descriptor, fieldConstraints); + FieldRules fieldRules = resolver.resolveFieldRules(descriptor); + FieldEvaluator fldEval = buildField(descriptor, fieldRules); msgEval.append(fldEval); } } - private FieldEvaluator buildField( - FieldDescriptor fieldDescriptor, FieldConstraints fieldConstraints) + private FieldEvaluator buildField(FieldDescriptor fieldDescriptor, FieldRules fieldRules) throws CompilationException { ValueEvaluator valueEvaluatorEval = new ValueEvaluator(fieldDescriptor, null); - boolean ignoreDefault = - fieldDescriptor.hasPresence() && shouldIgnoreDefault(fieldConstraints); + boolean ignoreDefault = fieldDescriptor.hasPresence() && shouldIgnoreDefault(fieldRules); Object zero = null; if (ignoreDefault) { zero = zeroValue(fieldDescriptor, false); @@ -224,50 +218,45 @@ private FieldEvaluator buildField( new FieldEvaluator( valueEvaluatorEval, fieldDescriptor, - fieldConstraints.getRequired(), + fieldRules.getRequired(), fieldDescriptor.hasPresence(), - fieldConstraints.getIgnore(), + fieldRules.getIgnore(), zero); - buildValue(fieldDescriptor, fieldConstraints, fieldEvaluator.valueEvaluator); + buildValue(fieldDescriptor, fieldRules, fieldEvaluator.valueEvaluator); return fieldEvaluator; } - private static boolean shouldIgnoreEmpty(FieldConstraints constraints) { - return constraints.getIgnore() == Ignore.IGNORE_IF_UNPOPULATED - || constraints.getIgnore() == Ignore.IGNORE_IF_DEFAULT_VALUE; + private static boolean shouldIgnoreEmpty(FieldRules rules) { + return rules.getIgnore() == Ignore.IGNORE_IF_UNPOPULATED + || rules.getIgnore() == Ignore.IGNORE_IF_DEFAULT_VALUE; } - private static boolean shouldIgnoreDefault(FieldConstraints constraints) { - return constraints.getIgnore() == Ignore.IGNORE_IF_DEFAULT_VALUE; + private static boolean shouldIgnoreDefault(FieldRules rules) { + return rules.getIgnore() == Ignore.IGNORE_IF_DEFAULT_VALUE; } private void buildValue( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluator) + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluator) throws CompilationException { - - if (fieldConstraints.getIgnore() == Ignore.IGNORE_ALWAYS) { + if (fieldRules.getIgnore() == Ignore.IGNORE_ALWAYS) { return; } - processIgnoreEmpty(fieldDescriptor, fieldConstraints, valueEvaluator); - processFieldExpressions(fieldDescriptor, fieldConstraints, valueEvaluator); - processEmbeddedMessage(fieldDescriptor, fieldConstraints, valueEvaluator); - processWrapperConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); - processStandardConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); - processAnyConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); - processEnumConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); - processMapConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); - processRepeatedConstraints(fieldDescriptor, fieldConstraints, valueEvaluator); + processIgnoreEmpty(fieldDescriptor, fieldRules, valueEvaluator); + processFieldExpressions(fieldDescriptor, fieldRules, valueEvaluator); + processEmbeddedMessage(fieldDescriptor, valueEvaluator); + processWrapperRules(fieldDescriptor, fieldRules, valueEvaluator); + processStandardRules(fieldDescriptor, fieldRules, valueEvaluator); + processAnyRules(fieldDescriptor, fieldRules, valueEvaluator); + processEnumRules(fieldDescriptor, fieldRules, valueEvaluator); + processMapRules(fieldDescriptor, fieldRules, valueEvaluator); + processRepeatedRules(fieldDescriptor, fieldRules, valueEvaluator); } private void processIgnoreEmpty( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { - if (valueEvaluatorEval.hasNestedRule() && shouldIgnoreEmpty(fieldConstraints)) { + if (valueEvaluatorEval.hasNestedRule() && shouldIgnoreEmpty(fieldRules)) { valueEvaluatorEval.setIgnoreEmpty(zeroValue(fieldDescriptor, true)); } } @@ -326,12 +315,10 @@ private Message createMessageForType(Descriptor messageType) throws CompilationE } private void processFieldExpressions( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { - List constraintsCelList = fieldConstraints.getCelList(); - if (constraintsCelList.isEmpty()) { + List rulesCelList = fieldRules.getCelList(); + if (rulesCelList.isEmpty()) { return; } List opts; @@ -359,17 +346,14 @@ private void processFieldExpressions( DescriptorMappings.protoKindToCELType(fieldDescriptor.getType())))); } Env finalEnv = env.extend(opts.toArray(new EnvOption[0])); - List compiledPrograms = - compileConstraints(constraintsCelList, finalEnv, true); + List compiledPrograms = compileRules(rulesCelList, finalEnv, true); if (!compiledPrograms.isEmpty()) { valueEvaluatorEval.append(new CelPrograms(valueEvaluatorEval, compiledPrograms)); } } private void processEmbeddedMessage( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + FieldDescriptor fieldDescriptor, ValueEvaluator valueEvaluatorEval) throws CompilationException { if (fieldDescriptor.getJavaType() != FieldDescriptor.JavaType.MESSAGE || fieldDescriptor.isMapField() @@ -382,10 +366,8 @@ private void processEmbeddedMessage( valueEvaluatorEval.append(embedEval); } - private void processWrapperConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + private void processWrapperRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { if (fieldDescriptor.getJavaType() != FieldDescriptor.JavaType.MESSAGE || fieldDescriptor.isMapField() @@ -393,38 +375,30 @@ private void processWrapperConstraints( return; } FieldDescriptor expectedWrapperDescriptor = - DescriptorMappings.expectedWrapperConstraints( - fieldDescriptor.getMessageType().getFullName()); - if (expectedWrapperDescriptor == null - || !fieldConstraints.hasField(expectedWrapperDescriptor)) { + DescriptorMappings.expectedWrapperRules(fieldDescriptor.getMessageType().getFullName()); + if (expectedWrapperDescriptor == null || !fieldRules.hasField(expectedWrapperDescriptor)) { return; } ValueEvaluator unwrapped = new ValueEvaluator( valueEvaluatorEval.getDescriptor(), valueEvaluatorEval.getNestedRule()); - buildValue( - fieldDescriptor.getMessageType().findFieldByName("value"), fieldConstraints, unwrapped); + buildValue(fieldDescriptor.getMessageType().findFieldByName("value"), fieldRules, unwrapped); valueEvaluatorEval.append(unwrapped); } - private void processStandardConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + private void processStandardRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { List compile = - constraintCache.compile( - fieldDescriptor, fieldConstraints, valueEvaluatorEval.hasNestedRule()); + ruleCache.compile(fieldDescriptor, fieldRules, valueEvaluatorEval.hasNestedRule()); if (compile.isEmpty()) { return; } valueEvaluatorEval.append(new CelPrograms(valueEvaluatorEval, compile)); } - private void processAnyConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) { + private void processAnyRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) { if ((fieldDescriptor.isRepeated() && !valueEvaluatorEval.hasNestedRule()) || fieldDescriptor.getJavaType() != FieldDescriptor.JavaType.MESSAGE || !fieldDescriptor.getMessageType().getFullName().equals("google.protobuf.Any")) { @@ -435,28 +409,24 @@ private void processAnyConstraints( new AnyEvaluator( valueEvaluatorEval, typeURLDesc, - fieldConstraints.getAny().getInList(), - fieldConstraints.getAny().getNotInList())); + fieldRules.getAny().getInList(), + fieldRules.getAny().getNotInList())); } - private void processEnumConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) { + private void processEnumRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) { if (fieldDescriptor.getJavaType() != FieldDescriptor.JavaType.ENUM) { return; } - if (fieldConstraints.getEnum().getDefinedOnly()) { + if (fieldRules.getEnum().getDefinedOnly()) { Descriptors.EnumDescriptor enumDescriptor = fieldDescriptor.getEnumType(); valueEvaluatorEval.append( new EnumEvaluator(valueEvaluatorEval, enumDescriptor.getValues())); } } - private void processMapConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + private void processMapRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { if (!fieldDescriptor.isMapField()) { return; @@ -464,19 +434,17 @@ private void processMapConstraints( MapEvaluator mapEval = new MapEvaluator(valueEvaluatorEval, fieldDescriptor); buildValue( fieldDescriptor.getMessageType().findFieldByNumber(1), - fieldConstraints.getMap().getKeys(), + fieldRules.getMap().getKeys(), mapEval.getKeyEvaluator()); buildValue( fieldDescriptor.getMessageType().findFieldByNumber(2), - fieldConstraints.getMap().getValues(), + fieldRules.getMap().getValues(), mapEval.getValueEvaluator()); valueEvaluatorEval.append(mapEval); } - private void processRepeatedConstraints( - FieldDescriptor fieldDescriptor, - FieldConstraints fieldConstraints, - ValueEvaluator valueEvaluatorEval) + private void processRepeatedRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, ValueEvaluator valueEvaluatorEval) throws CompilationException { if (fieldDescriptor.isMapField() || !fieldDescriptor.isRepeated() @@ -484,14 +452,13 @@ private void processRepeatedConstraints( return; } ListEvaluator listEval = new ListEvaluator(valueEvaluatorEval); - buildValue( - fieldDescriptor, fieldConstraints.getRepeated().getItems(), listEval.itemConstraints); + buildValue(fieldDescriptor, fieldRules.getRepeated().getItems(), listEval.itemRules); valueEvaluatorEval.append(listEval); } - private static List compileConstraints( - List constraints, Env env, boolean isField) throws CompilationException { - List expressions = Expression.fromConstraints(constraints); + private static List compileRules(List rules, Env env, boolean isField) + throws CompilationException { + List expressions = Expression.fromRules(rules); List compiledPrograms = new ArrayList<>(); for (int i = 0; i < expressions.size(); i++) { Expression expression = expressions.get(i); @@ -508,7 +475,7 @@ private static List compileConstraints( env.program(astExpression.ast), astExpression.source, rulePath, - new MessageValue(constraints.get(i)))); + new MessageValue(rules.get(i)))); } return compiledPrograms; } diff --git a/src/main/java/build/buf/protovalidate/Expression.java b/src/main/java/build/buf/protovalidate/Expression.java index 9c4c0820..ce5a33ad 100644 --- a/src/main/java/build/buf/protovalidate/Expression.java +++ b/src/main/java/build/buf/protovalidate/Expression.java @@ -14,27 +14,27 @@ package build.buf.protovalidate; -import build.buf.validate.Constraint; +import build.buf.validate.Rule; import java.util.ArrayList; import java.util.List; /** Expression represents a single CEL expression. */ class Expression { - /** The id of the constraint. */ + /** The id of the rule. */ public final String id; - /** The message of the constraint. */ + /** The message of the rule. */ public final String message; - /** The expression of the constraint. */ + /** The expression of the rule. */ public final String expression; /** * Constructs a new Expression. * - * @param id The ID of the constraint. - * @param message The message of the constraint. - * @param expression The expression of the constraint. + * @param id The ID of the rule. + * @param message The message of the rule. + * @param expression The expression of the rule. */ private Expression(String id, String message, String expression) { this.id = id; @@ -43,24 +43,24 @@ private Expression(String id, String message, String expression) { } /** - * Constructs a new Expression from the given constraint. + * Constructs a new Expression from the given rule. * - * @param constraint The constraint to create the expression from. + * @param rule The rule to create the expression from. */ - private Expression(Constraint constraint) { - this(constraint.getId(), constraint.getMessage(), constraint.getExpression()); + private Expression(Rule rule) { + this(rule.getId(), rule.getMessage(), rule.getExpression()); } /** - * Constructs a new list of {@link Expression} from the given list of constraints. + * Constructs a new list of {@link Expression} from the given list of rules. * - * @param constraints The list of constraints. + * @param rules The list of rules. * @return The list of expressions. */ - public static List fromConstraints(List constraints) { + public static List fromRules(List rules) { List expressions = new ArrayList<>(); - for (build.buf.validate.Constraint constraint : constraints) { - expressions.add(new Expression(constraint)); + for (build.buf.validate.Rule rule : rules) { + expressions.add(new Expression(rule)); } return expressions; } diff --git a/src/main/java/build/buf/protovalidate/FieldEvaluator.java b/src/main/java/build/buf/protovalidate/FieldEvaluator.java index 2f00ca7f..5721da57 100644 --- a/src/main/java/build/buf/protovalidate/FieldEvaluator.java +++ b/src/main/java/build/buf/protovalidate/FieldEvaluator.java @@ -15,8 +15,8 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.ExecutionException; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; +import build.buf.validate.FieldRules; import build.buf.validate.Ignore; import com.google.protobuf.Descriptors.FieldDescriptor; import com.google.protobuf.Message; @@ -28,14 +28,14 @@ /** Performs validation on a single message field, defined by its descriptor. */ class FieldEvaluator implements Evaluator { private static final FieldDescriptor REQUIRED_DESCRIPTOR = - FieldConstraints.getDescriptor().findFieldByNumber(FieldConstraints.REQUIRED_FIELD_NUMBER); + FieldRules.getDescriptor().findFieldByNumber(FieldRules.REQUIRED_FIELD_NUMBER); private static final FieldPath REQUIRED_RULE_PATH = FieldPath.newBuilder() .addElements(FieldPathUtils.fieldPathElement(REQUIRED_DESCRIPTOR)) .build(); - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; /** The {@link ValueEvaluator} to apply to the field's value */ public final ValueEvaluator valueEvaluator; @@ -62,7 +62,7 @@ class FieldEvaluator implements Evaluator { boolean hasPresence, Ignore ignore, @Nullable Object zero) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); this.valueEvaluator = valueEvaluator; this.descriptor = descriptor; this.required = required; @@ -88,8 +88,8 @@ private boolean shouldIgnoreAlways() { /** * Returns whether a field should skip validation on its zero value. * - *

This is generally true for nullable fields or fields with the ignore_empty constraint - * explicitly set. + *

This is generally true for nullable fields or fields with the ignore_empty rule explicitly + * set. */ private boolean shouldIgnoreEmpty() { return this.hasPresence @@ -106,14 +106,14 @@ private boolean shouldIgnoreDefault() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { if (this.shouldIgnoreAlways()) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } Message message = val.messageValue(); if (message == null) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } boolean hasField; if (descriptor.isRepeated()) { @@ -123,20 +123,20 @@ public List evaluate(Value val, boolean failFast) } if (required && !hasField) { return Collections.singletonList( - ConstraintViolation.newBuilder() + RuleViolation.newBuilder() .addFirstFieldPathElement(FieldPathUtils.fieldPathElement(descriptor)) .addAllRulePathElements(helper.getRulePrefixElements()) .addAllRulePathElements(REQUIRED_RULE_PATH.getElementsList()) - .setConstraintId("required") + .setRuleId("required") .setMessage("value is required") - .setRuleValue(new ConstraintViolation.FieldValue(true, REQUIRED_DESCRIPTOR))); + .setRuleValue(new RuleViolation.FieldValue(true, REQUIRED_DESCRIPTOR))); } if (this.shouldIgnoreEmpty() && !hasField) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } Object fieldValue = message.getField(descriptor); if (this.shouldIgnoreDefault() && Objects.equals(zero, fieldValue)) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } return valueEvaluator.evaluate(new ObjectValue(descriptor, fieldValue), failFast); } diff --git a/src/main/java/build/buf/protovalidate/FieldPathUtils.java b/src/main/java/build/buf/protovalidate/FieldPathUtils.java index b07664e8..4706cc5c 100644 --- a/src/main/java/build/buf/protovalidate/FieldPathUtils.java +++ b/src/main/java/build/buf/protovalidate/FieldPathUtils.java @@ -100,12 +100,12 @@ public static FieldPathElement fieldPathElement(Descriptors.FieldDescriptor fiel * @param rulePathElements Rule path elements to prepend. * @return For convenience, the list of violations passed into the violations parameter. */ - public static List updatePaths( - List violations, + public static List updatePaths( + List violations, @Nullable FieldPathElement fieldPathElement, List rulePathElements) { if (fieldPathElement != null || !rulePathElements.isEmpty()) { - for (ConstraintViolation.Builder violation : violations) { + for (RuleViolation.Builder violation : violations) { for (int i = rulePathElements.size() - 1; i >= 0; i--) { violation.addFirstRulePathElement(rulePathElements.get(i)); } diff --git a/src/main/java/build/buf/protovalidate/ListEvaluator.java b/src/main/java/build/buf/protovalidate/ListEvaluator.java index 6b77d229..52bc3e21 100644 --- a/src/main/java/build/buf/protovalidate/ListEvaluator.java +++ b/src/main/java/build/buf/protovalidate/ListEvaluator.java @@ -15,9 +15,9 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.ExecutionException; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; import build.buf.validate.FieldPathElement; +import build.buf.validate.FieldRules; import build.buf.validate.RepeatedRules; import java.util.ArrayList; import java.util.List; @@ -30,38 +30,36 @@ class ListEvaluator implements Evaluator { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.REPEATED_FIELD_NUMBER))) + FieldRules.getDescriptor().findFieldByNumber(FieldRules.REPEATED_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( RepeatedRules.getDescriptor() .findFieldByNumber(RepeatedRules.ITEMS_FIELD_NUMBER))) .build(); - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; - /** Constraints are checked on every item of the list. */ - final ValueEvaluator itemConstraints; + /** Rules are checked on every item of the list. */ + final ValueEvaluator itemRules; /** Constructs a {@link ListEvaluator}. */ ListEvaluator(ValueEvaluator valueEvaluator) { - this.helper = new ConstraintViolationHelper(valueEvaluator); - this.itemConstraints = new ValueEvaluator(null, REPEATED_ITEMS_RULE_PATH); + this.helper = new RuleViolationHelper(valueEvaluator); + this.itemRules = new ValueEvaluator(null, REPEATED_ITEMS_RULE_PATH); } @Override public boolean tautology() { - return itemConstraints.tautology(); + return itemRules.tautology(); } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { - List allViolations = new ArrayList<>(); + List allViolations = new ArrayList<>(); List repeatedValues = val.repeatedValue(); for (int i = 0; i < repeatedValues.size(); i++) { - List violations = - itemConstraints.evaluate(repeatedValues.get(i), failFast); + List violations = itemRules.evaluate(repeatedValues.get(i), failFast); if (violations.isEmpty()) { continue; } diff --git a/src/main/java/build/buf/protovalidate/MapEvaluator.java b/src/main/java/build/buf/protovalidate/MapEvaluator.java index 1e92accd..8bc7ea66 100644 --- a/src/main/java/build/buf/protovalidate/MapEvaluator.java +++ b/src/main/java/build/buf/protovalidate/MapEvaluator.java @@ -15,9 +15,9 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.ExecutionException; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; import build.buf.validate.FieldPathElement; +import build.buf.validate.FieldRules; import build.buf.validate.MapRules; import com.google.protobuf.Descriptors; import java.util.ArrayList; @@ -34,8 +34,7 @@ class MapEvaluator implements Evaluator { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.MAP_FIELD_NUMBER))) + FieldRules.getDescriptor().findFieldByNumber(FieldRules.MAP_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( MapRules.getDescriptor().findFieldByNumber(MapRules.KEYS_FIELD_NUMBER))) @@ -46,19 +45,18 @@ class MapEvaluator implements Evaluator { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.MAP_FIELD_NUMBER))) + FieldRules.getDescriptor().findFieldByNumber(FieldRules.MAP_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( MapRules.getDescriptor().findFieldByNumber(MapRules.VALUES_FIELD_NUMBER))) .build(); - private final ConstraintViolationHelper helper; + private final RuleViolationHelper helper; - /** Constraint for checking the map keys */ + /** Rule for checking the map keys */ private final ValueEvaluator keyEvaluator; - /** Constraint for checking the map values */ + /** Rule for checking the map values */ private final ValueEvaluator valueEvaluator; /** Field descriptor of the map field */ @@ -73,10 +71,10 @@ class MapEvaluator implements Evaluator { /** * Constructs a {@link MapEvaluator}. * - * @param valueEvaluator The value evaluator this constraint exists under. + * @param valueEvaluator The value evaluator this rule exists under. */ MapEvaluator(ValueEvaluator valueEvaluator, Descriptors.FieldDescriptor fieldDescriptor) { - this.helper = new ConstraintViolationHelper(valueEvaluator); + this.helper = new RuleViolationHelper(valueEvaluator); this.keyEvaluator = new ValueEvaluator(null, MAP_KEYS_RULE_PATH); this.valueEvaluator = new ValueEvaluator(null, MAP_VALUES_RULE_PATH); this.fieldDescriptor = fieldDescriptor; @@ -108,9 +106,9 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { - List violations = new ArrayList<>(); + List violations = new ArrayList<>(); Map mapValue = val.mapValue(); for (Map.Entry entry : mapValue.entrySet()) { violations.addAll(evalPairs(entry.getKey(), entry.getValue(), failFast)); @@ -119,29 +117,29 @@ public List evaluate(Value val, boolean failFast) } } if (violations.isEmpty()) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } return violations; } - private List evalPairs(Value key, Value value, boolean failFast) + private List evalPairs(Value key, Value value, boolean failFast) throws ExecutionException { - List keyViolations = + List keyViolations = keyEvaluator.evaluate(key, failFast).stream() .map(violation -> violation.setForKey(true)) .collect(Collectors.toList()); - final List valueViolations; + final List valueViolations; if (failFast && !keyViolations.isEmpty()) { - // Don't evaluate value constraints if failFast is enabled and keys failed validation. + // Don't evaluate value rules if failFast is enabled and keys failed validation. // We still need to continue execution to the end to properly prefix violation field paths. - valueViolations = ConstraintViolation.NO_VIOLATIONS; + valueViolations = RuleViolation.NO_VIOLATIONS; } else { valueViolations = valueEvaluator.evaluate(value, failFast); } if (keyViolations.isEmpty() && valueViolations.isEmpty()) { return Collections.emptyList(); } - List violations = + List violations = new ArrayList<>(keyViolations.size() + valueViolations.size()); violations.addAll(keyViolations); violations.addAll(valueViolations); diff --git a/src/main/java/build/buf/protovalidate/MessageEvaluator.java b/src/main/java/build/buf/protovalidate/MessageEvaluator.java index f5523720..fc496c2d 100644 --- a/src/main/java/build/buf/protovalidate/MessageEvaluator.java +++ b/src/main/java/build/buf/protovalidate/MessageEvaluator.java @@ -34,18 +34,18 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { - List allViolations = new ArrayList<>(); + List allViolations = new ArrayList<>(); for (Evaluator evaluator : evaluators) { - List violations = evaluator.evaluate(val, failFast); + List violations = evaluator.evaluate(val, failFast); if (failFast && !violations.isEmpty()) { return violations; } allViolations.addAll(violations); } if (allViolations.isEmpty()) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } return allViolations; } diff --git a/src/main/java/build/buf/protovalidate/OneofEvaluator.java b/src/main/java/build/buf/protovalidate/OneofEvaluator.java index b2f59f26..2b6a10c9 100644 --- a/src/main/java/build/buf/protovalidate/OneofEvaluator.java +++ b/src/main/java/build/buf/protovalidate/OneofEvaluator.java @@ -46,17 +46,17 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { Message message = val.messageValue(); if (message == null || !required || (message.getOneofFieldDescriptor(descriptor) != null)) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } return Collections.singletonList( - ConstraintViolation.newBuilder() + RuleViolation.newBuilder() .addFirstFieldPathElement( FieldPathElement.newBuilder().setFieldName(descriptor.getName()).build()) - .setConstraintId("required") + .setRuleId("required") .setMessage("exactly one field is required in oneof")); } } diff --git a/src/main/java/build/buf/protovalidate/ConstraintCache.java b/src/main/java/build/buf/protovalidate/RuleCache.java similarity index 63% rename from src/main/java/build/buf/protovalidate/ConstraintCache.java rename to src/main/java/build/buf/protovalidate/RuleCache.java index 740b25a3..25d37b18 100644 --- a/src/main/java/build/buf/protovalidate/ConstraintCache.java +++ b/src/main/java/build/buf/protovalidate/RuleCache.java @@ -15,8 +15,8 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.CompilationException; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; +import build.buf.validate.FieldRules; import build.buf.validate.ValidateProto; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.Descriptors; @@ -43,8 +43,8 @@ import org.projectnessie.cel.common.types.ref.Val; import org.projectnessie.cel.interpreter.Activation; -/** A build-through cache for computed standard constraints. */ -class ConstraintCache { +/** A build-through cache for computed standard rules. */ +class RuleCache { private static class CelRule { public final AstExpression astExpression; public final FieldDescriptor field; @@ -63,7 +63,7 @@ public CelRule(AstExpression astExpression, FieldDescriptor field, FieldPath rul EXTENSION_REGISTRY.add(ValidateProto.predefined); } - /** Partial eval options for evaluating the constraint's expression. */ + /** Partial eval options for evaluating the rule's expression. */ private static final ProgramOption PARTIAL_EVAL_OPTIONS = ProgramOption.evalOptions( EvalOption.OptTrackState, @@ -87,17 +87,17 @@ public CelRule(AstExpression astExpression, FieldDescriptor field, FieldPath rul /** Registry used to resolve dynamic extensions. */ private final ExtensionRegistry extensionRegistry; - /** Whether to allow unknown constraint fields or not. */ + /** Whether to allow unknown rule fields or not. */ private final boolean allowUnknownFields; /** - * Constructs a new build-through cache for the standard constraints, with a provided registry to + * Constructs a new build-through cache for the standard rules, with a provided registry to * resolve dynamic extensions. * * @param env The CEL environment for evaluation. - * @param config The configuration to use for the constraint cache. + * @param config The configuration to use for the rule cache. */ - public ConstraintCache(Env env, Config config) { + public RuleCache(Env env, Config config) { this.env = env; this.typeRegistry = config.getTypeRegistry(); this.extensionRegistry = config.getExtensionRegistry(); @@ -105,29 +105,29 @@ public ConstraintCache(Env env, Config config) { } /** - * Creates the standard constraints for the given field. If forItems is true, the constraints for - * repeated list items is built instead of the constraints on the list itself. + * Creates the standard rules for the given field. If forItems is true, the rules for repeated + * list items is built instead of the rules on the list itself. * * @param fieldDescriptor The field descriptor to be validated. - * @param fieldConstraints The field constraint that is used for validation. + * @param fieldRules The field rule that is used for validation. * @param forItems The field is an item list type. * @return The list of compiled programs. - * @throws CompilationException If the constraints fail to compile. + * @throws CompilationException If the rules fail to compile. */ public List compile( - FieldDescriptor fieldDescriptor, FieldConstraints fieldConstraints, boolean forItems) + FieldDescriptor fieldDescriptor, FieldRules fieldRules, boolean forItems) throws CompilationException { - ResolvedConstraint resolved = resolveConstraints(fieldDescriptor, fieldConstraints, forItems); + ResolvedRule resolved = resolveRules(fieldDescriptor, fieldRules, forItems); if (resolved == null) { - // Message null means there were no constraints resolved. + // Message null means there were no rules resolved. return Collections.emptyList(); } Message message = resolved.message; List completeProgramList = new ArrayList<>(); for (Map.Entry entry : message.getAllFields().entrySet()) { - FieldDescriptor constraintFieldDesc = entry.getKey(); + FieldDescriptor ruleFieldDesc = entry.getKey(); List programList = - compileRule(fieldDescriptor, forItems, resolved.setOneof, constraintFieldDesc, message); + compileRule(fieldDescriptor, forItems, resolved.setOneof, ruleFieldDesc, message); if (programList == null) continue; completeProgramList.addAll(programList); } @@ -173,35 +173,35 @@ public List compile( FieldDescriptor fieldDescriptor, boolean forItems, FieldDescriptor setOneof, - FieldDescriptor constraintFieldDesc, + FieldDescriptor ruleFieldDesc, Message message) throws CompilationException { List celRules = descriptorMap.get(fieldDescriptor); if (celRules != null) { return celRules; } - build.buf.validate.PredefinedConstraints constraints = getFieldConstraints(constraintFieldDesc); - if (constraints == null) return null; - List expressions = Expression.fromConstraints(constraints.getCelList()); + build.buf.validate.PredefinedRules rules = getFieldRules(ruleFieldDesc); + if (rules == null) return null; + List expressions = Expression.fromRules(rules.getCelList()); celRules = new ArrayList<>(expressions.size()); - Env ruleEnv = getRuleEnv(fieldDescriptor, message, constraintFieldDesc, forItems); + Env ruleEnv = getRuleEnv(fieldDescriptor, message, ruleFieldDesc, forItems); for (Expression expression : expressions) { FieldPath rulePath = FieldPath.newBuilder() .addElements(FieldPathUtils.fieldPathElement(setOneof)) - .addElements(FieldPathUtils.fieldPathElement(constraintFieldDesc)) + .addElements(FieldPathUtils.fieldPathElement(ruleFieldDesc)) .build(); celRules.add( new CelRule( - AstExpression.newAstExpression(ruleEnv, expression), constraintFieldDesc, rulePath)); + AstExpression.newAstExpression(ruleEnv, expression), ruleFieldDesc, rulePath)); } - descriptorMap.put(constraintFieldDesc, celRules); + descriptorMap.put(ruleFieldDesc, celRules); return celRules; } - private build.buf.validate.@Nullable PredefinedConstraints getFieldConstraints( - FieldDescriptor constraintFieldDesc) throws CompilationException { - DescriptorProtos.FieldOptions options = constraintFieldDesc.getOptions(); + private build.buf.validate.@Nullable PredefinedRules getFieldRules(FieldDescriptor ruleFieldDesc) + throws CompilationException { + DescriptorProtos.FieldOptions options = ruleFieldDesc.getOptions(); // If the protovalidate field option is unknown, reparse options using our extension registry. if (options.getUnknownFields().hasField(ValidateProto.predefined.getNumber())) { try { @@ -215,128 +215,125 @@ public List compile( return null; } Object extensionValue = options.getField(ValidateProto.predefined.getDescriptor()); - build.buf.validate.PredefinedConstraints constraints; - if (extensionValue instanceof build.buf.validate.PredefinedConstraints) { - constraints = (build.buf.validate.PredefinedConstraints) extensionValue; + build.buf.validate.PredefinedRules rules; + if (extensionValue instanceof build.buf.validate.PredefinedRules) { + rules = (build.buf.validate.PredefinedRules) extensionValue; } else if (extensionValue instanceof MessageLite) { // Extension is parsed but with different gencode. We need to reparse it. try { - constraints = - build.buf.validate.PredefinedConstraints.parseFrom( + rules = + build.buf.validate.PredefinedRules.parseFrom( ((MessageLite) extensionValue).toByteString()); } catch (InvalidProtocolBufferException e) { - throw new CompilationException("Failed to parse field constraints", e); + throw new CompilationException("Failed to parse field rules", e); } } else { // Extension was not a message, just discard it. return null; } - return constraints; + return rules; } /** * Calculates the environment for a specific rule invocation. * - * @param fieldDescriptor The field descriptor of the field with the constraint. - * @param constraintMessage The message of the standard constraints. - * @param constraintFieldDesc The field descriptor of the constraint. + * @param fieldDescriptor The field descriptor of the field with the rule. + * @param ruleMessage The message of the standard rules. + * @param ruleFieldDesc The field descriptor of the rule. * @param forItems Whether the field is a list type or not. * @return An environment with requisite declarations and types added. */ private Env getRuleEnv( FieldDescriptor fieldDescriptor, - Message constraintMessage, - FieldDescriptor constraintFieldDesc, + Message ruleMessage, + FieldDescriptor ruleFieldDesc, boolean forItems) { return env.extend( - EnvOption.types(constraintMessage.getDefaultInstanceForType()), + EnvOption.types(ruleMessage.getDefaultInstanceForType()), EnvOption.declarations( Decls.newVar( Variable.THIS_NAME, DescriptorMappings.getCELType(fieldDescriptor, forItems)), Decls.newVar( Variable.RULES_NAME, - Decls.newObjectType(constraintMessage.getDescriptorForType().getFullName())), - Decls.newVar( - Variable.RULE_NAME, DescriptorMappings.getCELType(constraintFieldDesc, false)))); + Decls.newObjectType(ruleMessage.getDescriptorForType().getFullName())), + Decls.newVar(Variable.RULE_NAME, DescriptorMappings.getCELType(ruleFieldDesc, false)))); } - private static class ResolvedConstraint { + private static class ResolvedRule { final Message message; final FieldDescriptor setOneof; - ResolvedConstraint(Message message, FieldDescriptor setOneof) { + ResolvedRule(Message message, FieldDescriptor setOneof) { this.message = message; this.setOneof = setOneof; } } /** - * Extracts the standard constraints for the specified field. An exception is thrown if the wrong - * constraints are applied to a field (typically if there is a type-mismatch). Null is returned if - * there are no standard constraints to apply to this field. + * Extracts the standard rules for the specified field. An exception is thrown if the wrong rules + * are applied to a field (typically if there is a type-mismatch). Null is returned if there are + * no standard rules to apply to this field. */ @Nullable - private ResolvedConstraint resolveConstraints( - FieldDescriptor fieldDescriptor, FieldConstraints fieldConstraints, boolean forItems) + private ResolvedRule resolveRules( + FieldDescriptor fieldDescriptor, FieldRules fieldRules, boolean forItems) throws CompilationException { - // Get the oneof field descriptor from the field constraints. + // Get the oneof field descriptor from the field rules. FieldDescriptor oneofFieldDescriptor = - fieldConstraints.getOneofFieldDescriptor(DescriptorMappings.FIELD_CONSTRAINTS_ONEOF_DESC); + fieldRules.getOneofFieldDescriptor(DescriptorMappings.FIELD_RULES_ONEOF_DESC); if (oneofFieldDescriptor == null) { - // If the oneof field descriptor is null there are no constraints to resolve. + // If the oneof field descriptor is null there are no rules to resolve. return null; } - // Get the expected constraint descriptor based on the provided field descriptor and the flag + // Get the expected rule descriptor based on the provided field descriptor and the flag // indicating whether it is for items. - FieldDescriptor expectedConstraintDescriptor = - DescriptorMappings.getExpectedConstraintDescriptor(fieldDescriptor, forItems); - if (expectedConstraintDescriptor != null - && !oneofFieldDescriptor.getFullName().equals(expectedConstraintDescriptor.getFullName())) { - // If the expected constraint does not match the actual oneof constraint, throw a + FieldDescriptor expectedRuleDescriptor = + DescriptorMappings.getExpectedRuleDescriptor(fieldDescriptor, forItems); + if (expectedRuleDescriptor != null + && !oneofFieldDescriptor.getFullName().equals(expectedRuleDescriptor.getFullName())) { + // If the expected rule does not match the actual oneof rule, throw a // CompilationError. throw new CompilationException( String.format( - "expected constraint %s, got %s on field %s", - expectedConstraintDescriptor.getName(), + "expected rule %s, got %s on field %s", + expectedRuleDescriptor.getName(), oneofFieldDescriptor.getName(), fieldDescriptor.getName())); } - // If the expected constraint descriptor is null or if the field constraints do not have the + // If the expected rule descriptor is null or if the field rules do not have the // oneof field descriptor - // there are no constraints to resolve, so return null. - if (expectedConstraintDescriptor == null || !fieldConstraints.hasField(oneofFieldDescriptor)) { + // there are no rules to resolve, so return null. + if (expectedRuleDescriptor == null || !fieldRules.hasField(oneofFieldDescriptor)) { return null; } - // Get the field from the field constraints identified by the oneof field descriptor, casted + // Get the field from the field rules identified by the oneof field descriptor, casted // as a Message. - Message typeConstraints = (Message) fieldConstraints.getField(oneofFieldDescriptor); - if (!typeConstraints.getUnknownFields().isEmpty()) { + Message typeRules = (Message) fieldRules.getField(oneofFieldDescriptor); + if (!typeRules.getUnknownFields().isEmpty()) { // If there are unknown fields, try to resolve them using the provided registries. Note that // we use the type registry to resolve the message descriptor. This is because Java protobuf // extension resolution relies on descriptor identity. The user's provided type registry can // provide matching message descriptors for the user's provided extension registry. See the // documentation for Options.setTypeRegistry for more information. - Descriptors.Descriptor expectedConstraintMessageDescriptor = - typeRegistry.find(expectedConstraintDescriptor.getMessageType().getFullName()); - if (expectedConstraintMessageDescriptor == null) { - expectedConstraintMessageDescriptor = expectedConstraintDescriptor.getMessageType(); + Descriptors.Descriptor expectedRuleMessageDescriptor = + typeRegistry.find(expectedRuleDescriptor.getMessageType().getFullName()); + if (expectedRuleMessageDescriptor == null) { + expectedRuleMessageDescriptor = expectedRuleDescriptor.getMessageType(); } try { - typeConstraints = + typeRules = DynamicMessage.parseFrom( - expectedConstraintMessageDescriptor, - typeConstraints.toByteString(), - extensionRegistry); + expectedRuleMessageDescriptor, typeRules.toByteString(), extensionRegistry); } catch (InvalidProtocolBufferException e) { throw new RuntimeException(e); } } - if (!allowUnknownFields && !typeConstraints.getUnknownFields().isEmpty()) { - throw new CompilationException("unrecognized field constraints"); + if (!allowUnknownFields && !typeRules.getUnknownFields().isEmpty()) { + throw new CompilationException("unrecognized field rules"); } - return new ResolvedConstraint(typeConstraints, oneofFieldDescriptor); + return new ResolvedRule(typeRules, oneofFieldDescriptor); } } diff --git a/src/main/java/build/buf/protovalidate/ConstraintResolver.java b/src/main/java/build/buf/protovalidate/RuleResolver.java similarity index 66% rename from src/main/java/build/buf/protovalidate/ConstraintResolver.java rename to src/main/java/build/buf/protovalidate/RuleResolver.java index ffe6bf0e..17091fc6 100644 --- a/src/main/java/build/buf/protovalidate/ConstraintResolver.java +++ b/src/main/java/build/buf/protovalidate/RuleResolver.java @@ -15,9 +15,9 @@ package build.buf.protovalidate; import build.buf.protovalidate.exceptions.CompilationException; -import build.buf.validate.FieldConstraints; -import build.buf.validate.MessageConstraints; -import build.buf.validate.OneofConstraints; +import build.buf.validate.FieldRules; +import build.buf.validate.MessageRules; +import build.buf.validate.OneofRules; import build.buf.validate.ValidateProto; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.Descriptors.Descriptor; @@ -27,8 +27,8 @@ import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.MessageLite; -/** Manages the resolution of protovalidate constraints. */ -class ConstraintResolver { +/** Manages the resolution of protovalidate rules. */ +class RuleResolver { private static final ExtensionRegistry EXTENSION_REGISTRY = ExtensionRegistry.newInstance(); static { @@ -38,12 +38,12 @@ class ConstraintResolver { } /** - * Resolves the constraints for a message descriptor. + * Resolves the rules for a message descriptor. * * @param desc the message descriptor. - * @return the resolved {@link MessageConstraints}. + * @return the resolved {@link MessageRules}. */ - MessageConstraints resolveMessageConstraints(Descriptor desc) + MessageRules resolveMessageRules(Descriptor desc) throws InvalidProtocolBufferException, CompilationException { DescriptorProtos.MessageOptions options = desc.getOptions(); // If the protovalidate message extension is unknown, reparse using extension registry. @@ -52,29 +52,29 @@ MessageConstraints resolveMessageConstraints(Descriptor desc) DescriptorProtos.MessageOptions.parseFrom(options.toByteString(), EXTENSION_REGISTRY); } if (!options.hasExtension(ValidateProto.message)) { - return MessageConstraints.getDefaultInstance(); + return MessageRules.getDefaultInstance(); } // Don't use getExtension here to avoid exception if descriptor types don't match. // This can occur if the extension is generated to a different Java package. Object value = options.getField(ValidateProto.message.getDescriptor()); - if (value instanceof MessageConstraints) { - return ((MessageConstraints) value); + if (value instanceof MessageRules) { + return ((MessageRules) value); } if (value instanceof MessageLite) { - // Possible that this represents the same constraint type, just generated to a different + // Possible that this represents the same rule type, just generated to a different // java_package. - return MessageConstraints.parseFrom(((MessageLite) value).toByteString()); + return MessageRules.parseFrom(((MessageLite) value).toByteString()); } - throw new CompilationException("unexpected message constraint option type: " + value); + throw new CompilationException("unexpected message rule option type: " + value); } /** - * Resolves the constraints for a oneof descriptor. + * Resolves the rules for a oneof descriptor. * * @param desc the oneof descriptor. - * @return the resolved {@link OneofConstraints}. + * @return the resolved {@link OneofRules}. */ - OneofConstraints resolveOneofConstraints(OneofDescriptor desc) + OneofRules resolveOneofRules(OneofDescriptor desc) throws InvalidProtocolBufferException, CompilationException { DescriptorProtos.OneofOptions options = desc.getOptions(); // If the protovalidate oneof extension is unknown, reparse using extension registry. @@ -82,29 +82,29 @@ OneofConstraints resolveOneofConstraints(OneofDescriptor desc) options = DescriptorProtos.OneofOptions.parseFrom(options.toByteString(), EXTENSION_REGISTRY); } if (!options.hasExtension(ValidateProto.oneof)) { - return OneofConstraints.getDefaultInstance(); + return OneofRules.getDefaultInstance(); } // Don't use getExtension here to avoid exception if descriptor types don't match. // This can occur if the extension is generated to a different Java package. Object value = options.getField(ValidateProto.oneof.getDescriptor()); - if (value instanceof OneofConstraints) { - return ((OneofConstraints) value); + if (value instanceof OneofRules) { + return ((OneofRules) value); } if (value instanceof MessageLite) { - // Possible that this represents the same constraint type, just generated to a different + // Possible that this represents the same rule type, just generated to a different // java_package. - return OneofConstraints.parseFrom(((MessageLite) value).toByteString()); + return OneofRules.parseFrom(((MessageLite) value).toByteString()); } - throw new CompilationException("unexpected oneof constraint option type: " + value); + throw new CompilationException("unexpected oneof rule option type: " + value); } /** - * Resolves the constraints for a field descriptor. + * Resolves the rules for a field descriptor. * * @param desc the field descriptor. - * @return the resolved {@link FieldConstraints}. + * @return the resolved {@link FieldRules}. */ - FieldConstraints resolveFieldConstraints(FieldDescriptor desc) + FieldRules resolveFieldRules(FieldDescriptor desc) throws InvalidProtocolBufferException, CompilationException { DescriptorProtos.FieldOptions options = desc.getOptions(); // If the protovalidate field option is unknown, reparse using extension registry. @@ -112,19 +112,19 @@ FieldConstraints resolveFieldConstraints(FieldDescriptor desc) options = DescriptorProtos.FieldOptions.parseFrom(options.toByteString(), EXTENSION_REGISTRY); } if (!options.hasExtension(ValidateProto.field)) { - return FieldConstraints.getDefaultInstance(); + return FieldRules.getDefaultInstance(); } // Don't use getExtension here to avoid exception if descriptor types don't match. // This can occur if the extension is generated to a different Java package. Object value = options.getField(ValidateProto.field.getDescriptor()); - if (value instanceof FieldConstraints) { - return ((FieldConstraints) value); + if (value instanceof FieldRules) { + return ((FieldRules) value); } if (value instanceof MessageLite) { - // Possible that this represents the same constraint type, just generated to a different + // Possible that this represents the same rule type, just generated to a different // java_package. - return FieldConstraints.parseFrom(((MessageLite) value).toByteString()); + return FieldRules.parseFrom(((MessageLite) value).toByteString()); } - throw new CompilationException("unexpected field constraint option type: " + value); + throw new CompilationException("unexpected field rule option type: " + value); } } diff --git a/src/main/java/build/buf/protovalidate/ConstraintViolation.java b/src/main/java/build/buf/protovalidate/RuleViolation.java similarity index 89% rename from src/main/java/build/buf/protovalidate/ConstraintViolation.java rename to src/main/java/build/buf/protovalidate/RuleViolation.java index c141d1ff..67be135b 100644 --- a/src/main/java/build/buf/protovalidate/ConstraintViolation.java +++ b/src/main/java/build/buf/protovalidate/RuleViolation.java @@ -26,10 +26,10 @@ import org.jspecify.annotations.Nullable; /** - * {@link ConstraintViolation} contains all of the collected information about an individual - * constraint violation. + * {@link RuleViolation} contains all of the collected information about an individual rule + * violation. */ -class ConstraintViolation implements Violation { +class RuleViolation implements Violation { /** Static value to return when there are no violations. */ public static final List NO_VIOLATIONS = new ArrayList<>(); @@ -77,7 +77,7 @@ public Descriptors.FieldDescriptor getDescriptor() { /** Builds a Violation instance. */ public static class Builder { - private @Nullable String constraintId; + private @Nullable String ruleId; private @Nullable String message; private boolean forKey = false; private final Deque fieldPath = new ArrayDeque<>(); @@ -86,13 +86,13 @@ public static class Builder { private @Nullable FieldValue ruleValue; /** - * Sets the constraint ID field of the resulting violation. + * Sets the rule ID field of the resulting violation. * - * @param constraintId Constraint ID value to use. + * @param ruleId Rule ID value to use. * @return The builder. */ - public Builder setConstraintId(String constraintId) { - this.constraintId = constraintId; + public Builder setRuleId(String ruleId) { + this.ruleId = ruleId; return this; } @@ -192,10 +192,10 @@ public Builder setRuleValue(@Nullable FieldValue ruleValue) { * * @return A Violation instance. */ - public ConstraintViolation build() { + public RuleViolation build() { build.buf.validate.Violation.Builder protoBuilder = build.buf.validate.Violation.newBuilder(); - if (constraintId != null) { - protoBuilder.setConstraintId(constraintId); + if (ruleId != null) { + protoBuilder.setRuleId(ruleId); } if (message != null) { protoBuilder.setMessage(message); @@ -209,14 +209,14 @@ public ConstraintViolation build() { if (!rulePath.isEmpty()) { protoBuilder.setRule(FieldPath.newBuilder().addAllElements(rulePath)); } - return new ConstraintViolation(protoBuilder.build(), fieldValue, ruleValue); + return new RuleViolation(protoBuilder.build(), fieldValue, ruleValue); } private Builder() {} } /** - * Creates a new empty builder for building a {@link ConstraintViolation}. + * Creates a new empty builder for building a {@link RuleViolation}. * * @return A new, empty {@link Builder}. */ @@ -224,7 +224,7 @@ public static Builder newBuilder() { return new Builder(); } - private ConstraintViolation( + private RuleViolation( build.buf.validate.Violation proto, @Nullable FieldValue fieldValue, @Nullable FieldValue ruleValue) { @@ -234,7 +234,7 @@ private ConstraintViolation( } /** - * Gets the protobuf data that corresponds to this constraint violation. + * Gets the protobuf data that corresponds to this rule violation. * * @return The protobuf violation data. */ diff --git a/src/main/java/build/buf/protovalidate/ConstraintViolationHelper.java b/src/main/java/build/buf/protovalidate/RuleViolationHelper.java similarity index 91% rename from src/main/java/build/buf/protovalidate/ConstraintViolationHelper.java rename to src/main/java/build/buf/protovalidate/RuleViolationHelper.java index 05e7ae28..6425fda9 100644 --- a/src/main/java/build/buf/protovalidate/ConstraintViolationHelper.java +++ b/src/main/java/build/buf/protovalidate/RuleViolationHelper.java @@ -20,14 +20,14 @@ import java.util.List; import org.jspecify.annotations.Nullable; -class ConstraintViolationHelper { +class RuleViolationHelper { private static final List EMPTY_PREFIX = new ArrayList<>(); private final @Nullable FieldPath rulePrefix; private final @Nullable FieldPathElement fieldPathElement; - ConstraintViolationHelper(@Nullable ValueEvaluator evaluator) { + RuleViolationHelper(@Nullable ValueEvaluator evaluator) { if (evaluator != null) { this.rulePrefix = evaluator.getNestedRule(); if (evaluator.getDescriptor() != null) { @@ -41,7 +41,7 @@ class ConstraintViolationHelper { } } - ConstraintViolationHelper(@Nullable FieldPath rulePrefix) { + RuleViolationHelper(@Nullable FieldPath rulePrefix) { this.rulePrefix = rulePrefix; this.fieldPathElement = null; } diff --git a/src/main/java/build/buf/protovalidate/UnknownDescriptorEvaluator.java b/src/main/java/build/buf/protovalidate/UnknownDescriptorEvaluator.java index 380d457d..b8652d93 100644 --- a/src/main/java/build/buf/protovalidate/UnknownDescriptorEvaluator.java +++ b/src/main/java/build/buf/protovalidate/UnknownDescriptorEvaluator.java @@ -38,10 +38,9 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { return Collections.singletonList( - ConstraintViolation.newBuilder() - .setMessage("No evaluator available for " + desc.getFullName())); + RuleViolation.newBuilder().setMessage("No evaluator available for " + desc.getFullName())); } } diff --git a/src/main/java/build/buf/protovalidate/ValidationResult.java b/src/main/java/build/buf/protovalidate/ValidationResult.java index 39e793ce..eb060f44 100644 --- a/src/main/java/build/buf/protovalidate/ValidationResult.java +++ b/src/main/java/build/buf/protovalidate/ValidationResult.java @@ -20,9 +20,8 @@ import java.util.List; /** - * {@link ValidationResult} is returned when a constraint is executed. It contains a list of - * violations. This is non-fatal. If there are no violations, the constraint is considered to have - * passed. + * {@link ValidationResult} is returned when a rule is executed. It contains a list of violations. + * This is non-fatal. If there are no violations, the rule is considered to have passed. */ public class ValidationResult { @@ -78,7 +77,7 @@ public String toString() { } builder.append( String.format( - "%s [%s]", violation.toProto().getMessage(), violation.toProto().getConstraintId())); + "%s [%s]", violation.toProto().getMessage(), violation.toProto().getRuleId())); } return builder.toString(); } diff --git a/src/main/java/build/buf/protovalidate/Validator.java b/src/main/java/build/buf/protovalidate/Validator.java index cb575f00..bc6880dc 100644 --- a/src/main/java/build/buf/protovalidate/Validator.java +++ b/src/main/java/build/buf/protovalidate/Validator.java @@ -30,7 +30,7 @@ public class Validator { private final EvaluatorBuilder evaluatorBuilder; /** - * failFast indicates whether the validator should stop evaluating constraints after the first + * failFast indicates whether the validator should stop evaluating rules after the first * violation. */ private final boolean failFast; @@ -55,12 +55,12 @@ public Validator() { } /** - * Checks that message satisfies its constraints. Constraints are defined within the Protobuf file - * as options from the buf.validate package. A {@link ValidationResult} is returned which contains - * a list of violations. If the list is empty, the message is valid. If the list is non-empty, the - * message is invalid. An exception is thrown if the message cannot be validated because the - * evaluation logic for the message cannot be built ({@link CompilationException}), or there is a - * type error when attempting to evaluate a CEL expression associated with the message ({@link + * Checks that message satisfies its rules. Rules are defined within the Protobuf file as options + * from the buf.validate package. A {@link ValidationResult} is returned which contains a list of + * violations. If the list is empty, the message is valid. If the list is non-empty, the message + * is invalid. An exception is thrown if the message cannot be validated because the evaluation + * logic for the message cannot be built ({@link CompilationException}), or there is a type error + * when attempting to evaluate a CEL expression associated with the message ({@link * ExecutionException}). * * @param msg the {@link Message} to be validated. @@ -73,12 +73,12 @@ public ValidationResult validate(Message msg) throws ValidationException { } Descriptor descriptor = msg.getDescriptorForType(); Evaluator evaluator = evaluatorBuilder.load(descriptor); - List result = evaluator.evaluate(new MessageValue(msg), failFast); + List result = evaluator.evaluate(new MessageValue(msg), failFast); if (result.isEmpty()) { return ValidationResult.EMPTY; } List violations = new ArrayList<>(result.size()); - for (ConstraintViolation.Builder builder : result) { + for (RuleViolation.Builder builder : result) { violations.add(builder.build()); } return new ValidationResult(violations); diff --git a/src/main/java/build/buf/protovalidate/ValueEvaluator.java b/src/main/java/build/buf/protovalidate/ValueEvaluator.java index 44af2d91..a967073e 100644 --- a/src/main/java/build/buf/protovalidate/ValueEvaluator.java +++ b/src/main/java/build/buf/protovalidate/ValueEvaluator.java @@ -40,8 +40,8 @@ class ValueEvaluator implements Evaluator { private final List evaluators = new ArrayList<>(); /** - * Indicates that the Constraints should not be applied if the field is unset or the default - * (typically zero) value. + * Indicates that the Rules should not be applied if the field is unset or the default (typically + * zero) value. */ private boolean ignoreEmpty; @@ -69,21 +69,21 @@ public boolean tautology() { } @Override - public List evaluate(Value val, boolean failFast) + public List evaluate(Value val, boolean failFast) throws ExecutionException { if (this.shouldIgnore(val.value(Object.class))) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } - List allViolations = new ArrayList<>(); + List allViolations = new ArrayList<>(); for (Evaluator evaluator : evaluators) { - List violations = evaluator.evaluate(val, failFast); + List violations = evaluator.evaluate(val, failFast); if (failFast && !violations.isEmpty()) { return violations; } allViolations.addAll(violations); } if (allViolations.isEmpty()) { - return ConstraintViolation.NO_VIOLATIONS; + return RuleViolation.NO_VIOLATIONS; } return allViolations; } diff --git a/src/main/java/build/buf/protovalidate/Violation.java b/src/main/java/build/buf/protovalidate/Violation.java index ab91edaa..d9666c52 100644 --- a/src/main/java/build/buf/protovalidate/Violation.java +++ b/src/main/java/build/buf/protovalidate/Violation.java @@ -18,8 +18,7 @@ import org.jspecify.annotations.Nullable; /** - * {@link Violation} provides all of the collected information about an individual constraint - * violation. + * {@link Violation} provides all of the collected information about an individual rule violation. */ public interface Violation { /** {@link FieldValue} represents a Protobuf field value inside a Protobuf message. */ diff --git a/src/main/java/build/buf/protovalidate/exceptions/CompilationException.java b/src/main/java/build/buf/protovalidate/exceptions/CompilationException.java index 863b1889..3760e942 100644 --- a/src/main/java/build/buf/protovalidate/exceptions/CompilationException.java +++ b/src/main/java/build/buf/protovalidate/exceptions/CompilationException.java @@ -14,7 +14,7 @@ package build.buf.protovalidate.exceptions; -/** CompilationException is returned when a constraint fails to compile. This is a fatal error. */ +/** CompilationException is returned when a rule fails to compile. This is a fatal error. */ public class CompilationException extends ValidationException { /** * Creates a CompilationException with the specified message. diff --git a/src/main/java/build/buf/protovalidate/exceptions/ExecutionException.java b/src/main/java/build/buf/protovalidate/exceptions/ExecutionException.java index 6342029a..6cd6e856 100644 --- a/src/main/java/build/buf/protovalidate/exceptions/ExecutionException.java +++ b/src/main/java/build/buf/protovalidate/exceptions/ExecutionException.java @@ -14,7 +14,7 @@ package build.buf.protovalidate.exceptions; -/** ExecutionException is returned when a constraint fails to execute. This is a fatal error. */ +/** ExecutionException is returned when a rule fails to execute. This is a fatal error. */ public class ExecutionException extends ValidationException { /** * Creates an ExecutionException with the specified message. diff --git a/src/main/resources/buf/validate/validate.proto b/src/main/resources/buf/validate/validate.proto index 7d324160..84bd36b8 100644 --- a/src/main/resources/buf/validate/validate.proto +++ b/src/main/resources/buf/validate/validate.proto @@ -32,7 +32,7 @@ option java_package = "build.buf.validate"; extend google.protobuf.MessageOptions { // Rules specify the validations to be performed on this message. By default, // no validation is performed against a message. - optional MessageConstraints message = 1159; + optional MessageRules message = 1159; } // OneofOptions is an extension to google.protobuf.OneofOptions. It allows @@ -42,7 +42,7 @@ extend google.protobuf.MessageOptions { extend google.protobuf.OneofOptions { // Rules specify the validations to be performed on this oneof. By default, // no validation is performed against a oneof. - optional OneofConstraints oneof = 1159; + optional OneofRules oneof = 1159; } // FieldOptions is an extension to google.protobuf.FieldOptions. It allows @@ -52,9 +52,9 @@ extend google.protobuf.OneofOptions { extend google.protobuf.FieldOptions { // Rules specify the validations to be performed on this field. By default, // no validation is performed against a field. - optional FieldConstraints field = 1159; + optional FieldRules field = 1159; - // Specifies predefined rules. When extending a standard constraint message, + // Specifies predefined rules. When extending a standard rule message, // this adds additional CEL expressions that apply when the extension is used. // // ```proto @@ -70,11 +70,11 @@ extend google.protobuf.FieldOptions { // int32 reserved = 1 [(buf.validate.field).int32.(is_zero) = true]; // } // ``` - optional PredefinedConstraints predefined = 1160; + optional PredefinedRules predefined = 1160; } -// `Constraint` represents a validation rule written in the Common Expression -// Language (CEL) syntax. Each Constraint includes a unique identifier, an +// `Rule` represents a validation rule written in the Common Expression +// Language (CEL) syntax. Each Rule includes a unique identifier, an // optional error message, and the CEL expression to evaluate. For more // information on CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md). // @@ -88,13 +88,13 @@ extend google.protobuf.FieldOptions { // int32 bar = 1; // } // ``` -message Constraint { - // `id` is a string that serves as a machine-readable name for this Constraint. +message Rule { + // `id` is a string that serves as a machine-readable name for this Rule. // It should be unique within its scope, which could be either a message or a field. optional string id = 1; // `message` is an optional field that provides a human-readable error message - // for this Constraint when the CEL expression evaluates to false. If a + // for this Rule when the CEL expression evaluates to false. If a // non-empty message is provided, any strings resulting from the CEL // expression evaluation are ignored. optional string message = 2; @@ -106,9 +106,9 @@ message Constraint { optional string expression = 3; } -// MessageConstraints represents validation rules that are applied to the entire message. -// It includes disabling options and a list of Constraint messages representing Common Expression Language (CEL) validation rules. -message MessageConstraints { +// MessageRules represents validation rules that are applied to the entire message. +// It includes disabling options and a list of Rule messages representing Common Expression Language (CEL) validation rules. +message MessageRules { // `disabled` is a boolean flag that, when set to true, nullifies any validation rules for this message. // This includes any fields within the message that would otherwise support validation. // @@ -120,8 +120,8 @@ message MessageConstraints { // ``` optional bool disabled = 1; - // `cel` is a repeated field of type Constraint. Each Constraint specifies a validation rule to be applied to this message. - // These constraints are written in Common Expression Language (CEL) syntax. For more information on + // `cel` is a repeated field of type Rule. Each Rule specifies a validation rule to be applied to this message. + // These rules are written in Common Expression Language (CEL) syntax. For more information on // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md). // // @@ -136,15 +136,15 @@ message MessageConstraints { // optional int32 foo = 1; // } // ``` - repeated Constraint cel = 3; + repeated Rule cel = 3; } -// The `OneofConstraints` message type enables you to manage constraints for +// The `OneofRules` message type enables you to manage rules for // oneof fields in your protobuf messages. -message OneofConstraints { +message OneofRules { // If `required` is true, exactly one field of the oneof must be present. A // validation error is returned if no fields in the oneof are present. The - // field itself may still be a default value; further constraints + // field itself may still be a default value; further rules // should be placed on the fields themselves to ensure they are valid values, // such as `min_len` or `gt`. // @@ -162,9 +162,9 @@ message OneofConstraints { optional bool required = 1; } -// FieldConstraints encapsulates the rules for each type of field. Depending on +// FieldRules encapsulates the rules for each type of field. Depending on // the field, the correct set should be used to ensure proper validations. -message FieldConstraints { +message FieldRules { // `cel` is a repeated field used to represent a textual expression // in the Common Expression Language (CEL) syntax. For more information on // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md). @@ -179,7 +179,7 @@ message FieldConstraints { // }]; // } // ``` - repeated Constraint cel = 23; + repeated Rule cel = 23; // If `required` is true, the field must be populated. A populated field can be // described as "serialized in the wire format," which includes: // @@ -246,9 +246,9 @@ message FieldConstraints { reserved "skipped", "ignore_empty"; } -// PredefinedConstraints are custom constraints that can be re-used with +// PredefinedRules are custom rules that can be re-used with // multiple fields. -message PredefinedConstraints { +message PredefinedRules { // `cel` is a repeated field used to represent a textual expression // in the Common Expression Language (CEL) syntax. For more information on // CEL, [see our documentation](https://github.com/bufbuild/protovalidate/blob/main/docs/cel.md). @@ -263,7 +263,7 @@ message PredefinedConstraints { // }]; // } // ``` - repeated Constraint cel = 1; + repeated Rule cel = 1; reserved 24, 26; reserved @@ -272,8 +272,8 @@ message PredefinedConstraints { ; } -// Specifies how FieldConstraints.ignore behaves. See the documentation for -// FieldConstraints.required for definitions of "populated" and "nullable". +// Specifies how FieldRules.ignore behaves. See the documentation for +// FieldRules.required for definitions of "populated" and "nullable". enum Ignore { // Validation is only skipped if it's an unpopulated nullable fields. // @@ -405,7 +405,7 @@ enum Ignore { // The validation rules of this field will be skipped and not evaluated. This // is useful for situations that necessitate turning off the rules of a field // containing a message that may not make sense in the current context, or to - // temporarily disable constraints during development. + // temporarily disable rules during development. // // ```proto // message MyMessage { @@ -423,7 +423,7 @@ enum Ignore { ; } -// FloatRules describes the constraints applied to `float` values. These +// FloatRules describes the rules applied to `float` values. These // rules may also be applied to the `google.protobuf.FloatValue` Well-Known-Type. message FloatRules { // `const` requires the field value to exactly match the specified value. If @@ -437,7 +437,7 @@ message FloatRules { // ``` optional float const = 1 [(predefined).cel = { id: "float.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { @@ -592,7 +592,7 @@ message FloatRules { // ``` repeated float in = 6 [(predefined).cel = { id: "float.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `in` requires the field value to not be equal to any of the specified @@ -618,7 +618,7 @@ message FloatRules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -635,8 +635,8 @@ message FloatRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -647,7 +647,7 @@ message FloatRules { extensions 1000 to max; } -// DoubleRules describes the constraints applied to `double` values. These +// DoubleRules describes the rules applied to `double` values. These // rules may also be applied to the `google.protobuf.DoubleValue` Well-Known-Type. message DoubleRules { // `const` requires the field value to exactly match the specified value. If @@ -661,7 +661,7 @@ message DoubleRules { // ``` optional double const = 1 [(predefined).cel = { id: "double.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -813,7 +813,7 @@ message DoubleRules { // ``` repeated double in = 6 [(predefined).cel = { id: "double.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -839,7 +839,7 @@ message DoubleRules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -856,8 +856,8 @@ message DoubleRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -868,7 +868,7 @@ message DoubleRules { extensions 1000 to max; } -// Int32Rules describes the constraints applied to `int32` values. These +// Int32Rules describes the rules applied to `int32` values. These // rules may also be applied to the `google.protobuf.Int32Value` Well-Known-Type. message Int32Rules { // `const` requires the field value to exactly match the specified value. If @@ -882,7 +882,7 @@ message Int32Rules { // ``` optional int32 const = 1 [(predefined).cel = { id: "int32.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field @@ -1035,7 +1035,7 @@ message Int32Rules { // ``` repeated int32 in = 6 [(predefined).cel = { id: "int32.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -1054,7 +1054,7 @@ message Int32Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -1071,8 +1071,8 @@ message Int32Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -1083,7 +1083,7 @@ message Int32Rules { extensions 1000 to max; } -// Int64Rules describes the constraints applied to `int64` values. These +// Int64Rules describes the rules applied to `int64` values. These // rules may also be applied to the `google.protobuf.Int64Value` Well-Known-Type. message Int64Rules { // `const` requires the field value to exactly match the specified value. If @@ -1097,7 +1097,7 @@ message Int64Rules { // ``` optional int64 const = 1 [(predefined).cel = { id: "int64.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -1250,7 +1250,7 @@ message Int64Rules { // ``` repeated int64 in = 6 [(predefined).cel = { id: "int64.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -1269,7 +1269,7 @@ message Int64Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -1286,8 +1286,8 @@ message Int64Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -1298,7 +1298,7 @@ message Int64Rules { extensions 1000 to max; } -// UInt32Rules describes the constraints applied to `uint32` values. These +// UInt32Rules describes the rules applied to `uint32` values. These // rules may also be applied to the `google.protobuf.UInt32Value` Well-Known-Type. message UInt32Rules { // `const` requires the field value to exactly match the specified value. If @@ -1312,7 +1312,7 @@ message UInt32Rules { // ``` optional uint32 const = 1 [(predefined).cel = { id: "uint32.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -1465,7 +1465,7 @@ message UInt32Rules { // ``` repeated uint32 in = 6 [(predefined).cel = { id: "uint32.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -1484,7 +1484,7 @@ message UInt32Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -1501,8 +1501,8 @@ message UInt32Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -1513,7 +1513,7 @@ message UInt32Rules { extensions 1000 to max; } -// UInt64Rules describes the constraints applied to `uint64` values. These +// UInt64Rules describes the rules applied to `uint64` values. These // rules may also be applied to the `google.protobuf.UInt64Value` Well-Known-Type. message UInt64Rules { // `const` requires the field value to exactly match the specified value. If @@ -1527,7 +1527,7 @@ message UInt64Rules { // ``` optional uint64 const = 1 [(predefined).cel = { id: "uint64.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -1679,7 +1679,7 @@ message UInt64Rules { // ``` repeated uint64 in = 6 [(predefined).cel = { id: "uint64.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -1698,7 +1698,7 @@ message UInt64Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -1715,8 +1715,8 @@ message UInt64Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -1727,7 +1727,7 @@ message UInt64Rules { extensions 1000 to max; } -// SInt32Rules describes the constraints applied to `sint32` values. +// SInt32Rules describes the rules applied to `sint32` values. message SInt32Rules { // `const` requires the field value to exactly match the specified value. If // the field value doesn't match, an error message is generated. @@ -1740,7 +1740,7 @@ message SInt32Rules { // ``` optional sint32 const = 1 [(predefined).cel = { id: "sint32.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field @@ -1893,7 +1893,7 @@ message SInt32Rules { // ``` repeated sint32 in = 6 [(predefined).cel = { id: "sint32.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -1912,7 +1912,7 @@ message SInt32Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -1929,8 +1929,8 @@ message SInt32Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -1941,7 +1941,7 @@ message SInt32Rules { extensions 1000 to max; } -// SInt64Rules describes the constraints applied to `sint64` values. +// SInt64Rules describes the rules applied to `sint64` values. message SInt64Rules { // `const` requires the field value to exactly match the specified value. If // the field value doesn't match, an error message is generated. @@ -1954,7 +1954,7 @@ message SInt64Rules { // ``` optional sint64 const = 1 [(predefined).cel = { id: "sint64.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field @@ -2107,7 +2107,7 @@ message SInt64Rules { // ``` repeated sint64 in = 6 [(predefined).cel = { id: "sint64.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -2126,7 +2126,7 @@ message SInt64Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -2143,8 +2143,8 @@ message SInt64Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -2155,7 +2155,7 @@ message SInt64Rules { extensions 1000 to max; } -// Fixed32Rules describes the constraints applied to `fixed32` values. +// Fixed32Rules describes the rules applied to `fixed32` values. message Fixed32Rules { // `const` requires the field value to exactly match the specified value. // If the field value doesn't match, an error message is generated. @@ -2168,7 +2168,7 @@ message Fixed32Rules { // ``` optional fixed32 const = 1 [(predefined).cel = { id: "fixed32.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -2321,7 +2321,7 @@ message Fixed32Rules { // ``` repeated fixed32 in = 6 [(predefined).cel = { id: "fixed32.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -2340,7 +2340,7 @@ message Fixed32Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -2357,8 +2357,8 @@ message Fixed32Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -2369,7 +2369,7 @@ message Fixed32Rules { extensions 1000 to max; } -// Fixed64Rules describes the constraints applied to `fixed64` values. +// Fixed64Rules describes the rules applied to `fixed64` values. message Fixed64Rules { // `const` requires the field value to exactly match the specified value. If // the field value doesn't match, an error message is generated. @@ -2382,7 +2382,7 @@ message Fixed64Rules { // ``` optional fixed64 const = 1 [(predefined).cel = { id: "fixed64.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -2535,7 +2535,7 @@ message Fixed64Rules { // ``` repeated fixed64 in = 6 [(predefined).cel = { id: "fixed64.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -2554,7 +2554,7 @@ message Fixed64Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -2571,8 +2571,8 @@ message Fixed64Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -2583,7 +2583,7 @@ message Fixed64Rules { extensions 1000 to max; } -// SFixed32Rules describes the constraints applied to `fixed32` values. +// SFixed32Rules describes the rules applied to `fixed32` values. message SFixed32Rules { // `const` requires the field value to exactly match the specified value. If // the field value doesn't match, an error message is generated. @@ -2596,7 +2596,7 @@ message SFixed32Rules { // ``` optional sfixed32 const = 1 [(predefined).cel = { id: "sfixed32.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -2749,7 +2749,7 @@ message SFixed32Rules { // ``` repeated sfixed32 in = 6 [(predefined).cel = { id: "sfixed32.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -2768,7 +2768,7 @@ message SFixed32Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -2785,8 +2785,8 @@ message SFixed32Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -2797,7 +2797,7 @@ message SFixed32Rules { extensions 1000 to max; } -// SFixed64Rules describes the constraints applied to `fixed64` values. +// SFixed64Rules describes the rules applied to `fixed64` values. message SFixed64Rules { // `const` requires the field value to exactly match the specified value. If // the field value doesn't match, an error message is generated. @@ -2810,7 +2810,7 @@ message SFixed64Rules { // ``` optional sfixed64 const = 1 [(predefined).cel = { id: "sfixed64.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` requires the field value to be less than the specified value (field < @@ -2963,7 +2963,7 @@ message SFixed64Rules { // ``` repeated sfixed64 in = 6 [(predefined).cel = { id: "sfixed64.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to not be equal to any of the specified @@ -2982,7 +2982,7 @@ message SFixed64Rules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -2999,8 +2999,8 @@ message SFixed64Rules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -3011,7 +3011,7 @@ message SFixed64Rules { extensions 1000 to max; } -// BoolRules describes the constraints applied to `bool` values. These rules +// BoolRules describes the rules applied to `bool` values. These rules // may also be applied to the `google.protobuf.BoolValue` Well-Known-Type. message BoolRules { // `const` requires the field value to exactly match the specified boolean value. @@ -3025,11 +3025,11 @@ message BoolRules { // ``` optional bool const = 1 [(predefined).cel = { id: "bool.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -3046,8 +3046,8 @@ message BoolRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -3058,7 +3058,7 @@ message BoolRules { extensions 1000 to max; } -// StringRules describes the constraints applied to `string` values These +// StringRules describes the rules applied to `string` values These // rules may also be applied to the `google.protobuf.StringValue` Well-Known-Type. message StringRules { // `const` requires the field value to exactly match the specified value. If @@ -3072,7 +3072,7 @@ message StringRules { // ``` optional string const = 1 [(predefined).cel = { id: "string.const" - expression: "this != rules.const ? 'value must equal `%s`'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal `%s`'.format([getField(rules, 'const')]) : ''" }]; // `len` dictates that the field value must have the specified @@ -3258,7 +3258,7 @@ message StringRules { // ``` repeated string in = 10 [(predefined).cel = { id: "string.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` specifies that the field value cannot be equal to any @@ -3275,11 +3275,17 @@ message StringRules { expression: "this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''" }]; - // `WellKnown` rules provide advanced constraints against common string - // patterns + // `WellKnown` rules provide advanced rules against common string + // patterns. oneof well_known { - // `email` specifies that the field value must be a valid email address - // (addr-spec only) as defined by [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1). + // `email` specifies that the field value must be a valid email address, for + // example "foo@example.com". + // + // Conforms to the definition for a valid email address from the [HTML standard](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address). + // Note that this standard willfully deviates from [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322), + // which allows many unexpected forms of email addresses and will easily match + // a typographical error. + // // If the field value isn't a valid email address, an error message will be generated. // // ```proto @@ -3301,10 +3307,18 @@ message StringRules { } ]; - // `hostname` specifies that the field value must be a valid - // hostname as defined by [RFC 1034](https://datatracker.ietf.org/doc/html/rfc1034#section-3.5). This constraint doesn't support - // internationalized domain names (IDNs). If the field value isn't a - // valid hostname, an error message will be generated. + // `hostname` specifies that the field value must be a valid hostname, for + // example "foo.example.com". + // + // A valid hostname follows the rules below: + // - The name consists of one or more labels, separated by a dot ("."). + // - Each label can be 1 to 63 alphanumeric characters. + // - A label can contain hyphens ("-"), but must not start or end with a hyphen. + // - The right-most label must not be digits only. + // - The name can have a trailing dot—for example, "foo.example.com.". + // - The name can be 253 characters at most, excluding the optional trailing dot. + // + // If the field value isn't a valid hostname, an error message will be generated. // // ```proto // message MyString { @@ -3325,8 +3339,15 @@ message StringRules { } ]; - // `ip` specifies that the field value must be a valid IP - // (v4 or v6) address, without surrounding square brackets for IPv6 addresses. + // `ip` specifies that the field value must be a valid IP (v4 or v6) address. + // + // IPv4 addresses are expected in the dotted decimal format—for example, "192.168.5.21". + // IPv6 addresses are expected in their text representation—for example, "::1", + // or "2001:0DB8:ABCD:0012::0". + // + // Both formats are well-defined in the internet standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). + // Zone identifiers for IPv6 addresses (for example, "fe80::a%en1") are supported. + // // If the field value isn't a valid IP address, an error message will be // generated. // @@ -3349,9 +3370,9 @@ message StringRules { } ]; - // `ipv4` specifies that the field value must be a valid IPv4 - // address. If the field value isn't a valid IPv4 address, an error message - // will be generated. + // `ipv4` specifies that the field value must be a valid IPv4 address—for + // example "192.168.5.21". If the field value isn't a valid IPv4 address, an + // error message will be generated. // // ```proto // message MyString { @@ -3372,9 +3393,9 @@ message StringRules { } ]; - // `ipv6` specifies that the field value must be a valid - // IPv6 address, without surrounding square brackets. If the field value is - // not a valid IPv6 address, an error message will be generated. + // `ipv6` specifies that the field value must be a valid IPv6 address—for + // example "::1", or "d7a:115c:a1e0:ab12:4843:cd96:626b:430b". If the field + // value is not a valid IPv6 address, an error message will be generated. // // ```proto // message MyString { @@ -3395,8 +3416,11 @@ message StringRules { } ]; - // `uri` specifies that the field value must be a valid URI as defined by - // [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3). + // `uri` specifies that the field value must be a valid URI, for example + // "https://example.com/foo/bar?baz=quux#frag". + // + // URI is defined in the internet standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). + // Zone Identifiers in IPv6 address literals are supported ([RFC 6874](https://datatracker.ietf.org/doc/html/rfc6874)). // // If the field value isn't a valid URI, an error message will be generated. // @@ -3419,11 +3443,13 @@ message StringRules { } ]; - // `uri_ref` specifies that the field value must be a valid URI Reference as - // defined by [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1). + // `uri_ref` specifies that the field value must be a valid URI Reference—either + // a URI such as "https://example.com/foo/bar?baz=quux#frag", or a Relative + // Reference such as "./foo/bar?query". // - // A URI Reference is either a [URI](https://datatracker.ietf.org/doc/html/rfc3986#section-3), - // or a [Relative Reference](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2). + // URI, URI Reference, and Relative Reference are defined in the internet + // standard [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986). Zone + // Identifiers in IPv6 address literals are supported ([RFC 6874](https://datatracker.ietf.org/doc/html/rfc6874)). // // If the field value isn't a valid URI Reference, an error message will be // generated. @@ -3441,10 +3467,9 @@ message StringRules { }]; // `address` specifies that the field value must be either a valid hostname - // as defined by [RFC 1034](https://datatracker.ietf.org/doc/html/rfc1034#section-3.5) - // (which doesn't support internationalized domain names or IDNs) or a valid - // IP (v4 or v6). If the field value isn't a valid hostname or IP, an error - // message will be generated. + // (for example, "example.com"), or a valid IP (v4 or v6) address (for example, + // "192.168.0.1", or "::1"). If the field value isn't a valid hostname or IP, + // an error message will be generated. // // ```proto // message MyString { @@ -3512,10 +3537,10 @@ message StringRules { } ]; - // `ip_with_prefixlen` specifies that the field value must be a valid IP (v4 or v6) - // address with prefix length. If the field value isn't a valid IP with prefix - // length, an error message will be generated. - // + // `ip_with_prefixlen` specifies that the field value must be a valid IP + // (v4 or v6) address with prefix length—for example, "192.168.5.21/16" or + // "2001:0DB8:ABCD:0012::F1/64". If the field value isn't a valid IP with + // prefix length, an error message will be generated. // // ```proto // message MyString { @@ -3537,9 +3562,9 @@ message StringRules { ]; // `ipv4_with_prefixlen` specifies that the field value must be a valid - // IPv4 address with prefix. - // If the field value isn't a valid IPv4 address with prefix length, - // an error message will be generated. + // IPv4 address with prefix length—for example, "192.168.5.21/16". If the + // field value isn't a valid IPv4 address with prefix length, an error + // message will be generated. // // ```proto // message MyString { @@ -3561,7 +3586,7 @@ message StringRules { ]; // `ipv6_with_prefixlen` specifies that the field value must be a valid - // IPv6 address with prefix length. + // IPv6 address with prefix length—for example, "2001:0DB8:ABCD:0012::F1/64". // If the field value is not a valid IPv6 address with prefix length, // an error message will be generated. // @@ -3584,10 +3609,15 @@ message StringRules { } ]; - // `ip_prefix` specifies that the field value must be a valid IP (v4 or v6) prefix. + // `ip_prefix` specifies that the field value must be a valid IP (v4 or v6) + // prefix—for example, "192.168.0.0/16" or "2001:0DB8:ABCD:0012::0/64". + // + // The prefix must have all zeros for the unmasked bits. For example, + // "2001:0DB8:ABCD:0012::0/64" designates the left-most 64 bits for the + // prefix, and the remaining 64 bits must be zero. + // // If the field value isn't a valid IP prefix, an error message will be - // generated. The prefix must have all zeros for the masked bits of the prefix (e.g., - // `127.0.0.0/16`, not `127.0.0.1/16`). + // generated. // // ```proto // message MyString { @@ -3609,9 +3639,14 @@ message StringRules { ]; // `ipv4_prefix` specifies that the field value must be a valid IPv4 - // prefix. If the field value isn't a valid IPv4 prefix, an error message - // will be generated. The prefix must have all zeros for the masked bits of - // the prefix (e.g., `127.0.0.0/16`, not `127.0.0.1/16`). + // prefix, for example "192.168.0.0/16". + // + // The prefix must have all zeros for the unmasked bits. For example, + // "192.168.0.0/16" designates the left-most 16 bits for the prefix, + // and the remaining 16 bits must be zero. + // + // If the field value isn't a valid IPv4 prefix, an error message + // will be generated. // // ```proto // message MyString { @@ -3632,10 +3667,15 @@ message StringRules { } ]; - // `ipv6_prefix` specifies that the field value must be a valid IPv6 prefix. + // `ipv6_prefix` specifies that the field value must be a valid IPv6 prefix—for + // example, "2001:0DB8:ABCD:0012::0/64". + // + // The prefix must have all zeros for the unmasked bits. For example, + // "2001:0DB8:ABCD:0012::0/64" designates the left-most 64 bits for the + // prefix, and the remaining 64 bits must be zero. + // // If the field value is not a valid IPv6 prefix, an error message will be - // generated. The prefix must have all zeros for the masked bits of the prefix - // (e.g., `2001:db8::/48`, not `2001:db8::1/48`). + // generated. // // ```proto // message MyString { @@ -3656,10 +3696,16 @@ message StringRules { } ]; - // `host_and_port` specifies the field value must be a valid host and port - // pair. The host must be a valid hostname or IP address while the port - // must be in the range of 0-65535, inclusive. IPv6 addresses must be delimited - // with square brackets (e.g., `[::1]:1234`). + // `host_and_port` specifies that the field value must be valid host/port + // pair—for example, "example.com:8080". + // + // The host can be one of: + //- An IPv4 address in dotted decimal format—for example, "192.168.5.21". + //- An IPv6 address enclosed in square brackets—for example, "[2001:0DB8:ABCD:0012::F1]". + //- A hostname—for example, "example.com". + // + // The port is separated by a colon. It must be non-empty, with a decimal number + // in the range of 0-65535, inclusive. bool host_and_port = 32 [ (predefined).cel = { id: "string.host_and_port" @@ -3733,7 +3779,7 @@ message StringRules { optional bool strict = 25; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -3750,8 +3796,8 @@ message StringRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -3773,7 +3819,7 @@ enum KnownRegex { KNOWN_REGEX_HTTP_HEADER_VALUE = 2; } -// BytesRules describe the constraints applied to `bytes` values. These rules +// BytesRules describe the rules applied to `bytes` values. These rules // may also be applied to the `google.protobuf.BytesValue` Well-Known-Type. message BytesRules { // `const` requires the field value to exactly match the specified bytes @@ -3787,7 +3833,7 @@ message BytesRules { // ``` optional bytes const = 1 [(predefined).cel = { id: "bytes.const" - expression: "this != rules.const ? 'value must be %x'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must be %x'.format([getField(rules, 'const')]) : ''" }]; // `len` requires the field value to have the specified length in bytes. @@ -3908,7 +3954,7 @@ message BytesRules { // ``` repeated bytes in = 8 [(predefined).cel = { id: "bytes.in" - expression: "dyn(rules)['in'].size() > 0 && !(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "getField(rules, 'in').size() > 0 && !(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to be not equal to any of the specified @@ -3927,11 +3973,11 @@ message BytesRules { expression: "this in rules.not_in ? 'value must not be in list %s'.format([rules.not_in]) : ''" }]; - // WellKnown rules provide advanced constraints against common byte + // WellKnown rules provide advanced rules against common byte // patterns oneof well_known { // `ip` ensures that the field `value` is a valid IP address (v4 or v6) in byte format. - // If the field value doesn't meet this constraint, an error message is generated. + // If the field value doesn't meet this rule, an error message is generated. // // ```proto // message MyBytes { @@ -3953,7 +3999,7 @@ message BytesRules { ]; // `ipv4` ensures that the field `value` is a valid IPv4 address in byte format. - // If the field value doesn't meet this constraint, an error message is generated. + // If the field value doesn't meet this rule, an error message is generated. // // ```proto // message MyBytes { @@ -3975,7 +4021,7 @@ message BytesRules { ]; // `ipv6` ensures that the field `value` is a valid IPv6 address in byte format. - // If the field value doesn't meet this constraint, an error message is generated. + // If the field value doesn't meet this rule, an error message is generated. // ```proto // message MyBytes { // // value must be a valid IPv6 address @@ -3997,7 +4043,7 @@ message BytesRules { } // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -4014,8 +4060,8 @@ message BytesRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4026,7 +4072,7 @@ message BytesRules { extensions 1000 to max; } -// EnumRules describe the constraints applied to `enum` values. +// EnumRules describe the rules applied to `enum` values. message EnumRules { // `const` requires the field value to exactly match the specified enum value. // If the field value doesn't match, an error message is generated. @@ -4045,7 +4091,7 @@ message EnumRules { // ``` optional int32 const = 1 [(predefined).cel = { id: "enum.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; // `defined_only` requires the field value to be one of the defined values for @@ -4083,7 +4129,7 @@ message EnumRules { // ``` repeated int32 in = 3 [(predefined).cel = { id: "enum.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` requires the field value to be not equal to any of the @@ -4108,7 +4154,7 @@ message EnumRules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -4129,8 +4175,8 @@ message EnumRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4141,7 +4187,7 @@ message EnumRules { extensions 1000 to max; } -// RepeatedRules describe the constraints applied to `repeated` values. +// RepeatedRules describe the rules applied to `repeated` values. message RepeatedRules { // `min_items` requires that this field must contain at least the specified // minimum number of items. @@ -4176,7 +4222,7 @@ message RepeatedRules { }]; // `unique` indicates that all elements in this field must - // be unique. This constraint is strictly applicable to scalar and enum + // be unique. This rule is strictly applicable to scalar and enum // types, with message types not being supported. // // ```proto @@ -4191,13 +4237,13 @@ message RepeatedRules { expression: "!rules.unique || this.unique()" }]; - // `items` details the constraints to be applied to each item + // `items` details the rules to be applied to each item // in the field. Even for repeated message fields, validation is executed // against each item unless skip is explicitly specified. // // ```proto // message MyRepeated { - // // The items in the field `value` must follow the specified constraints. + // // The items in the field `value` must follow the specified rules. // repeated string value = 1 [(buf.validate.field).repeated.items = { // string: { // min_len: 3 @@ -4206,11 +4252,11 @@ message RepeatedRules { // }]; // } // ``` - optional FieldConstraints items = 4; + optional FieldRules items = 4; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4221,7 +4267,7 @@ message RepeatedRules { extensions 1000 to max; } -// MapRules describe the constraints applied to `map` values. +// MapRules describe the rules applied to `map` values. message MapRules { //Specifies the minimum number of key-value pairs allowed. If the field has // fewer key-value pairs than specified, an error message is generated. @@ -4251,11 +4297,11 @@ message MapRules { expression: "uint(this.size()) > rules.max_pairs ? 'map must be at most %d entries'.format([rules.max_pairs]) : ''" }]; - //Specifies the constraints to be applied to each key in the field. + //Specifies the rules to be applied to each key in the field. // // ```proto // message MyMap { - // // The keys in the field `value` must follow the specified constraints. + // // The keys in the field `value` must follow the specified rules. // map value = 1 [(buf.validate.field).map.keys = { // string: { // min_len: 3 @@ -4264,15 +4310,15 @@ message MapRules { // }]; // } // ``` - optional FieldConstraints keys = 4; + optional FieldRules keys = 4; - //Specifies the constraints to be applied to the value of each key in the + //Specifies the rules to be applied to the value of each key in the // field. Message values will still have their validations evaluated unless //skip is specified here. // // ```proto // message MyMap { - // // The values in the field `value` must follow the specified constraints. + // // The values in the field `value` must follow the specified rules. // map value = 1 [(buf.validate.field).map.values = { // string: { // min_len: 5 @@ -4281,11 +4327,11 @@ message MapRules { // }]; // } // ``` - optional FieldConstraints values = 5; + optional FieldRules values = 5; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4296,7 +4342,7 @@ message MapRules { extensions 1000 to max; } -// AnyRules describe constraints applied exclusively to the `google.protobuf.Any` well-known type. +// AnyRules describe rules applied exclusively to the `google.protobuf.Any` well-known type. message AnyRules { // `in` requires the field's `type_url` to be equal to one of the //specified values. If it doesn't match any of the specified values, an error @@ -4321,7 +4367,7 @@ message AnyRules { repeated string not_in = 3; } -// DurationRules describe the constraints applied exclusively to the `google.protobuf.Duration` well-known type. +// DurationRules describe the rules applied exclusively to the `google.protobuf.Duration` well-known type. message DurationRules { // `const` dictates that the field must match the specified value of the `google.protobuf.Duration` type exactly. // If the field's value deviates from the specified value, an error message @@ -4335,7 +4381,7 @@ message DurationRules { // ``` optional google.protobuf.Duration const = 2 [(predefined).cel = { id: "duration.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // `lt` stipulates that the field must be less than the specified value of the `google.protobuf.Duration` type, @@ -4488,7 +4534,7 @@ message DurationRules { // ``` repeated google.protobuf.Duration in = 7 [(predefined).cel = { id: "duration.in" - expression: "!(this in dyn(rules)['in']) ? 'value must be in list %s'.format([dyn(rules)['in']]) : ''" + expression: "!(this in getField(rules, 'in')) ? 'value must be in list %s'.format([getField(rules, 'in')]) : ''" }]; // `not_in` denotes that the field must not be equal to @@ -4508,7 +4554,7 @@ message DurationRules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -4525,8 +4571,8 @@ message DurationRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4537,7 +4583,7 @@ message DurationRules { extensions 1000 to max; } -// TimestampRules describe the constraints applied exclusively to the `google.protobuf.Timestamp` well-known type. +// TimestampRules describe the rules applied exclusively to the `google.protobuf.Timestamp` well-known type. message TimestampRules { // `const` dictates that this field, of the `google.protobuf.Timestamp` type, must exactly match the specified value. If the field value doesn't correspond to the specified timestamp, an error message will be generated. // @@ -4549,7 +4595,7 @@ message TimestampRules { // ``` optional google.protobuf.Timestamp const = 2 [(predefined).cel = { id: "timestamp.const" - expression: "this != rules.const ? 'value must equal %s'.format([rules.const]) : ''" + expression: "this != getField(rules, 'const') ? 'value must equal %s'.format([getField(rules, 'const')]) : ''" }]; oneof less_than { // requires the duration field value to be less than the specified value (field < value). If the field value doesn't meet the required conditions, an error message is generated. @@ -4726,7 +4772,7 @@ message TimestampRules { }]; // `example` specifies values that the field may have. These values SHOULD - // conform to other constraints. `example` values will not impact validation + // conform to other rules. `example` values will not impact validation // but may be used as helpful guidance on how to populate the given field. // // ```proto @@ -4744,8 +4790,8 @@ message TimestampRules { }]; // Extension fields in this range that have the (buf.validate.predefined) - // option set will be treated as predefined field constraints that can then be - // set on the field options of other fields to apply field constraints. + // option set will be treated as predefined field rules that can then be + // set on the field options of other fields to apply field rules. // Extension numbers 1000 to 99999 are reserved for extension numbers that are // defined in the [Protobuf Global Extension Registry][1]. Extension numbers // above this range are reserved for extension numbers that are not explicitly @@ -4757,7 +4803,7 @@ message TimestampRules { } // `Violations` is a collection of `Violation` messages. This message type is returned by -// protovalidate when a proto message fails to meet the requirements set by the `Constraint` validation rules. +// protovalidate when a proto message fails to meet the requirements set by the `Rule` validation rules. // Each individual violation is represented by a `Violation` message. message Violations { // `violations` is a repeated field that contains all the `Violation` messages corresponding to the violations detected. @@ -4765,14 +4811,14 @@ message Violations { } // `Violation` represents a single instance where a validation rule, expressed -// as a `Constraint`, was not met. It provides information about the field that -// caused the violation, the specific constraint that wasn't fulfilled, and a +// as a `Rule`, was not met. It provides information about the field that +// caused the violation, the specific rule that wasn't fulfilled, and a // human-readable error message. // // ```json // { // "fieldPath": "bar", -// "constraintId": "foo.bar", +// "ruleId": "foo.bar", // "message": "bar must be greater than 0" // } // ``` @@ -4798,9 +4844,9 @@ message Violation { // ``` optional FieldPath field = 5; - // `rule` is a machine-readable path that points to the specific constraint rule that failed validation. - // This will be a nested field starting from the FieldConstraints of the field that failed validation. - // For custom constraints, this will provide the path of the constraint, e.g. `cel[0]`. + // `rule` is a machine-readable path that points to the specific rule rule that failed validation. + // This will be a nested field starting from the FieldRules of the field that failed validation. + // For custom rules, this will provide the path of the rule, e.g. `cel[0]`. // // For example, consider the following message: // @@ -4808,7 +4854,7 @@ message Violation { // message Message { // bool a = 1 [(buf.validate.field).required = true]; // bool b = 2 [(buf.validate.field).cel = { - // id: "custom_constraint", + // id: "custom_rule", // expression: "!this ? 'b must be true': ''" // }] // } @@ -4828,12 +4874,12 @@ message Violation { // ``` optional FieldPath rule = 6; - // `constraint_id` is the unique identifier of the `Constraint` that was not fulfilled. - // This is the same `id` that was specified in the `Constraint` message, allowing easy tracing of which rule was violated. - optional string constraint_id = 2; + // `rule_id` is the unique identifier of the `Rule` that was not fulfilled. + // This is the same `id` that was specified in the `Rule` message, allowing easy tracing of which rule was violated. + optional string rule_id = 2; // `message` is a human-readable error message that describes the nature of the violation. - // This can be the default error message from the violated `Constraint`, or it can be a custom message that gives more context about the violation. + // This can be the default error message from the violated `Rule`, or it can be a custom message that gives more context about the violation. optional string message = 3; // `for_key` indicates whether the violation was caused by a map key, rather than a value. diff --git a/src/test/java/build/buf/protovalidate/ValidatorCelExpressionTest.java b/src/test/java/build/buf/protovalidate/ValidatorCelExpressionTest.java index e613476f..b709a680 100644 --- a/src/test/java/build/buf/protovalidate/ValidatorCelExpressionTest.java +++ b/src/test/java/build/buf/protovalidate/ValidatorCelExpressionTest.java @@ -16,16 +16,14 @@ import static org.assertj.core.api.Assertions.assertThat; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; +import build.buf.validate.FieldRules; import build.buf.validate.Violation; import com.example.imports.buf.validate.RepeatedRules; import java.util.Arrays; import org.junit.jupiter.api.Test; -/** - * This test verifies that custom (CEL-based) field and/or message constraints evaluate as expected. - */ +/** This test verifies that custom (CEL-based) field and/or message rules evaluate as expected. */ public class ValidatorCelExpressionTest { @Test @@ -68,12 +66,12 @@ public void testFieldExpressionRepeatedMessage() throws Exception { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.CEL_FIELD_NUMBER)) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.CEL_FIELD_NUMBER)) .toBuilder() .setIndex(0) .build())) - .setConstraintId("field_expression.repeated.message") + .setRuleId("field_expression.repeated.message") .setMessage("test message field_expression.repeated.message") .build(); @@ -130,19 +128,19 @@ public void testFieldExpressionRepeatedMessageItems() throws Exception { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.REPEATED_FIELD_NUMBER))) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.REPEATED_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( RepeatedRules.getDescriptor().findFieldByName("items"))) .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.CEL_FIELD_NUMBER)) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.CEL_FIELD_NUMBER)) .toBuilder() .setIndex(0) .build())) - .setConstraintId("field_expression.repeated.message.items") + .setRuleId("field_expression.repeated.message.items") .setMessage("test message field_expression.repeated.message.items") .build(); diff --git a/src/test/java/build/buf/protovalidate/ValidatorDifferentJavaPackagesTest.java b/src/test/java/build/buf/protovalidate/ValidatorDifferentJavaPackagesTest.java index 17940266..917f9c43 100644 --- a/src/test/java/build/buf/protovalidate/ValidatorDifferentJavaPackagesTest.java +++ b/src/test/java/build/buf/protovalidate/ValidatorDifferentJavaPackagesTest.java @@ -17,12 +17,12 @@ import static org.assertj.core.api.Assertions.assertThat; import build.buf.protovalidate.exceptions.ValidationException; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; import build.buf.validate.FieldPathElement; +import build.buf.validate.FieldRules; import build.buf.validate.Violation; import com.example.imports.buf.validate.StringRules; -import com.example.imports.validationtest.ExampleFieldConstraints; +import com.example.imports.validationtest.ExampleFieldRules; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import java.util.Collections; @@ -60,23 +60,23 @@ */ public class ValidatorDifferentJavaPackagesTest { @Test - public void testValidationFieldConstraints() throws Exception { + public void testValidationFieldRules() throws Exception { // Valid message - matches regex - com.example.imports.validationtest.ExampleFieldConstraints validMsgImports = - com.example.imports.validationtest.ExampleFieldConstraints.newBuilder() + com.example.imports.validationtest.ExampleFieldRules validMsgImports = + com.example.imports.validationtest.ExampleFieldRules.newBuilder() .setRegexStringField("abc123") .build(); expectNoViolations(validMsgImports); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleFieldConstraints validMsgNoImports = - com.example.noimports.validationtest.ExampleFieldConstraints.parseFrom( + com.example.noimports.validationtest.ExampleFieldRules validMsgNoImports = + com.example.noimports.validationtest.ExampleFieldRules.parseFrom( validMsgImports.toByteString()); expectNoViolations(validMsgNoImports); // 10 chars long - regex requires 1-9 chars - com.example.imports.validationtest.ExampleFieldConstraints invalidMsgImports = - com.example.imports.validationtest.ExampleFieldConstraints.newBuilder() + com.example.imports.validationtest.ExampleFieldRules invalidMsgImports = + com.example.imports.validationtest.ExampleFieldRules.newBuilder() .setRegexStringField("0123456789") .build(); Violation expectedViolation = @@ -85,96 +85,94 @@ public void testValidationFieldConstraints() throws Exception { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - com.example.imports.validationtest.ExampleFieldConstraints - .getDescriptor() + com.example.imports.validationtest.ExampleFieldRules.getDescriptor() .findFieldByNumber( - ExampleFieldConstraints.REGEX_STRING_FIELD_FIELD_NUMBER)))) + ExampleFieldRules.REGEX_STRING_FIELD_FIELD_NUMBER)))) .setRule( FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.STRING_FIELD_NUMBER))) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.STRING_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( StringRules.getDescriptor() .findFieldByNumber(StringRules.PATTERN_FIELD_NUMBER)))) - .setConstraintId("string.pattern") + .setRuleId("string.pattern") .setMessage("value does not match regex pattern `^[a-z0-9]{1,9}$`") .build(); expectViolation(invalidMsgImports, expectedViolation); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleFieldConstraints invalidMsgNoImports = - com.example.noimports.validationtest.ExampleFieldConstraints.newBuilder() + com.example.noimports.validationtest.ExampleFieldRules invalidMsgNoImports = + com.example.noimports.validationtest.ExampleFieldRules.newBuilder() .setRegexStringField("0123456789") .build(); expectViolation(invalidMsgNoImports, expectedViolation); } @Test - public void testValidationOneofConstraints() + public void testValidationOneofRules() throws ValidationException, InvalidProtocolBufferException { - // Valid message - matches oneof constraint - com.example.imports.validationtest.ExampleOneofConstraints validMsgImports = - com.example.imports.validationtest.ExampleOneofConstraints.newBuilder() + // Valid message - matches oneof rule + com.example.imports.validationtest.ExampleOneofRules validMsgImports = + com.example.imports.validationtest.ExampleOneofRules.newBuilder() .setEmail("foo@bar.com") .build(); expectNoViolations(validMsgImports); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleOneofConstraints validMsgNoImports = - com.example.noimports.validationtest.ExampleOneofConstraints.parseFrom( + com.example.noimports.validationtest.ExampleOneofRules validMsgNoImports = + com.example.noimports.validationtest.ExampleOneofRules.parseFrom( validMsgImports.toByteString()); expectNoViolations(validMsgNoImports); - com.example.imports.validationtest.ExampleOneofConstraints invalidMsgImports = - com.example.imports.validationtest.ExampleOneofConstraints.getDefaultInstance(); + com.example.imports.validationtest.ExampleOneofRules invalidMsgImports = + com.example.imports.validationtest.ExampleOneofRules.getDefaultInstance(); Violation expectedViolation = Violation.newBuilder() .setField( FieldPath.newBuilder() .addElements(FieldPathElement.newBuilder().setFieldName("contact_info"))) - .setConstraintId("required") + .setRuleId("required") .setMessage("exactly one field is required in oneof") .build(); expectViolation(invalidMsgImports, expectedViolation); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleOneofConstraints invalidMsgNoImports = - com.example.noimports.validationtest.ExampleOneofConstraints.parseFrom( + com.example.noimports.validationtest.ExampleOneofRules invalidMsgNoImports = + com.example.noimports.validationtest.ExampleOneofRules.parseFrom( invalidMsgImports.toByteString()); expectViolation(invalidMsgNoImports, expectedViolation); } @Test - public void testValidationMessageConstraintsDifferentJavaPackage() throws Exception { - com.example.imports.validationtest.ExampleMessageConstraints validMsg = - com.example.imports.validationtest.ExampleMessageConstraints.newBuilder() + public void testValidationMessageRulesDifferentJavaPackage() throws Exception { + com.example.imports.validationtest.ExampleMessageRules validMsg = + com.example.imports.validationtest.ExampleMessageRules.newBuilder() .setPrimaryEmail("foo@bar.com") .build(); expectNoViolations(validMsg); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleMessageConstraints validMsgNoImports = - com.example.noimports.validationtest.ExampleMessageConstraints.parseFrom( - validMsg.toByteString()); + com.example.noimports.validationtest.ExampleMessageRules validMsgNoImports = + com.example.noimports.validationtest.ExampleMessageRules.parseFrom(validMsg.toByteString()); expectNoViolations(validMsgNoImports); - com.example.imports.validationtest.ExampleMessageConstraints invalidMsgImports = - com.example.imports.validationtest.ExampleMessageConstraints.newBuilder() + com.example.imports.validationtest.ExampleMessageRules invalidMsgImports = + com.example.imports.validationtest.ExampleMessageRules.newBuilder() .setSecondaryEmail("foo@bar.com") .build(); Violation expectedViolation = Violation.newBuilder() - .setConstraintId("secondary_email_depends_on_primary") + .setRuleId("secondary_email_depends_on_primary") .setMessage("cannot set a secondary email without setting a primary one") .build(); expectViolation(invalidMsgImports, expectedViolation); // Create same message under noimports package. Validation behavior should match. - com.example.noimports.validationtest.ExampleMessageConstraints invalidMsgNoImports = - com.example.noimports.validationtest.ExampleMessageConstraints.parseFrom( + com.example.noimports.validationtest.ExampleMessageRules invalidMsgNoImports = + com.example.noimports.validationtest.ExampleMessageRules.parseFrom( invalidMsgImports.toByteString()); expectViolation(invalidMsgNoImports, expectedViolation); } diff --git a/src/test/java/build/buf/protovalidate/ValidatorDynamicMessageTest.java b/src/test/java/build/buf/protovalidate/ValidatorDynamicMessageTest.java index 685f7f75..1b0a542d 100644 --- a/src/test/java/build/buf/protovalidate/ValidatorDynamicMessageTest.java +++ b/src/test/java/build/buf/protovalidate/ValidatorDynamicMessageTest.java @@ -17,16 +17,16 @@ import static com.example.imports.validationtest.PredefinedProto.isIdent; import static org.assertj.core.api.Assertions.assertThat; -import build.buf.validate.FieldConstraints; import build.buf.validate.FieldPath; import build.buf.validate.FieldPathElement; +import build.buf.validate.FieldRules; import build.buf.validate.Violation; import com.example.imports.buf.validate.StringRules; -import com.example.imports.validationtest.ExamplePredefinedFieldConstraints; -import com.example.noimports.validationtest.ExampleFieldConstraints; -import com.example.noimports.validationtest.ExampleMessageConstraints; -import com.example.noimports.validationtest.ExampleOneofConstraints; -import com.example.noimports.validationtest.ExampleRequiredFieldConstraints; +import com.example.imports.validationtest.ExamplePredefinedFieldRules; +import com.example.noimports.validationtest.ExampleFieldRules; +import com.example.noimports.validationtest.ExampleMessageRules; +import com.example.noimports.validationtest.ExampleOneofRules; +import com.example.noimports.validationtest.ExampleRequiredFieldRules; import com.example.noimports.validationtest.PredefinedProto; import com.google.protobuf.DescriptorProtos; import com.google.protobuf.Descriptors; @@ -46,14 +46,14 @@ * This test mimics the behavior when performing validation with protovalidate on a file descriptor * set (as created by protoc --retain_options --descriptor_set_out=...). These * descriptor types have the protovalidate extensions as unknown fields and need to be parsed with - * an extension registry for the constraints to be recognized and validated. + * an extension registry for the rules to be recognized and validated. */ public class ValidatorDynamicMessageTest { @Test - public void testFieldConstraintDynamicMessage() throws Exception { + public void testFieldRuleDynamicMessage() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExampleFieldConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExampleFieldRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("regex_string_field"), "0123456789"); Violation expectedViolation = @@ -69,13 +69,13 @@ public void testFieldConstraintDynamicMessage() throws Exception { FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.STRING_FIELD_NUMBER))) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.STRING_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( StringRules.getDescriptor() .findFieldByNumber(StringRules.PATTERN_FIELD_NUMBER)))) - .setConstraintId("string.pattern") + .setRuleId("string.pattern") .setMessage("value does not match regex pattern `^[a-z0-9]{1,9}$`") .build(); ValidationResult result = new Validator().validate(messageBuilder.build()); @@ -86,15 +86,15 @@ public void testFieldConstraintDynamicMessage() throws Exception { } @Test - public void testOneofConstraintDynamicMessage() throws Exception { + public void testOneofRuleDynamicMessage() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExampleOneofConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExampleOneofRules.getDefaultInstance()); Violation expectedViolation = Violation.newBuilder() .setField( FieldPath.newBuilder() .addElements(FieldPathElement.newBuilder().setFieldName("contact_info"))) - .setConstraintId("required") + .setRuleId("required") .setMessage("exactly one field is required in oneof") .build(); assertThat(new Validator().validate(messageBuilder.build()).toProto().getViolationsList()) @@ -102,15 +102,15 @@ public void testOneofConstraintDynamicMessage() throws Exception { } @Test - public void testMessageConstraintDynamicMessage() throws Exception { + public void testMessageRuleDynamicMessage() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExampleMessageConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExampleMessageRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("secondary_email"), "something@somewhere.com"); Violation expectedViolation = Violation.newBuilder() - .setConstraintId("secondary_email_depends_on_primary") + .setRuleId("secondary_email_depends_on_primary") .setMessage("cannot set a secondary email without setting a primary one") .build(); assertThat(new Validator().validate(messageBuilder.build()).toProto().getViolationsList()) @@ -118,18 +118,18 @@ public void testMessageConstraintDynamicMessage() throws Exception { } @Test - public void testRequiredFieldConstraintDynamicMessage() throws Exception { + public void testRequiredFieldRuleDynamicMessage() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExampleRequiredFieldConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExampleRequiredFieldRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("regex_string_field"), "abc123"); assertThat(new Validator().validate(messageBuilder.build()).getViolations()).isEmpty(); } @Test - public void testRequiredFieldConstraintDynamicMessageInvalid() throws Exception { + public void testRequiredFieldRuleDynamicMessageInvalid() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExampleRequiredFieldConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExampleRequiredFieldRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("regex_string_field"), "0123456789"); Violation expectedViolation = @@ -145,13 +145,13 @@ public void testRequiredFieldConstraintDynamicMessageInvalid() throws Exception FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.STRING_FIELD_NUMBER))) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.STRING_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement( StringRules.getDescriptor() .findFieldByNumber(StringRules.PATTERN_FIELD_NUMBER)))) - .setConstraintId("string.pattern") + .setRuleId("string.pattern") .setMessage("value does not match regex pattern `^[a-z0-9]{1,9}$`") .build(); assertThat(new Validator().validate(messageBuilder.build()).toProto().getViolationsList()) @@ -159,9 +159,9 @@ public void testRequiredFieldConstraintDynamicMessageInvalid() throws Exception } @Test - public void testPredefinedFieldConstraintDynamicMessage() throws Exception { + public void testPredefinedFieldRuleDynamicMessage() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExamplePredefinedFieldConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExamplePredefinedFieldRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("ident_field"), "abc123"); ExtensionRegistry registry = ExtensionRegistry.newInstance(); @@ -174,9 +174,9 @@ public void testPredefinedFieldConstraintDynamicMessage() throws Exception { } @Test - public void testPredefinedFieldConstraintDynamicMessageInvalid() throws Exception { + public void testPredefinedFieldRuleDynamicMessageInvalid() throws Exception { DynamicMessage.Builder messageBuilder = - createMessageWithUnknownOptions(ExamplePredefinedFieldConstraints.getDefaultInstance()); + createMessageWithUnknownOptions(ExamplePredefinedFieldRules.getDefaultInstance()); messageBuilder.setField( messageBuilder.getDescriptorForType().findFieldByName("ident_field"), "0123456789"); Violation expectedViolation = @@ -190,11 +190,11 @@ public void testPredefinedFieldConstraintDynamicMessageInvalid() throws Exceptio FieldPath.newBuilder() .addElements( FieldPathUtils.fieldPathElement( - FieldConstraints.getDescriptor() - .findFieldByNumber(FieldConstraints.STRING_FIELD_NUMBER))) + FieldRules.getDescriptor() + .findFieldByNumber(FieldRules.STRING_FIELD_NUMBER))) .addElements( FieldPathUtils.fieldPathElement(PredefinedProto.isIdent.getDescriptor()))) - .setConstraintId("string.is_ident") + .setRuleId("string.is_ident") .setMessage("invalid identifier") .build(); ExtensionRegistry registry = ExtensionRegistry.newInstance(); diff --git a/src/test/resources/proto/validationtest/custom_constraints.proto b/src/test/resources/proto/validationtest/custom_rules.proto similarity index 100% rename from src/test/resources/proto/validationtest/custom_constraints.proto rename to src/test/resources/proto/validationtest/custom_rules.proto diff --git a/src/test/resources/proto/validationtest/predefined.proto b/src/test/resources/proto/validationtest/predefined.proto index 74caab08..b5d4af70 100644 --- a/src/test/resources/proto/validationtest/predefined.proto +++ b/src/test/resources/proto/validationtest/predefined.proto @@ -27,6 +27,6 @@ extend buf.validate.StringRules { ]; } -message ExamplePredefinedFieldConstraints { +message ExamplePredefinedFieldRules { optional string ident_field = 1 [(buf.validate.field).string.(is_ident) = true]; } diff --git a/src/test/resources/proto/validationtest/required.proto b/src/test/resources/proto/validationtest/required.proto index c664767e..570694f6 100644 --- a/src/test/resources/proto/validationtest/required.proto +++ b/src/test/resources/proto/validationtest/required.proto @@ -18,7 +18,7 @@ package validationtest; import "buf/validate/validate.proto"; -message ExampleRequiredFieldConstraints { +message ExampleRequiredFieldRules { required string regex_string_field = 1 [(buf.validate.field).string.pattern = "^[a-z0-9]{1,9}$"]; optional string unconstrained = 2; } diff --git a/src/test/resources/proto/validationtest/validationtest.proto b/src/test/resources/proto/validationtest/validationtest.proto index 8f68d600..68d5c0d4 100644 --- a/src/test/resources/proto/validationtest/validationtest.proto +++ b/src/test/resources/proto/validationtest/validationtest.proto @@ -18,12 +18,12 @@ package validationtest; import "buf/validate/validate.proto"; -message ExampleFieldConstraints { +message ExampleFieldRules { string regex_string_field = 1 [(buf.validate.field).string.pattern = "^[a-z0-9]{1,9}$"]; string unconstrained = 2; } -message ExampleOneofConstraints { +message ExampleOneofRules { // contact_info is the user's contact information oneof contact_info { // required ensures that exactly one field in oneof is set. Without this @@ -40,7 +40,7 @@ message ExampleOneofConstraints { } } -message ExampleMessageConstraints { +message ExampleMessageRules { option (buf.validate.message).cel = { id: "secondary_email_depends_on_primary", expression: