Skip to content

Latest commit

 

History

History
67 lines (55 loc) · 2.8 KB

predefined-constraints.md

File metadata and controls

67 lines (55 loc) · 2.8 KB

Predefined Constraints

Custom constraints in protovalidate afford a lot of power, but can become cumbersome and repetitive when the same kind of custom constraints are needed across multiple fields or messages. To this end, protovalidate provides a mechanism for creating reusable constraints that can be applied on multiple fields.

Predefined constraints require Protobuf extensions, which are not available in proto3. Either proto2 or at least Protobuf 2023 Edition must be used to define predefined constraints. Predefined constraints defined in proto2 or Protobuf 2023 Edition or later can be imported and utilized in proto3 files.

Predefined Field Constraints

To create a predefined field constraint, extend one of the standard rules messages. For example, to define a new rule for float fields, extend buf.validate.FloatRules, as follows:

import "buf/validate/validate.proto";

extend buf.validate.FloatRules {
  float abs_range = 80048952 [(buf.validate.predefined).cel = {
    id: "float.abs_range"
    expression: "this >= -rule && this <= rule"
    message: "float value is out of range"
  }];
}

Tip

Constraints can refer to their own value with the rule constant. Rules apply when they are set, so a boolean constraint in the form of is_... should always check to ensure that rule is true.

Warning

Be mindful that extension numbers must not conflict with any other extension to the same message across all Protobuf files in a given process. This restriction also applies to users that consume Protobuf files indirectly as dependencies. The same extension number may be re-used across different kinds of constraint, e.g. 1000 in FloatRules is distinct from 1000 in Int32Rules.

Extension numbers may be from 1000 to 536870911, inclusive. Values from 1000 to 99999 are reserved for Protobuf Global Extension Registry entries, and values from 100000 to 536870911 are reserved for integers that are not explicitly assigned. It is discouraged to use the latter range for rules that are defined in public schemas due to the risk of conflicts.

Similarly to the standard constraints, a rule will take effect when it is set on the options of a field. Here is how one might use a predefined constraint:

message MyMessage {
  float normal_value = 1 [(buf.validate.field).float.(abs_range) = 1.0];
}

Tip

Extensions are always qualified by the package they are defined in. In this example, we assume that abs_range is defined in the same package it is used in, so no further qualification is needed. In other cases, you will need to qualify the package name of the extension, e.g. (buf.validate.field).float.(foo.bar.abs_range)