• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

.circleci/H13-Jun-2019-

bazel/H13-Jun-2019-

gogoproto/H13-Jun-2019-

java/H13-Jun-2019-

module/H13-Jun-2019-

templates/H13-Jun-2019-

tests/harness/H13-Jun-2019-

validate/H13-Jun-2019-

vendor/H13-Jun-2019-

windows/H13-Jun-2019-

.dockerignoreH A D13-Jun-201913

.gitignoreH A D13-Jun-2019645

BUILDH A D13-Jun-2019746

CODE_OF_CONDUCT.mdH A D13-Jun-20191.3 KiB

DCOH A D13-Jun-20191.4 KiB

DockerfileH A D13-Jun-20191.7 KiB

Gopkg.lockH A D13-Jun-20192.9 KiB

Gopkg.tomlH A D13-Jun-2019366

LICENSEH A D13-Jun-201911.1 KiB

MakefileH A D13-Jun-20196.4 KiB

NOTICEH A D13-Jun-2019116

README.mdH A D13-Jun-201925.7 KiB

WORKSPACEH A D13-Jun-20192.4 KiB

main.goH A D13-Jun-2019302

rule_comparison.mdH A D13-Jun-20193.5 KiB

README.md

1# protoc-gen-validate (PGV)
2
3*This project is currently in **alpha**. The API should be considered unstable and likely to change*
4
5PGV is a protoc plugin to generate polyglot message validators. While protocol buffers effectively guarantee the types of structured data, they cannot enforce semantic rules for values. This plugin adds support to protoc-generated code to validate such constraints.
6
7Developers import the PGV extension and annotate the messages and fields in their proto files with constraint rules:
8
9```protobuf
10syntax = "proto3";
11
12package examplepb;
13
14import "validate/validate.proto";
15
16message Person {
17  uint64 id    = 1 [(validate.rules).uint64.gt    = 999];
18
19  string email = 2 [(validate.rules).string.email = true];
20
21  string name  = 3 [(validate.rules).string = {
22                      pattern:   "^[^[0-9]A-Za-z]+( [^[0-9]A-Za-z]+)*$",
23                      max_bytes: 256,
24                   }];
25
26  Location home = 4 [(validate.rules).message.required = true];
27
28  message Location {
29    double lat = 1 [(validate.rules).double = { gte: -90,  lte: 90 }];
30    double lng = 2 [(validate.rules).double = { gte: -180, lte: 180 }];
31  }
32}
33```
34
35Executing `protoc` with PGV and the target language's default plugin will create `Validate` methods on the generated types:
36
37```go
38p := new(Person)
39
40err := p.Validate() // err: Id must be greater than 999
41p.Id = 1000
42
43err = p.Validate() // err: Email must be a valid email address
44p.Email = "example@lyft.com"
45
46err = p.Validate() // err: Name must match pattern '^[^\d\s]+( [^\d\s]+)*$'
47p.Name = "Protocol Buffer"
48
49err = p.Validate() // err: Home is required
50p.Home = &Location{37.7, 999}
51
52err = p.Validate() // err: Home.Lng must be within [-180, 180]
53p.Home.Lng = -122.4
54
55err = p.Validate() // err: nil
56```
57
58## Usage
59
60### Dependencies
61
62- `go` toolchain (≥ v1.7)
63- `protoc` compiler in `$PATH`
64- `protoc-gen-validate` in `$PATH`
65- official language-specific plugin for target language(s)
66- **Only `proto3` syntax is currently supported.** `proto2` syntax support is planned.
67
68### Installation
69
70Installing PGV can currently only be done from source:
71
72```sh
73# fetches this repo into $GOPATH
74go get -d github.com/envoyproxy/protoc-gen-validate
75
76# installs PGV into $GOPATH/bin
77make build
78```
79
80### Parameters
81
82- **`lang`**: specify the target language to generate. Currently, the only supported options are:
83  - `go`
84  - `gogo` for [gogo proto](https://github.com/gogo/protobuf) (experimental)
85  - `cc` for c++ (partially implemented)
86  - `java`
87
88Support for `python` is planned.
89
90### Examples
91
92#### Go
93
94Go generation should occur into the same output path as the official plugin. For a proto file `example.proto`, the corresponding validation code is generated into `../generated/example.pb.validate.go`:
95
96```sh
97protoc \
98  -I . \
99  -I ${GOPATH}/src \
100  -I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
101  --go_out=":../generated" \
102  --validate_out="lang=go:../generated" \
103  example.proto
104```
105
106All messages generated include the new `Validate() error` method. PGV requires no additional runtime dependencies from the existing generated code.
107
108#### Gogo
109
110There is an experimental support for [gogo
111protobuf](https://github.com/gogo/protobuf) plugin for `go`. Use the following
112command to generate `gogo`-compatible validation code:
113
114```sh
115protoc \
116  -I . \
117  -I ${GOPATH}/src \
118  -I ${GOPATH}/src/github.com/envoyproxy/protoc-gen-validate \
119  --gogofast_out=":../generated"\
120  --validate_out="lang=gogo:../generated" \ example.proto
121```
122
123Gogo support has the following limitations:
124- only `gogofast` plugin is supported and tested, meaning that the fields
125  should be properly annotated with `gogoproto` annotations;
126- `gogoproto.nullable` is supported on fields;
127- `gogoproto.stdduration` is supported on fields;
128- `gogoproto.stdtime` is supported on fields;
129
130#### Java
131
132Java generation is integrated with the existing protobuf toolchain for java projects. For Maven projects, add the following to your pom.xml.
133
134```xml
135<dependencies>
136    <dependency>
137        <groupId> io.envoyproxy.protoc-gen-validate</groupId>
138        <artifactId>pgv-java-stub</artifactId>
139        <version>${pgv.version}</version>
140    </dependency>
141</dependencies>
142
143<build>
144    <extensions>
145        <extension>
146            <groupId>kr.motd.maven</groupId>
147            <artifactId>os-maven-plugin</artifactId>
148            <version>1.4.1.Final</version>
149        </extension>
150    </extensions>
151    <plugins>
152        <plugin>
153            <groupId>org.xolstice.maven.plugins</groupId>
154            <artifactId>protobuf-maven-plugin</artifactId>
155            <version>0.5.0</version>
156            <configuration>
157                <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
158            </configuration>
159                <execution>
160                    <id>protoc-java-pgv</id>
161                    <goals>
162                        <goal>compile-custom</goal>
163                    </goals>
164                    <configuration>
165                        <pluginParameter>lang=java</pluginParameter>
166                        <pluginId>java-pgv</pluginId>
167                        <pluginArtifact>io.envoyproxy.protoc-gen-validate:protoc-gen-validate:${pgv.version}:exe:${os.detected.classifier}</pluginArtifact>
168                    </configuration>
169                </execution>
170            </executions>
171        </plugin>
172    </plugins>
173</build>
174```
175
176Gradle projects follow a similar pattern.
177
178## Constraint Rules
179
180[The provided constraints](validate/validate.proto) are modeled largerly after those in JSON Schema. PGV rules can be mixed for the same field; the plugin ensures the rules applied to a field cannot contradict before code generation.
181
182Check the [constraint rule comparison matrix](rule_comparison.md) for language-specific constraint capabilities.
183
184### Numerics
185
186> All numeric types (`float`, `double`, `int32`, `int64`, `uint32`, `uint64`, `sint32`, `sint64`, `fixed32`, `fixed64`, `sfixed32`, `sfixed64`) share the same rules.
187
188- **const**: the field must be _exactly_ the specified value.
189
190  ```protobuf
191  // x must equal 1.23 exactly
192  float x = 1 [(validate.rules).float.const = 1.23];
193  ```
194
195- **lt/lte/gt/gte**: these inequalities (`<`, `<=`, `>`, `>=`, respectively) allow for deriving ranges in which the field must reside.
196
197  ```protobuf
198  // x must be less than 10
199  int32 x = 1 [(validate.rules).int32.lt = 10];
200
201  // x must be greater than or equal to 20
202  uint64 x = 1 [(validate.rules).uint64.gte = 20];
203
204  // x must be in the range [30, 40)
205  fixed32 x = 1 [(validate.rules).fixed32 = {gte:30, lt: 40}];
206  ```
207
208  Inverting the values of `lt(e)` and `gt(e)` is valid and creates an exclusive range.
209
210  ```protobuf
211  // x must be outside the range [30, 40)
212  double x = 1 [(validate.rules).double = {lt:30, gte:40}];
213  ```
214
215- **in/not_in**: these two rules permit specifying white/blacklists for the values of a field.
216
217  ```protobuf
218  // x must be either 1, 2, or 3
219  uint32 x = 1 [(validate.rules).uint32 = {in: [1,2,3]}];
220
221  // x cannot be 0 nor 0.99
222  float x = 1 [(validate.rules).float = {not_in: [0, 0.99]}];
223  ```
224
225### Bools
226
227- **const**: the field must be _exactly_ the specified value.
228
229  ```protobuf
230  // x must be set to true
231  bool x = 1 [(validate.rules).bool.const = true];
232
233  // x cannot be set to true
234  bool x = 1 [(validate.rules).bool.const = false];
235  ```
236
237### Strings
238
239- **const**: the field must be _exactly_ the specified value.
240
241  ```protobuf
242  // x must be set to "foo"
243  string x = 1 [(validate.rules).string.const = "foo"];
244  ```
245
246- **len/min_len/max_len**: these rules constrain the number of characters (Unicode code points) in the field. Note that the number of characters may differ from the number of bytes in the string. The string is considered as-is, and does not normalize.
247
248  ```protobuf
249  // x must be exactly 5 characters long
250  string x = 1 [(validate.rules).string.len = 5];
251
252  // x must be at least 3 characters long
253  string x = 1 [(validate.rules).string.min_len = 3];
254
255  // x must be between 5 and 10 characters, inclusive
256  string x = 1 [(validate.rules).string = {min_len: 5, max_len: 10}];
257  ```
258
259- **min_bytes/max_bytes**: these rules constrain the number of bytes in the field.
260
261  ```protobuf
262  // x must be at most 15 bytes long
263  string x = 1 [(validate.rules).string.max_bytes = 15];
264
265  // x must be between 128 and 1024 bytes long
266  string x = 1 [(validate.rules).string = {min_bytes: 128, max_bytes: 1024}];
267  ```
268
269- **pattern**: the field must match the specified [RE2-compliant][re2] regular expression. The included expression should elide any delimiters (ie, `/\d+/` should just be `\d+`).
270
271  ```protobuf
272  // x must be a non-empty, case-insensitive hexadecimal string
273  string x = 1 [(validate.rules).string.pattern = "(?i)^[0-9a-f]+$"];
274  ```
275
276- **prefix/suffix/contains**: the field must contain the specified substring in an optionally explicit location.
277
278  ```protobuf
279  // x must begin with "foo"
280  string x = 1 [(validate.rules).string.prefix = "foo"];
281
282  // x must end with "bar"
283  string x = 1 [(validate.rules).string.suffix = "bar"];
284
285  // x must contain "baz" anywhere inside it
286  string x = 1 [(validate.rules).string.contains = "baz"];
287
288  // x must begin with "fizz" and end with "buzz"
289  string x = 1 [(validate.rules).string = {prefix: "fizz", suffix: "buzz"}];
290
291  // x must end with ".proto" and be less than 64 characters
292  string x = 1 [(validate.rules).string = {suffix: ".proto", max_len:64}];
293  ```
294
295- **in/not_in**: these two rules permit specifying white/blacklists for the values of a field.
296
297  ```protobuf
298  // x must be either "foo", "bar", or "baz"
299  string x = 1 [(validate.rules).string = {in: ["foo", "bar", "baz"]}];
300
301  // x cannot be "fizz" nor "buzz"
302  string x = 1 [(validate.rules).string = {not_in: ["fizz", "buzz"]}];
303  ```
304
305- **well-known formats**: these rules provide advanced constraints for common string patterns. These constraints will typically be more permissive and performant than equivalent regular expression patterns, while providing more explanatory failure descriptions.
306
307  ```protobuf
308  // x must be a valid email address (via RFC 1034)
309  string x = 1 [(validate.rules).string.email = true];
310
311  // x must be a valid address (IP or Hostname).
312  string x = 1 [(validate.rules).string.address = true];
313
314  // x must be a valid hostname (via RFC 1034)
315  string x = 1 [(validate.rules).string.hostname = true];
316
317  // x must be a valid IP address (either v4 or v6)
318  string x = 1 [(validate.rules).string.ip = true];
319
320  // x must be a valid IPv4 address
321  // eg: "192.168.0.1"
322  string x = 1 [(validate.rules).string.ipv4 = true];
323
324  // x must be a valid IPv6 address
325  // eg: "fe80::3"
326  string x = 1 [(validate.rules).string.ipv6 = true];
327
328  // x must be a valid absolute URI (via RFC 3986)
329  string x = 1 [(validate.rules).string.uri = true];
330
331  // x must be a valid URI reference (either absolute or relative)
332  string x = 1 [(validate.rules).string.uri_ref = true];
333  ```
334
335### Bytes
336
337> Literal values should be expressed with strings, using escaping where necessary.
338
339- **const**: the field must be _exactly_ the specified value.
340
341  ```protobuf
342  // x must be set to "foo" ("\x66\x6f\x6f")
343  bytes x = 1 [(validate.rules).bytes.const = "foo"];
344
345  // x must be set to "\xf0\x90\x28\xbc"
346  bytes x = 1 [(validate.rules).bytes.const = "\xf0\x90\x28\xbc"];
347  ```
348
349- **len/min_len/max_len**: these rules constrain the number of bytes in the field.
350
351  ```protobuf
352  // x must be exactly 3 bytes
353  bytes x = 1 [(validate.rules).bytes.len = 3];
354
355  // x must be at least 3 bytes long
356  bytes x = 1 [(validate.rules).bytes.min_len = 3];
357
358  // x must be between 5 and 10 bytes, inclusive
359  bytes x = 1 [(validate.rules).bytes = {min_len: 5, max_len: 10}];
360  ```
361
362- **pattern**: the field must match the specified [RE2-compliant][re2] regular expression. The included expression should elide any delimiters (ie, `/\d+/` should just be `\d+`).
363
364  ```protobuf
365  // x must be a non-empty, ASCII byte sequence
366  bytes x = 1 [(validate.rules).bytes.pattern = "^[\x00-\x7F]+$"];
367  ```
368
369- **prefix/suffix/contains**: the field must contain the specified byte sequence in an optionally explicit location.
370
371  ```protobuf
372  // x must begin with "\x99"
373  bytes x = 1 [(validate.rules).bytes.prefix = "\x99"];
374
375  // x must end with "buz\x7a"
376  bytes x = 1 [(validate.rules).bytes.suffix = "buz\x7a"];
377
378  // x must contain "baz" anywhere inside it
379  bytes x = 1 [(validate.rules).bytes.contains = "baz"];
380  ```
381
382- **in/not_in**: these two rules permit specifying white/blacklists for the values of a field.
383
384  ```protobuf
385  // x must be either "foo", "bar", or "baz"
386  bytes x = 1 [(validate.rules).bytes = {in: ["foo", "bar", "baz"]}];
387
388  // x cannot be "fizz" nor "buzz"
389  bytes x = 1 [(validate.rules).bytes = {not_in: ["fizz", "buzz"]}];
390  ```
391
392- **well-known formats**: these rules provide advanced constraints for common patterns. These constraints will typically be more permissive and performant than equivalent regular expression patterns, while providing more explanatory failure descriptions.
393
394  ```protobuf
395  // x must be a valid IP address (either v4 or v6) in byte format
396  bytes x = 1 [(validate.rules).bytes.ip = true];
397
398  // x must be a valid IPv4 address in byte format
399  // eg: "\xC0\xA8\x00\x01"
400  bytes x = 1 [(validate.rules).bytes.ipv4 = true];
401
402  // x must be a valid IPv6 address in byte format
403  // eg: "\x20\x01\x0D\xB8\x85\xA3\x00\x00\x00\x00\x8A\x2E\x03\x70\x73\x34"
404  bytes x = 1 [(validate.rules).bytes.ipv6 = true];
405  ```
406
407### Enums
408
409> All literal values should use the numeric (int32) value as defined in the enum descriptor.
410
411The following examples use this `State` enum
412
413```protobuf
414enum State {
415  INACTIVE = 0;
416  PENDING  = 1;
417  ACTIVE   = 2;
418}
419```
420
421- **const**: the field must be _exactly_ the specified value.
422
423  ```protobuf
424  // x must be set to ACTIVE (2)
425  State x = 1 [(validate.rules).enum.const = 2];
426  ```
427
428- **defined_only**: the field must be one of the specified values in the enum descriptor.
429
430  ```protobuf
431  // x can only be INACTIVE, PENDING, or ACTIVE
432  State x = 1 [(validate.rules).enum.defined_only = true];
433  ```
434
435- **in/not_in**: these two rules permit specifying white/blacklists for the values of a field.
436
437  ```protobuf
438  // x must be either INACTIVE (0) or ACTIVE (2)
439  State x = 1 [(validate.rules).enum = {in: [0,2]}];
440
441  // x cannot be PENDING (1)
442  State x = 1 [(validate.rules).enum = {not_in: [1]}];
443  ```
444
445### Messages
446
447> If a field contains a message and the message has been generated with PGV, validation will be performed recursively. Message's not generated with PGV are skipped.
448
449```protobuf
450// if Person was generated with PGV and x is set,
451// x's fields will be validated.
452Person x = 1;
453```
454
455- **skip**: this rule specifies that the validation rules of this field should not be evaluated.
456
457  ```protobuf
458  // The fields on Person x will not be validated.
459  Person x = 1 [(validate.rules).message.skip = true];
460  ```
461
462- **required**: this rule specifies that the field cannot be unset.
463
464  ```protobuf
465  // x cannot be unset
466  Person x = 1 [(validate.rules).message.required = true];
467
468  // x cannot be unset, but the validations on x will not be performed
469  Person x = 1 [(validate.rules).message = {required: true, skip: true}];
470  ```
471
472### Repeated
473
474- **min_items/max_items**: these rules control how many elements are contained in the field
475
476  ```protobuf
477  // x must contain at least 3 elements
478  repeated int32 x = 1 [(validate.rules).repeated.min_items = 3];
479
480  // x must contain between 5 and 10 Persons, inclusive
481  repeated Person x = 1 [(validate.rules).repeated = {min_items: 5, max_items: 10}];
482
483  // x must contain exactly 7 elements
484  repeated double x = 1 [(validate.rules).repeated = {min_items: 7, max_items: 7}];
485  ```
486
487- **unique**: this rule requires that all elements in the field must be unique. This rule does not support repeated messages.
488
489  ```protobuf
490  // x must contain unique int64 values
491  repeated int64 x = 1 [(validate.rules).repeated.unique = true];
492  ```
493
494- **items**: this rule specifies constraints that should be applied to each element in the field. Repeated message fields also have their validation rules applied unless `skip` is specified on this constraint.
495
496  ```protobuf
497  // x must contain positive float values
498  repeated float x = 1 [(validate.rules).repeated.items.float.gt = 0];
499
500  // x must contain Persons but don't validate them
501  repeated Person x = 1 [(validate.rules).repeated.items.message.skip = true];
502  ```
503
504### Maps
505
506- **min_pairs/max_pairs**: these rules control how many KV pairs are contained in this field
507
508  ```protobuf
509  // x must contain at most 3 KV pairs
510  map<string, uint64> x = 1 [(validate.rules).map.min_pairs = 3];
511
512  // x must contain between 5 and 10 KV pairs
513  map<string, string> x = 1 [(validate.rules)].map = {min_pairs: 5, max_pairs: 10}];
514
515  // x must contain exactly 7 KV pairs
516  map<string, Person> x = 1 [(validate.rules)].map = {min_pairs: 7, max_pairs: 7}];
517  ```
518
519- **no_sparse**: for map fields with message values, setting this rule to true disallows keys with unset values.
520
521  ```protobuf
522  // all values in x must be set
523  map<uint64, Person> x = 1 [(validate.rules).map.no_sparse = true];
524  ```
525
526- **keys**: this rule specifies constraints that are applied to the keys in the field.
527
528  ```protobuf
529  // x's keys must all be negative
530  <sint32, string> x = [(validate.rules).map.keys.sint32.lt = 0];
531  ```
532
533- **values**: this rule specifies constraints that are be applied to each value in the field. Repeated message fields also have their validation rules applied unless `skip` is specified on this constraint.
534
535  ```protobuf
536  // x must contain strings of at least 3 characters
537  map<string, string> x = 1 [(validate.rules).map.values.string.min_len = 3];
538
539  // x must contain Persons but doesn't validate them
540  map<string, Person> x = 1 [(validate.rules).map.values.message.skip = true];
541  ```
542
543### Well-Known Types (WKTs)
544
545A set of [WKTs][wkts] are packaged with protoc and common message patterns useful in many domains.
546
547#### Scalar Value Wrappers
548
549In the `proto3` syntax, there is no way of distinguishing between unset and the zero value of a scalar field. The value WKTs permit this differentiation by wrapping them in a message. PGV permits using the same scalar rules that the wrapper encapsulates.
550
551```protobuf
552// if it is set, x must be greater than 3
553google.protobuf.Int32Value x = 1 [(validate.rules).int32.gt = 3];
554```
555
556#### Anys
557
558- **required**: this rule specifies that the field must be set
559
560  ```protobuf
561  // x cannot be unset
562  google.protobuf.Any x = 1 [(validate.rules).any.required = true];
563  ```
564
565- **in/not_in**: these two rules permit specifying white/blacklists for the `type_url` value in this field. Consider using a `oneof` union instead of `in` if possible.
566
567  ```protobuf
568  // x must not be the Duration or Timestamp WKT
569  google.protobuf.Any x = 1 [(validate.rules).any = {not_in: [
570      "type.googleapis.com/google.protobuf.Duration",
571      "type.googleapis.com/google.protobuf.Timestamp"
572    ]}];
573  ```
574
575#### Durations
576
577- **required**: this rule specifies that the field must be set
578
579  ```protobuf
580  // x cannot be unset
581  google.protobuf.Duration x = 1 [(validate.rules).duration.required = true];
582  ```
583
584- **const**: the field must be _exactly_ the specified value.
585
586  ```protobuf
587  // x must equal 1.5s exactly
588  google.protobuf.Duration x = 1 [(validate.rules).duration.const = {
589      seconds: 1,
590      nanos:   500000000
591    }];
592  ```
593
594- **lt/lte/gt/gte**: these inequalities (`<`, `<=`, `>`, `>=`, respectively) allow for deriving ranges in which the field must reside.
595
596  ```protobuf
597  // x must be less than 10s
598  google.protobuf.Duration x = 1 [(validate.rules).duration.lt.seconds = 10];
599
600  // x must be greater than or equal to 20ns
601  google.protobuf.Duration x = 1 [(validate.rules).duration.gte.nanos = 20];
602
603  // x must be in the range [0s, 1s)
604  google.protobuf.Duration x = 1 [(validate.rules).duration = {
605      gte: {},
606      lt:  {seconds: 1}
607    }];
608  ```
609
610  Inverting the values of `lt(e)` and `gt(e)` is valid and creates an exclusive range.
611
612  ```protobuf
613  // x must be outside the range [0s, 1s)
614  google.protobuf.Duration x = 1 [(validate.rules).duration = {
615      lt:  {},
616      gte: {seconds: 1}
617    }];
618  ```
619
620- **in/not_in**: these two rules permit specifying white/blacklists for the values of a field.
621
622  ```protobuf
623  // x must be either 0s or 1s
624  google.protobuf.Duration x = 1 [(validate.rules).duration = {in: [
625      {},
626      {seconds: 1}
627    ]}];
628
629  // x cannot be 20s nor 500ns
630  google.protobuf.Duration x = 1 [(validate.rules).duration = {not_in: [
631      {seconds: 20},
632      {nanos: 500}
633    ]}];
634  ```
635
636#### Timestamps
637
638- **required**: this rule specifies that the field must be set
639
640  ```protobuf
641  // x cannot be unset
642  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.required = true];
643  ```
644
645- **const**: the field must be _exactly_ the specified value.
646
647  ```protobuf
648  // x must equal 2009/11/10T23:00:00.500Z exactly
649  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
650      seconds: 63393490800,
651      nanos:   500000000
652    }];
653  ```
654
655- **lt/lte/gt/gte**: these inequalities (`<`, `<=`, `>`, `>=`, respectively) allow for deriving ranges in which the field must reside.
656
657  ```protobuf
658  // x must be less than the Unix Epoch
659  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt.seconds = 0];
660
661  // x must be greater than or equal to 2009/11/10T23:00:00Z
662  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.gte.seconds = 63393490800];
663
664  // x must be in the range [epoch, 2009/11/10T23:00:00Z)
665  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
666      gte: {},
667      lt:  {seconds: 63393490800}
668    }];
669  ```
670
671  Inverting the values of `lt(e)` and `gt(e)` is valid and creates an exclusive range.
672
673  ```protobuf
674  // x must be outside the range [epoch, 2009/11/10T23:00:00Z)
675  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
676      lt:  {},
677      gte: {seconds: 63393490800}
678    }];
679  ```
680
681- **lt_now/gt_now**: these inequalities allow for ranges relative to the current time. These rules cannot be used with the absolute rules above.
682
683  ```protobuf
684  // x must be less than the current timestamp
685  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt_now = true];
686  ```
687- **within**: this rule specifies that the field's value should be within a duration of the current time. This rule can be used in conjunction with `lt_now` and `gt_now` to control those ranges.
688
689  ```protobuf
690  // x must be within ±1s of the current time
691  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.within.seconds = 1];
692
693  // x must be within the range (now, now+1h)
694  google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
695      gt_now: true,
696      within: {seconds: 3600}
697    }];
698  ```
699
700### Message-Global
701
702- **disabled**: All validation rules for the fields on a message can be nullified, including any message fields that support validation themselves.
703
704  ```protobuf
705  message Person {
706    option (validate.disabled) = true;
707
708    // x will not be required to be greater than 123
709    uint64 x = 1 [(validate.rules).uint64.gt = 123];
710
711    // y's fields will not be validated
712    Person y = 2;
713  }
714  ```
715
716### OneOfs
717
718- **required**: require that one of the fields in a `oneof` must be set. By default, none or one of the unioned fields can be set. Enabling this rules disallows having all of them unset.
719
720  ```protobuf
721  oneof id {
722    // either x, y, or z must be set.
723    option (validate.required) = true;
724
725    string x = 1;
726    int32  y = 2;
727    Person z = 3;
728  }
729  ```
730
731## Development
732
733PGV is written in Go on top of the [protoc-gen-star][pg*] framework and compiles to a standalone binary.
734
735### Dependencies
736
737All PGV dependencies are currently checked into the project. To test PGV, `protoc` must be installed, either from [source][protoc-source], the provided [releases][protoc-releases], or a package manager. The official protoc plugin for the target language(s) should be installed as well.
738
739### Make Targets
740
741- **`make build`**: generates the constraints proto and compiles PGV into `$GOPATH/bin`
742
743- **`make lint`**: runs static-analysis rules against the PGV codebase, including `golint`, `go vet`, and `gofmt -s`
744
745- **`make tests`**: runs all tests with race detection and coverage percentage
746
747- **`make quick`**: runs all tests without the race detector or coverage percentage
748
749- **`make cover`**: runs all tests with race detection, generating a coverage report and opening it in a browser
750
751- **`make kitchensink`**: generates the proto files in [`/tests/kitchensink`](/tests/kitchensink). This includes the officially generated code, as well as the validations.
752
753- **`make testcases`**: generates the proto files in [`/tests/harness/cases`](/tests/harness/cases). These are used by the test harness to verify the validation rules generated for each language.
754
755- **`make harness`**: executes the test-cases against each language's test harness.
756
757### Run all tests under Bazel
758
759Ensure that your `PATH` is setup to include `protoc-gen-go` and `protoc`, then:
760
761```
762bazel run //tests/harness/executor:executor
763```
764
765### Docker
766
767PGV comes with a [Dockerfile](/Dockerfile) for consistent development tooling and CI. The main entrypoint is `make` with `quick` as the default target. This repo should be volumed into `/go/src/github.com/envoyproxy/protoc-gen-validate` for the proper behavior.
768
769```sh
770# build the image
771docker build -t lyft/protoc-gen-validate .
772
773# executes the default make target: quick
774docker run --rm \
775  -v $(PWD):/go/src/github.com/envoyproxy/protoc-gen-validate \
776  lyft/protoc-gen-validate
777
778# executes the 'build' & 'generate-testdata' make targets
779docker run --rm \
780  -v $(PWD):/go/src/github.com/envoyproxy/protoc-gen-validate \
781  lyft/protoc-gen-validate \
782  build generate-testdata
783```
784
785[protoc-source]:   https://github.com/google/protobuf
786[protoc-releases]: https://github.com/google/protobuf/releases
787[pg*]:             https://github.com/lyft/protoc-gen-star
788[re2]:             https://github.com/google/re2/wiki/Syntax
789[wkts]:            https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
790