Giter Site home page Giter Site logo

bufbuild / protoc-gen-validate Goto Github PK

View Code? Open in Web Editor NEW
3.7K 200.0 578.0 13.77 MB

Protocol Buffer Validation - Being replaced by github.com/bufbuild/protovalidate

Home Page: https://github.com/bufbuild/protovalidate

License: Apache License 2.0

Makefile 2.50% Go 48.37% Starlark 17.01% Dockerfile 0.39% C++ 3.10% Python 9.90% Shell 0.03% Java 18.64% PowerShell 0.04%
protoc constraints protoc-plugin protocol-buffers validation

protoc-gen-validate's Introduction

protoc-gen-validate (PGV)

License Release Slack

Important

protoc-gen-validate (PGV) has reached a stable state and is in maintenance mode.

We recommend that new and existing projects transition to using protovalidate.

Read our blog post if you want to learn more about the limitations of protoc-gen-validate and how we have designed protovalidate to be better.

PGV 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.

Developers import the PGV extension and annotate the messages and fields in their proto files with constraint rules:

syntax = "proto3";

package examplepb;

import "validate/validate.proto";

message Person {
  uint64 id = 1 [(validate.rules).uint64.gt = 999];

  string email = 2 [(validate.rules).string.email = true];

  string name = 3 [(validate.rules).string = {
    pattern:   "^[A-Za-z]+( [A-Za-z]+)*$",
    max_bytes: 256,
  }];

  Location home = 4 [(validate.rules).message.required = true];

  message Location {
    double lat = 1 [(validate.rules).double = {gte: -90,  lte: 90}];
    double lng = 2 [(validate.rules).double = {gte: -180, lte: 180}];
  }
}

Executing protoc with PGV and the target language's default plugin will create Validate methods on the generated types:

p := new(Person)

err := p.Validate() // err: Id must be greater than 999
p.Id = 1000

err = p.Validate() // err: Email must be a valid email address
p.Email = "[email protected]"

err = p.Validate() // err: Name must match pattern '^[A-Za-z]+( [A-Za-z]+)*$'
p.Name = "Protocol Buffer"

err = p.Validate() // err: Home is required
p.Home = &Location{37.7, 999}

err = p.Validate() // err: Home.Lng must be within [-180, 180]
p.Home.Lng = -122.4

err = p.Validate() // err: nil

Usage

Dependencies

  • go toolchain (โ‰ฅ v1.7)
  • protoc compiler in $PATH
  • protoc-gen-validate in $PATH
  • official language-specific plugin for target language(s)
  • Only proto3 syntax is currently supported. proto2 syntax support is planned.

Installation

Download from GitHub Releases

Download assets from GitHub Releases and unarchive them and add plugins into $PATH.

Build from source

# fetches this repo into $GOPATH
go get -d github.com/envoyproxy/protoc-gen-validate

๐Ÿ’ก Yes, our go module path is github.com/envoyproxy/protoc-gen-validate not bufbuild this is intentional.

Changing the module path is effectively creating a new, independent module. We would prefer not to break our users. The Go team are working on better cmd/go support for modules that change paths, but progress is slow. Until then, we will continue to use the envoyproxy module path.

git clone https://github.com/bufbuild/protoc-gen-validate.git
# installs PGV into $GOPATH/bin
cd protoc-gen-validate && make build

Parameters

  • lang: specify the target language to generate. Currently, the only supported options are:
    • go
    • cc for c++ (partially implemented)
    • java
  • Note: Python works via runtime code generation. There's no compile-time generation. See the Python section for details.

Examples

Go

Go 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:

protoc \
  -I . \
  -I path/to/validate/ \
  --go_out=":../generated" \
  --validate_out="lang=go:../generated" \
  example.proto

All messages generated include the following methods:

  • Validate() error which returns the first error encountered during validation.
  • ValidateAll() error which returns all errors encountered during validation.

PGV requires no additional runtime dependencies from the existing generated code.

Note: by default example.pb.validate.go is nested in a directory structure that matches your option go_package name. You can change this using the protoc parameter paths=source_relative:., as like --validate_out="lang=go,paths=source_relative:../generated". Then --validate_out will output the file where it is expected. See Google's protobuf documentation or packages and input paths or parameters for more information.

There's also support for the module=example.com/foo flag described here .

With newer Buf CLI versions (>v1.9.0), you can use the new plugin key instead of using the protoc command directly:

# buf.gen.yaml

version: v1
plugins:
  - plugin: buf.build/bufbuild/validate-go
    out: gen
# proto/buf.yaml

version: v1
deps:
  - buf.build/envoyproxy/protoc-gen-validate

Java

Java generation is integrated with the existing protobuf toolchain for java projects. For Maven projects, add the following to your pom.xml or build.gradle.

<dependencies>
    <dependency>
        <groupId>build.buf.protoc-gen-validate</groupId>
        <artifactId>pgv-java-stub</artifactId>
        <version>${pgv.version}</version>
    </dependency>
</dependencies>

<build>
<extensions>
    <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.4.1.Final</version>
    </extension>
</extensions>
<plugins>
    <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.6.1</version>
        <configuration>
            <protocArtifact>
                com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
            </protocArtifact>
        </configuration>
        <executions>
            <execution>
                <id>protoc-java-pgv</id>
                <goals>
                    <goal>compile-custom</goal>
                </goals>
                <configuration>
                    <pluginParameter>lang=java</pluginParameter>
                    <pluginId>java-pgv</pluginId>
                    <pluginArtifact>
                        build.buf.protoc-gen-validate:protoc-gen-validate:${pgv.version}:exe:${os.detected.classifier}
                    </pluginArtifact>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>
</build>
plugins {
    ...
    id "com.google.protobuf" version "${protobuf.version}"
    ...
}

protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:${protoc.version}"
    }

    plugins {
        javapgv {
            artifact = "build.buf.protoc-gen-validate:protoc-gen-validate:${pgv.version}"
        }
    }

    generateProtoTasks {
        all()*.plugins {
            javapgv {
                option "lang=java"
            }
        }
    }
}
// Create a validator index that reflectively loads generated validators
ValidatorIndex index = new ReflectiveValidatorIndex();
// Assert that a message is valid
index.validatorFor(message.getClass()).assertValid(message);

// Create a gRPC client and server interceptor to automatically validate messages (requires pgv-java-grpc module)
clientStub = clientStub.withInterceptors(new ValidatingClientInterceptor(index));
serverBuilder.addService(ServerInterceptors.intercept(svc, new ValidatingServerInterceptor(index)));

Python

The python implementation works via JIT code generation. In other words, the validate(msg) function is written on-demand and exec-ed. An LRU-cache improves performance by storing generated functions per descriptor.

The python package is available on PyPI.

To run validate(), do the following:

from entities_pb2 import Person
from protoc_gen_validate.validator import validate, ValidationFailed

p = Person(first_name="Foo", last_name="Bar", age=42)
try:
    validate(p)
except ValidationFailed as err:
    print(err)

You can view what code has been generated by using the print_validate() function.

Constraint Rules

The provided constraints 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.

Check the constraint rule comparison matrix for language-specific constraint capabilities.

Numerics

All numeric types (float, double, int32, int64, uint32, uint64 , sint32, sint64, fixed32, fixed64, sfixed32, sfixed64) share the same rules.

  • const: the field must be exactly the specified value.

    // x must equal 1.23 exactly
    float x = 1 [(validate.rules).float.const = 1.23];
  • lt/lte/gt/gte: these inequalities (<, <=, >, >=, respectively) allow for deriving ranges in which the field must reside.

    // x must be less than 10
    int32 x = 1 [(validate.rules).int32.lt = 10];
    
    // x must be greater than or equal to 20
    uint64 x = 1 [(validate.rules).uint64.gte = 20];
    
    // x must be in the range [30, 40)
    fixed32 x = 1 [(validate.rules).fixed32 = {gte:30, lt: 40}];

    Inverting the values of lt(e) and gt(e) is valid and creates an exclusive range.

    // x must be outside the range [30, 40)
    double x = 1 [(validate.rules).double = {lt:30, gte:40}];
  • in/not_in: these two rules permit specifying allow/denylists for the values of a field.

    // x must be either 1, 2, or 3
    uint32 x = 1 [(validate.rules).uint32 = {in: [1,2,3]}];
    
    // x cannot be 0 nor 0.99
    float x = 1 [(validate.rules).float = {not_in: [0, 0.99]}];
  • ignore_empty: this rule specifies that if field is empty or set to the default value, to ignore any validation rules. These are typically useful where being able to unset a field in an update request, or to skip validation for optional fields where switching to WKTs is not feasible.

    uint32 x = 1 [(validate.rules).uint32 = {ignore_empty: true, gte: 200}];

Bools

  • const: the field must be exactly the specified value.

    // x must be set to true
    bool x = 1 [(validate.rules).bool.const = true];
    
    // x cannot be set to true
    bool x = 1 [(validate.rules).bool.const = false];

Strings

  • const: the field must be exactly the specified value.

    // x must be set to "foo"
    string x = 1 [(validate.rules).string.const = "foo"];
  • 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.

    // x must be exactly 5 characters long
    string x = 1 [(validate.rules).string.len = 5];
    
    // x must be at least 3 characters long
    string x = 1 [(validate.rules).string.min_len = 3];
    
    // x must be between 5 and 10 characters, inclusive
    string x = 1 [(validate.rules).string = {min_len: 5, max_len: 10}];
  • min_bytes/max_bytes: these rules constrain the number of bytes in the field.

    // x must be at most 15 bytes long
    string x = 1 [(validate.rules).string.max_bytes = 15];
    
    // x must be between 128 and 1024 bytes long
    string x = 1 [(validate.rules).string = {min_bytes: 128, max_bytes: 1024}];
  • pattern: the field must match the specified RE2-compliant regular expression. The included expression should elide any delimiters (ie, /\d+/ should just be \d+).

    // x must be a non-empty, case-insensitive hexadecimal string
    string x = 1 [(validate.rules).string.pattern = "(?i)^[0-9a-f]+$"];
  • prefix/suffix/contains/not_contains: the field must contain the specified substring in an optionally explicit location, or not contain the specified substring.

    // x must begin with "foo"
    string x = 1 [(validate.rules).string.prefix = "foo"];
    
    // x must end with "bar"
    string x = 1 [(validate.rules).string.suffix = "bar"];
    
    // x must contain "baz" anywhere inside it
    string x = 1 [(validate.rules).string.contains = "baz"];
    
    // x cannot contain "baz" anywhere inside it
    string x = 1 [(validate.rules).string.not_contains = "baz"];
    
    // x must begin with "fizz" and end with "buzz"
    string x = 1 [(validate.rules).string = {prefix: "fizz", suffix: "buzz"}];
    
    // x must end with ".proto" and be less than 64 characters
    string x = 1 [(validate.rules).string = {suffix: ".proto", max_len:64}];
  • in/not_in: these two rules permit specifying allow/denylists for the values of a field.

    // x must be either "foo", "bar", or "baz"
    string x = 1 [(validate.rules).string = {in: ["foo", "bar", "baz"]}];
    
    // x cannot be "fizz" nor "buzz"
    string x = 1 [(validate.rules).string = {not_in: ["fizz", "buzz"]}];
  • ignore_empty: this rule specifies that if field is empty or set to the default value, to ignore any validation rules. These are typically useful where being able to unset a field in an update request, or to skip validation for optional fields where switching to WKTs is not feasible.

    string CountryCode = 1 [(validate.rules).string = {ignore_empty: true, len: 2}];
  • 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.

    // x must be a valid email address (via RFC 5322)
    string x = 1 [(validate.rules).string.email = true];
    
    // x must be a valid address (IP or Hostname).
    string x = 1 [(validate.rules).string.address = true];
    
    // x must be a valid hostname (via RFC 1034)
    string x = 1 [(validate.rules).string.hostname = true];
    
    // x must be a valid IP address (either v4 or v6)
    string x = 1 [(validate.rules).string.ip = true];
    
    // x must be a valid IPv4 address
    // eg: "192.168.0.1"
    string x = 1 [(validate.rules).string.ipv4 = true];
    
    // x must be a valid IPv6 address
    // eg: "fe80::3"
    string x = 1 [(validate.rules).string.ipv6 = true];
    
    // x must be a valid absolute URI (via RFC 3986)
    string x = 1 [(validate.rules).string.uri = true];
    
    // x must be a valid URI reference (either absolute or relative)
    string x = 1 [(validate.rules).string.uri_ref = true];
    
    // x must be a valid UUID (via RFC 4122)
    string x = 1 [(validate.rules).string.uuid = true];
    
    // x must conform to a well known regex for HTTP header names (via RFC 7230)
    string x = 1 [(validate.rules).string.well_known_regex = HTTP_HEADER_NAME]
    
    // x must conform to a well known regex for HTTP header values (via RFC 7230)
    string x = 1 [(validate.rules).string.well_known_regex = HTTP_HEADER_VALUE];
    
    // x must conform to a well known regex for headers, disallowing \r\n\0 characters.
    string x = 1 [(validate.rules).string {well_known_regex: HTTP_HEADER_VALUE, strict: false}];

Bytes

Literal values should be expressed with strings, using escaping where necessary.

  • const: the field must be exactly the specified value.

    // x must be set to "foo" ("\x66\x6f\x6f")
    bytes x = 1 [(validate.rules).bytes.const = "foo"];
    
    // x must be set to "\xf0\x90\x28\xbc"
    bytes x = 1 [(validate.rules).bytes.const = "\xf0\x90\x28\xbc"];
  • len/min_len/max_len: these rules constrain the number of bytes in the field.

    // x must be exactly 3 bytes
    bytes x = 1 [(validate.rules).bytes.len = 3];
    
    // x must be at least 3 bytes long
    bytes x = 1 [(validate.rules).bytes.min_len = 3];
    
    // x must be between 5 and 10 bytes, inclusive
    bytes x = 1 [(validate.rules).bytes = {min_len: 5, max_len: 10}];
  • pattern: the field must match the specified RE2-compliant regular expression. The included expression should elide any delimiters (ie, /\d+/ should just be \d+).

    // x must be a non-empty, ASCII byte sequence
    bytes x = 1 [(validate.rules).bytes.pattern = "^[\x00-\x7F]+$"];
  • prefix/suffix/contains: the field must contain the specified byte sequence in an optionally explicit location.

    // x must begin with "\x99"
    bytes x = 1 [(validate.rules).bytes.prefix = "\x99"];
    
    // x must end with "buz\x7a"
    bytes x = 1 [(validate.rules).bytes.suffix = "buz\x7a"];
    
    // x must contain "baz" anywhere inside it
    bytes x = 1 [(validate.rules).bytes.contains = "baz"];
  • in/not_in: these two rules permit specifying allow/denylists for the values of a field.

    // x must be either "foo", "bar", or "baz"
    bytes x = 1 [(validate.rules).bytes = {in: ["foo", "bar", "baz"]}];
    
    // x cannot be "fizz" nor "buzz"
    bytes x = 1 [(validate.rules).bytes = {not_in: ["fizz", "buzz"]}];
  • ignore_empty: this rule specifies that if field is empty or set to the default value, to ignore any validation rules. These are typically useful where being able to unset a field in an update request, or to skip validation for optional fields where switching to WKTs is not feasible.

    bytes x = 1 [(validate.rules).bytes = {ignore_empty: true, in: ["foo", "bar", "baz"]}];
  • 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.

    // x must be a valid IP address (either v4 or v6) in byte format
    bytes x = 1 [(validate.rules).bytes.ip = true];
    
    // x must be a valid IPv4 address in byte format
    // eg: "\xC0\xA8\x00\x01"
    bytes x = 1 [(validate.rules).bytes.ipv4 = true];
    
    // x must be a valid IPv6 address in byte format
    // eg: "\x20\x01\x0D\xB8\x85\xA3\x00\x00\x00\x00\x8A\x2E\x03\x70\x73\x34"
    bytes x = 1 [(validate.rules).bytes.ipv6 = true];

Enums

All literal values should use the numeric (int32) value as defined in the enum descriptor.

The following examples use this State enum

enum State {
  INACTIVE = 0;
  PENDING = 1;
  ACTIVE = 2;
}
  • const: the field must be exactly the specified value.

    // x must be set to ACTIVE (2)
    State x = 1 [(validate.rules).enum.const = 2];
  • defined_only: the field must be one of the specified values in the enum descriptor.

    // x can only be INACTIVE, PENDING, or ACTIVE
    State x = 1 [(validate.rules).enum.defined_only = true];
  • in/not_in: these two rules permit specifying allow/denylists for the values of a field.

    // x must be either INACTIVE (0) or ACTIVE (2)
    State x = 1 [(validate.rules).enum = {in: [0,2]}];
    
    // x cannot be PENDING (1)
    State x = 1 [(validate.rules).enum = {not_in: [1]}];

Messages

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.

// if Person was generated with PGV and x is set,
// x's fields will be validated.
    Person x = 1;
  • skip: this rule specifies that the validation rules of this field should not be evaluated.

    // The fields on Person x will not be validated.
    Person x = 1 [(validate.rules).message.skip = true];
  • required: this rule specifies that the field cannot be unset.

    // x cannot be unset
    Person x = 1 [(validate.rules).message.required = true];
    
    // x cannot be unset, but the validations on x will not be performed
    Person x = 1 [(validate.rules).message = {required: true, skip: true}];

Repeated

  • min_items/max_items: these rules control how many elements are contained in the field

    // x must contain at least 3 elements
    repeated int32 x = 1 [(validate.rules).repeated.min_items = 3];
    
    // x must contain between 5 and 10 Persons, inclusive
    repeated Person x = 1 [(validate.rules).repeated = {min_items: 5, max_items: 10}];
    
    // x must contain exactly 7 elements
    repeated double x = 1 [(validate.rules).repeated = {min_items: 7, max_items: 7}];
  • unique: this rule requires that all elements in the field must be unique. This rule does not support repeated messages.

    // x must contain unique int64 values
    repeated int64 x = 1 [(validate.rules).repeated.unique = true];
  • 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.

    // x must contain positive float values
    repeated float x = 1 [(validate.rules).repeated.items.float.gt = 0];
    
    // x must contain Persons but don't validate them
    repeated Person x = 1 [(validate.rules).repeated.items.message.skip = true];
  • ignore_empty: this rule specifies that if field is empty or set to the default value, to ignore any validation rules. These are typically useful where being able to unset a field in an update request, or to skip validation for optional fields where switching to WKTs is not feasible.

    repeated int64 x = 1 [(validate.rules).repeated = {ignore_empty: true, items: {int64: {gt: 200}}}];

Maps

  • min_pairs/max_pairs: these rules control how many KV pairs are contained in this field

    // x must contain at least 3 KV pairs
    map<string, uint64> x = 1 [(validate.rules).map.min_pairs = 3];
    
    // x must contain between 5 and 10 KV pairs
    map<string, string> x = 1 [(validate.rules).map = {min_pairs: 5, max_pairs: 10}];
    
    // x must contain exactly 7 KV pairs
    map<string, Person> x = 1 [(validate.rules).map = {min_pairs: 7, max_pairs: 7}];
  • no_sparse: for map fields with message values, setting this rule to true disallows keys with unset values.

    // all values in x must be set
    map<uint64, Person> x = 1 [(validate.rules).map.no_sparse = true];
  • keys: this rule specifies constraints that are applied to the keys in the field.

    // x's keys must all be negative
    <sint32, string> x = [(validate.rules).map.keys.sint32.lt = 0];
  • 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.

    // x must contain strings of at least 3 characters
    map<string, string> x = 1 [(validate.rules).map.values.string.min_len = 3];
    
    // x must contain Persons but doesn't validate them
    map<string, Person> x = 1 [(validate.rules).map.values.message.skip = true];
  • ignore_empty: this rule specifies that if field is empty or set to the default value, to ignore any validation rules. These are typically useful where being able to unset a field in an update request, or to skip validation for optional fields where switching to WKTs is not feasible.

    map<string, string> x = 1 [(validate.rules).map = {ignore_empty: true, values: {string: {min_len: 3}}}];

Well-Known Types (WKTs)

A set of WKTs are packaged with protoc and common message patterns useful in many domains.

Scalar Value Wrappers

In 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.

// if it is set, x must be greater than 3
    google.protobuf.Int32Value x = 1 [(validate.rules).int32.gt = 3];

Message Rules can also be used with scalar Well-Known Types (WKTs):

// Ensures that if a value is not set for age, it would not pass the validation despite its zero value being 0.
message X {google.protobuf.Int32Value age = 1 [(validate.rules).int32.gt = -1, (validate.rules).message.required = true];}

Anys

  • required: this rule specifies that the field must be set

    // x cannot be unset
    google.protobuf.Any x = 1 [(validate.rules).any.required = true];
  • in/not_in: these two rules permit specifying allow/denylists for the type_url value in this field. Consider using a oneof union instead of in if possible.

    // x must not be the Duration or Timestamp WKT
    google.protobuf.Any x = 1 [(validate.rules).any = {not_in: [
        "type.googleapis.com/google.protobuf.Duration",
        "type.googleapis.com/google.protobuf.Timestamp"
      ]}];

Durations

  • required: this rule specifies that the field must be set

    // x cannot be unset
    google.protobuf.Duration x = 1 [(validate.rules).duration.required = true];
  • const: the field must be exactly the specified value.

    // x must equal 1.5s exactly
    google.protobuf.Duration x = 1 [(validate.rules).duration.const = {
        seconds: 1,
        nanos:   500000000
      }];
  • lt/lte/gt/gte: these inequalities (<, <=, >, >=, respectively) allow for deriving ranges in which the field must reside.

    // x must be less than 10s
    google.protobuf.Duration x = 1 [(validate.rules).duration.lt.seconds = 10];
    
    // x must be greater than or equal to 20ns
    google.protobuf.Duration x = 1 [(validate.rules).duration.gte.nanos = 20];
    
    // x must be in the range [0s, 1s)
    google.protobuf.Duration x = 1 [(validate.rules).duration = {
        gte: {},
        lt:  {seconds: 1}
      }];

    Inverting the values of lt(e) and gt(e) is valid and creates an exclusive range.

    // x must be outside the range [0s, 1s)
    google.protobuf.Duration x = 1 [(validate.rules).duration = {
        lt:  {},
        gte: {seconds: 1}
      }];
  • in/not_in: these two rules permit specifying allow/denylists for the values of a field.

    // x must be either 0s or 1s
    google.protobuf.Duration x = 1 [(validate.rules).duration = {in: [
        {},
        {seconds: 1}
      ]}];
    
    // x cannot be 20s nor 500ns
    google.protobuf.Duration x = 1 [(validate.rules).duration = {not_in: [
        {seconds: 20},
        {nanos: 500}
      ]}];

Timestamps

  • required: this rule specifies that the field must be set

    // x cannot be unset
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.required = true];
  • const: the field must be exactly the specified value.

    // x must equal 2009/11/10T23:00:00.500Z exactly
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.const = {
        seconds: 63393490800,
        nanos:   500000000
      }];
  • lt/lte/gt/gte: these inequalities (<, <=, >, >=, respectively) allow for deriving ranges in which the field must reside.

    // x must be less than the Unix Epoch
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt.seconds = 0];
    
    // x must be greater than or equal to 2009/11/10T23:00:00Z
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.gte.seconds = 63393490800];
    
    // x must be in the range [epoch, 2009/11/10T23:00:00Z)
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
        gte: {},
        lt:  {seconds: 63393490800}
      }];

    Inverting the values of lt(e) and gt(e) is valid and creates an exclusive range.

    // x must be outside the range [epoch, 2009/11/10T23:00:00Z)
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
        lt:  {},
        gte: {seconds: 63393490800}
      }];
  • lt_now/gt_now: these inequalities allow for ranges relative to the current time. These rules cannot be used with the absolute rules above.

    // x must be less than the current timestamp
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.lt_now = true];
  • 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.

    // x must be within ยฑ1s of the current time
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp.within.seconds = 1];
    
    // x must be within the range (now, now+1h)
    google.protobuf.Timestamp x = 1 [(validate.rules).timestamp = {
        gt_now: true,
        within: {seconds: 3600}
      }];

Message-Global

  • disabled: All validation rules for the fields on a message can be nullified, including any message fields that support validation themselves.

    message Person {
      option (validate.disabled) = true;
    
      // x will not be required to be greater than 123
      uint64 x = 1 [(validate.rules).uint64.gt = 123];
    
      // y's fields will not be validated
      Person y = 2;
    }
  • ignored: Don't generate a validate method or any related validation code for this message.

    message Person {
      option (validate.ignored) = true;
    
      // x will not be required to be greater than 123
      uint64 x = 1 [(validate.rules).uint64.gt = 123];
    
      // y's fields will not be validated
      Person y = 2;
    }

OneOfs

  • 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.

    oneof id {
      // either x, y, or z must be set.
      option (validate.required) = true;
    
      string x = 1;
      int32  y = 2;
      Person z = 3;
    }

Development

PGV is written in Go on top of the protoc-gen-star framework and compiles to a standalone binary.

Dependencies

All PGV dependencies are currently checked into the project. To test PGV, protoc must be installed, either from source, the provided releases, or a package manager. The official protoc plugin for the target language(s) should be installed as well.

Make Targets

  • make build: generates the constraints proto and compiles PGV into $GOPATH/bin

  • make lint: runs static-analysis rules against the PGV codebase, including golint, go vet, and gofmt -s

  • make testcases: generates the proto files in /tests/harness/cases. These are used by the test harness to verify the validation rules generated for each language.

  • make harness: executes the test-cases against each language's test harness.

Run all tests under Bazel

Ensure that your PATH is setup to include protoc-gen-go and protoc, then:

bazel test //tests/...

Docker

PGV comes with a Dockerfile for consistent development tooling and CI. The main entrypoint is make with build as the default target.

# build the image
docker build -t bufbuild/protoc-gen-validate .

# executes the default make target: build
docker run --rm \
  bufbuild/protoc-gen-validate

# executes the 'ci' make target
docker run --rm \
  bufbuild/protoc-gen-validate ci

# executes the 'build' & 'testcases' make targets
docker run --rm \
  bufbuild/protoc-gen-validate build testcases

# override the entrypoint and interact with the container directly
# this can be useful when wanting to run bazel commands without
# bazel installed locally.
docker run --rm \
 -it --entrypoint=/bin/bash \
 bufbuild/protoc-gen-validate

protoc-gen-validate's People

Contributors

achasveachas avatar akonradi avatar asraa avatar codyaray avatar danielhochman avatar dependabot[bot] avatar dio avatar dominiquehunziker avatar elb3k avatar elliotmjackson avatar envoy-bot avatar fishcakez avatar fleshgrinder avatar htuch avatar keith avatar kothariaditya avatar kyessenov avatar lizan avatar mjduijn avatar mmorel-35 avatar pkwarren avatar reflejo avatar rmichela avatar rodaine avatar rubensf avatar securityinsanity avatar sesmith177 avatar shikugawa avatar wozz avatar wrowe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

protoc-gen-validate's Issues

[Suggest] About string validate

Hi:
While validate string, for example a token of 32 char in [a-zA-Z0-9], if set proto like :

message GetByPostRequest {
	string Token = 1 [(validate.rules).string = {pattern: "^[a-zA-Z\\d]{32}$", max_len:32, min_len:32}];
}

the generate go code will be like :

	if !_GetByPostRequest_Token_Pattern.MatchString(m.GetToken()) {
		return GetByPostRequestValidationError{
			Field:  "Token",
			Reason: "value does not match regex pattern \"^[a-zA-Z\\\\d]{32}$\"",
		}
	}

	if utf8.RuneCountInString(m.GetToken()) != 32 {
		return GetByPostRequestValidationError{
			Field:  "Token",
			Reason: "value length must be 32 runes",
		}
	}

The vaildate of pattern will always called before length check, if it can always provide validate for low cost before high cost would be nicer.

ps: is there any possibility to provide a len field instead of max_len:32, min_len:32?

Thanks a lot!

please add `option java_package`

I am working on envoy data plane on top of scala and I found issue when because of packages' naming conflict.

[error] /data-plane/target/scala-2.11/src_managed/main/protobuf-generated/validate/validate/AnyRules.scala:136: missing argument list for method validate in trait GeneratedMessageCompanion
[error] Unapplied methods are only converted to functions when a function type is expected.
[error] You can make this conversion explicit by writing `validate _` or `validate(_)` instead of `validate`.
[error]   lazy val defaultInstance = validate.validate.AnyRules(
[error]                              ^

so I suggest to have option java_package = "lyft.proto.validate"; in this project.

Clean up generated code

The generated validation code is not very well formatted, so either the templates should be modified to make it look nice or there should be a post-processing pass to auto-format it.

Python Support

Any plan to support python or do you accept python support PR?

Thanks for the awesome project!

Validation over multiple fields

I have a message like this :

message Request {
    repeated NewRequest add = 1;
    repeated RemoveRequest remove = 1;
}

I would like to validate if in the json there's at least one item of the 2 repeated and if in the json the field is called really add or remove (check for example the camel cases).

{
 "add": [{
 }]
}

and not like this for example:

{
 "Add": [{
 }]
}

Unable to build envoy behind a proxy

I'm encountering the following error when building envoy behind a proxy, I believe the issue is due to dependencies of protoc-gen-validate.

ERROR: /usr/mancini/.cache/bazel/_bazel_mancini/b1b7ee4f2abe773f993beb4c85591835/external/com_lyft_protoc_gen_validate/BUILD:18:1: no such package '@com_github_golang_protobuf//ptypes/wrappers': failed to fetch com_github_golang_protobuf: # cd .; git clone https://github.com/golang/protobuf /usr/mancini/.cache/bazel/_bazel_mancini/b1b7ee4f2abe773f993beb4c85591835/external/com_github_golang_protobuf
Cloning into '/usr/mancini/.cache/bazel/_bazel_mancini/b1b7ee4f2abe773f993beb4c85591835/external/com_github_golang_protobuf'...
fatal: unable to access 'https://github.com/golang/protobuf/': SSL certificate problem: unable to get local issuer certificate
2018/05/21 11:29:30 exit status 128
and referenced by '@com_lyft_protoc_gen_validate//:go_google_protobuf'

I have set the HTTP_PROXY and HTTPS_PROXY environment variables, and I've even configured git to disable ssl verification, and have imported the github certificate into my java keystore. Other git repositories required by the build are able to be cloned without issue.

The build is unable to clone https://github.com/golang/protobuf, which is a dependency of com_lyft_protoc_gen_validate.

I see the following issues associated with building behind a proxy:

#57 (reference)
#59
envoyproxy/envoy#2578
bazelbuild/rules_go#1053

, all of which seem to have been resolved, yet this issue persists.

I am able to clone https://github.com/golang/protobuf on that box from the command line, just not during the build. I would just override this repository in my WORKSPACE, and point it to a local repository, but it is a non-bazel repository, so I would need to know the BUILD file associated with it.

Does anybody have any suggestions?

Cross package enum validation is broken for Go and C++

See https://github.com/htuch/protoc-gen-validate/tree/enum-other-package (run make bazel-harness).

For Go, the issue is that the imports in .validate.go do not include the package where the enum is located, tests_harness_cases_other_package.TestEnum_name. i.e. test_harness_cases_other_package is missing. This support can be added to templates/go/file.go with something like:

diff --git a/templates/go/file.go b/templates/go/file.go
index da898b6..3e0afc3 100644
--- a/templates/go/file.go
+++ b/templates/go/file.go
@@ -19,6 +19,9 @@ import (
        "unicode/utf8"
 
        "github.com/golang/protobuf/ptypes"
+{{ range .Imports }}
+       {{ .GoName.String }} "{{ .ImportPath }}"
+{{ end }}
 )

However, this confuses the Go compiler unless the go_package option in the protos is set to the fully qualified path, which we can't do today due to #41.

There is a similar issue in the C++ templates, but rather than being includes/imports, I think it's just a question of modifying templates/cc/enum.go to use cType instead of $f.Type.Name.Element.

String OR validation

Is it possible to validate a string field that should be either empty (len == 0) or of len == 2. I don't see how that could be done today. Or am I mistaken? Would you accept a PR for such a feature?

Omitempty? Or some way to not validate default/zero-value?

Just about every single validation library I've seen in golang has let me opt out of validating a field if it is empty / the zero-value.

For example, here is what several common validation libraries use:

type Params struct {
        ID              string    `validate:"len=32"`
	CountryCode     string    `validate:"omitempty,len=2"`
	Size            int       `validate:"omitempty,gt=10"`
}

This means:
Validate ID is length 32 and not empty.
If CountryCode is not empty, validate the length is 2.
If Size is not zero, validate the length is greater than 10.

Can we please have some way to specify, on every single validation option and type you have, to ignore the zero-value?

This is the only thing stopping me from making full use of this library.
My use case is that I serve GRPC with the REST Gateway, and I have several proto messages that have both optional (zero value = ok and ignored) and required parameters. For the optional parameters, I'd still like to validate them if they aren't empty.

Proposed format:

string email = 1 [(validate.rules).string = { omitempty: true, email: true}];
double lat   = 1 [(validate.rules).double = { omitempty: true, gte: -90,  lte: 90 }];

Trouble to run with the example

Hi, I'm trying PGV using the example provided. However, I got the following error while attempting to get the generated output.

$ protoc -I . -I /Users/xiaolan/go/src/protobuf-3.5.1/src --go_out=":../generated" --validate_out="lang=go:../generated" example.proto
2018/02/01 23:05:13 protoc-gen-go: error:can't find object with type ..Person
--validate_out: protoc-gen-validate: Plugin failed with status code 1.

Note that the generated code without --validate_out works. Anybody can help? thanks

Port to arm64

In envoyproxy/envoy#1861 (comment) , @moderation is attempting a port of Envoy to arm64. After getting pretty far they got stuck in a dependency on lyft/protoc-gen-validate.

I suspect that one of the issues is a hardcoded dependency to arm64 binaries in the build_container script at

https://github.com/lyft/protoc-gen-validate/blob/master/scripts/build_container.sh#L18
https://github.com/lyft/protoc-gen-validate/blob/master/scripts/build_container.sh#L38

However there may be more issues lurking; using this issue to track them.

Allow conditional validation on embed messages

Is it a good idea to have conditional validation on embed messages ?

Here is an example

syntax = "proto3";

package example;

import "google/api/annotations.proto";

service Example {
    rpc CreateExample (CreateExampleRequest) returns (CreateExampleResponse) {
            option (google.api.http) = {
            post: "/api/example/create"
            body: "*"
        };
    }
    rpc UpdateExample (UpdateExampleRequest) returns (UpdateExampleResponse) {
        option (google.api.http) = {
            post: "/api/example/update"
            body: "*"
        };
    }
}

// REQUESTS
message CreateExampleRequest {
    //validation on this object is different than validation on UpdateExampleRequest.Example object
    Example e = 1;
}
message UpdateExampleRequest {
    Example e = 1;
}

//RESPONSES
message CreateExampleResponse {
    string id = 1;
}
message UpdateExampleResponse {
    string id = 1;
}

message Example {
    //add validation option related to outterMessage (CreateExampleRequest, UpdateExampleRequest)

    string id = 1;
    int64 version = 2;
    string created_at = 3;
}

Add support for import_path parameter

The protoc-gen-go plugin supports an import_path parameter defined as

import_path=foo/bar - used as the package if no input files declare go_package. If it contains slashes, everything up to the rightmost slash is ignored.

https://github.com/golang/protobuf#parameters

The package name is set by protoc-gen-star:

https://github.com/lyft/protoc-gen-star/blob/f3b83fca61817e55058744eb1a3c8e2e95a0dada/gatherer.go#L54

The GoPackageName method is defined in protoc-gen-go, but the import_path parameter does not seem to get passed through:

https://github.com/golang/protobuf/blob/9eb2c01ac278a5d89ce4b2be68fe4500955d8179/protoc-gen-go/generator/generator.go#L530-L541

Dependency on "context" means that minimum version of Go is >= 1.7

An attempt to build with Go 1.6.3 (as provided by Ubuntu 16.04 LTS) fails with

package context: unrecognized import path "context" (import path does not begin with hostname)

If there is a minimum version of Go required for a successful build of protoc-gen-validate, it should be included in the README notes.

Add Mac address validation to string well-known format

This is feature request
Add Mac address validation for string type using next Golang code.

import "net"
// func ParseMAC(s string) (hw HardwareAddr, err error)
if _, err := net.ParseMAC(macAddress); err != nil {
    return Error("Mac is invalid")
}

WKT StringValue doesn't support validation

This is bug.

What I try to solve
Add optional domain_name field with validation for hostname validity.

I found that this lib doesn't support omitting optional value for generic string type: #102.

Generic string type

// Proto file field
string domain_name = 28 [(validate.rules).string.hostname = true];
// Generated code
...
        if err := m._validateHostname(m.GetDomainName()); err != nil {
		return OnPremHostValidationError{
			field:  "DomainName",
			reason: "value must be a valid hostname",
			cause:  err,
		}
	}
...
func (m *OnPremHost) _validateHostname(host string) error {
	s := strings.ToLower(strings.TrimSuffix(host, "."))

	if len(host) > 253 {
		return errors.New("hostname cannot exceed 253 characters")
	}

	for _, part := range strings.Split(s, ".") {
		if l := len(part); l == 0 || l > 63 {
			return errors.New("hostname part must be non-empty and cannot exceed 63 characters")
		}

		if part[0] == '-' {
			return errors.New("hostname parts cannot begin with hyphens")
		}

		if part[len(part)-1] == '-' {
			return errors.New("hostname parts cannot end with hyphens")
		}

		for _, r := range part {
			if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {
				return fmt.Errorf("hostname parts can only contain alphanumeric characters or hyphens, got %q", string(r))
			}
		}
	}

	return nil
}

This is ok, but it doesn't support empty value omitting.

Then I tried to use WKT google.protobuf.StringValue type for domain_name field. As this library description suggests in such case. But it doesn't work as well. See details below.

WKT google.protobuf.StringValue type

// Proto file field
import "google/protobuf/wrappers.proto";
google.protobuf.StringValue domain_name = 28 [(validate.rules).string.hostname = true];
// Generated code
...
if wrapper := m.GetDomainName(); wrapper != nil {

	if err := m._validateHostname(wrapper.GetValue()); err != nil {
		return OnPremHostValidationError{
			field:  "DomainName",
			reason: "value must be a valid hostname",
			cause:  err,
		}
	}

}
...

That's it! Method _validateHostname is not generated. Therefore code cannot be compiled. The same issue is reproducible for all WKT StringValue type values with separate validation method. It is not generated at all.

PGV at gRPConf?

CNCF is hosting gRPConf on March 21st in Sunnyvale, CA and has an open CFP. We should submit a joint presentation on PGV for the Ecosystem and Tooling track. Thoughts?

Add UUIDv4 to well-known formats

Heya! First, thanks for working on PGV, I like it a lot. ๐Ÿ‘

It would be nice to be able to say

string id = 1 [(validate.rules).string.uuidv4 = true];

instead of

string id = 1 [(validate.rules).string.pattern = "^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-4[a-fA-F0-9]{3}-[8|9|aA|bB][a-fA-F0-9]{3}-[a-fA-F0-9]{12}$"];

What would you think? I'd be happy to contribute that. ๐Ÿ˜ƒ

Escaping regex asterisk (per RE2 syntax)

With a regex pattern including a \* protoc returns "Invalid escape sequence in string literal."
Thinking it might need an extra level of escaping, tried \\* which got through the generation, but in the generated output, the regex now includes: \\\\* which is not the desired output.

The only workaround my team has found to escape a literal asterisk is [*]{1}. That works, but it is cumbersome. Hoping there is some other secret escape mechanism that would allow \* to work...?

Investigate using Bazel toolchain support to avoid forking rules_go

Today, we fork https://github.com/bazelbuild/rules_go to add our own variant of go_proto_library with the ability to use the PGV plugin for code generation, see:

rules_go recently gained the ability to work with proto_library and custom toolchains, see https://github.com/bazelbuild/rules_go/blob/master/proto/toolchain.bzl. We might be able to avoid forking as a result and have a more maintainable solution going forward if we switch to this model.

Customized error messages on validation failures

Is it possible to specify a custom message for validation failures? For example, if a min_length validation fails, I would like to return a reason that doesn't reference 'runes'. Or be able to understand the error message returned is a minimum length violation, and translate that to my own messaging.

I don't see a way to do either; i don't see a way to specify a custom message in the validation option in the proto file, and the error returned is specific to the message, but not (even more) specific to the min_len validation.

Thanks!

[feature request] Validate should collect all errors before returning

The example in the README shows that Validate stops on the first error that occurs, however it would be more useful to gather the full set of errors and return it rather than just the first in the case of multiple errors.

A basic example:

type MultiError []error

func (m *MultiError) Error() string {
  s := make([]string, len(*m))
  for i, e := range *m {
    s[i] = e.Error()
  }
  return strings.Join(s, "\n")
}

This enables collecting all errors to be reported as well as inspection if needed:

err := m.Validate()
if err != nil {
  if merr, ok := err.(*MultiError); ok {
    for _, me := range *merr {
      // One error at a time..
    }
  }
}

Support for gogoproto

It seems that this tool only supports the standard goproto code generation output. I have noticed the following deviations for gogo:

  • disregard of go_package annotation that allows multiple proto files to be combined into one package, to avoid circular dependencies between packages
  • no support for gogoproto code generation optimization annotations:
    • gogoproto.nullable changes a pointer to an embedded struct for fields that are not nullable by construction
    • gogoproto.stdduration changes a duration from ptypes.Duration to time.Duration

What needs to be done to extend PGV with gogo support?

Cross-file C++ validation requires manual header specification

Due to the headers-with-function-inlining approach we've adopted, if we have a proto A that depends on B and C, it's necessary to manually specify:

#include "b.pb.validate.h"
#include "c.pb.validate.h"
#include "a.pb.validate.h"

to make use of A's validate capabilities. We're not doing transitive include generation. This is actually super error prone, as not getting a correct topological sort can result in false positive proto validations.

If you simply #include <imported .proto>.pb.validate.h in the PGV C++ template, this fails, since it also picks up things like WKT types that the validator does not run against.

Add no_sparse rule for repeated message fields

Map fields with message values support:

... [(validate.rules).map.values.message.required = true];

However, the no_sparse rule is, arguably, more clear:

... [(validate.rules).map.no_sparse = true]

Similarly, repeated message fields support:

... [(validate.rules).repeated.items.message.required = true];

Would it be reasonable to add a no_sparse rule for repeated fields. For example:

... [(validate.rules).repeated.no_sparse = true]

Nested field validation?

It seems the generated code's Validate() only validates the current object. It should recurse into nested fields too.

Include generated `validate/validate.pb.go`

This repository is not friendly to standard go dependency management tools.
The recent change to envoy/data-plane-api adds an import on github.com/lyft/protoc-gen-validate/validate to generated pb.go files (this repository). Golang tools expect to find generated sources in the repo, which are non-existent.

enum: fail to import cross packages

If your proto files are in different packages, the generated code does not correctly import the packages it needs. I noticed this with enums. Here is s simple test case:

// foo/foo.proto
syntax = "proto3";

package foo;
option go_package = "foo";

enum Foo {
    ZERO = 0;
    ONE =  1;
}
// bar.proto
syntax = "proto3";

package bar;
option go_package = "bar";

import "validate/validate.proto";
import "foo/foo.proto";

message Bar {
   foo.Foo foo = 1 [(validate.rules).enum = { in: [1] }];
}

run with:

protoc -I$GOPATH/src/github.com/lyft/protoc-gen-validate -I. --go_out=. --validate_out=lang=go:. bar.proto

You'll note that the generated code imports foo:

var _Bar_Foo_InLookup = map[foo.Foo]struct{}{
	1: {},
}

But does not import it. This is because the imports is currently static: https://github.com/lyft/protoc-gen-validate/blob/master/templates/go/file.go

How to represent regular expressions

The current defacto regular expression implementation is the one used by Go, which uses the re2 syntax. It isn't POSIX-compliant, nor is it immediately compatible with C++'s std::basic_regex and friends. This shows up most obviously when trying to use flags (. matches newline, case-insensitive matching, etc.) to modify the matching behavior: Go encodes these as part of the expression string while C++ uses a separate bitmask.

make validate.Module() importable

Hey there!

Thanks again for this lovely plugin. ๐ŸŽ‰

I've just run into a road block with testing some protoc-related stuff via go test. Specifically, I haven't found a way to run the validate module from code, as I cannot import it:

Trying to do

import validate "github.com/lyft/protoc-gen-validate"

gives me

import "github.com/lyft/protoc-gen-validate" is a program, not an importable package

By any chance, would you agree to a PR moving that module into validate/?

Add proto field name to validation error

Right now, only go field name present. If this validation error, for instance, is used to construct rpc validation error message (like google.rpc.BadRequest), it will be better to put field name from protofile.

Any proto file that uses this package can't generate PHP code because this project is still proto2

Our servers are all in golang, but some of our clients are in PHP, so we generate PHP libraries for all of our proto files.

If you use protoc-gen-validate, you would have this import in your proto file:
import "protoc-gen-validate/validate/validate.proto";

If you generate PHP out of your proto file, you will end up with a file that contains this:

<?php
# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: v1.proto

namespace GPBMetadata;
class V1
{
    public static $is_initialized = false;
    public static function initOnce() {
        $pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
        if (static::$is_initialized == true) {
          return;
        }
        \GPBMetadata\Google\Api\Annotations::initOnce();
        \GPBMetadata\Google\Protobuf\Timestamp::initOnce();
        \GPBMetadata\ProtocGenValidate\Validate\Validate::initOnce();

If you try to use this PHP code, you will need to have the php library \GPBMetadata\ProtocGenValidate\Validate\Validate

I solved this for other proto files by just generating them, so I took the same approach here:

$ protoc -I=./include -I=./vendor/github.com/lyft --plugin=protoc-gen-grpc=/go/src/github.com/grpc/bins/opt/grpc_php_plugin --grpc_out=./php/protoc-gen-validate --php_out=./php/protoc-gen-validate /vendor/github.com/lyft/protoc-gen-validate/validate/validate.proto
--php_out: protoc-gen-validate/validate/validate.proto: Can only generate PHP code for proto3 .proto files.
Please add 'syntax = "proto3";' to the top of your .proto file.

This fails because php can only generate files for proto3.

gogoproto cannot compile validate.proto

Attempting to apply gogoproto to data-plane-api, getting these errors:

WARNING: field BytesRules.Const is a non-nullable bytes type, nullable=false has no effect
WARNING: field BytesRules.Prefix is a non-nullable bytes type, nullable=false has no effect
WARNING: field BytesRules.Suffix is a non-nullable bytes type, nullable=false has no effect
WARNING: field BytesRules.Contains is a non-nullable bytes type, nullable=false has no effect
ERROR: field StringRules.Email cannot be in an oneof and a non-nullable field
--gogoslick_out: protoc-gen-gogoslick: Plugin failed with status code 1.

gogo claims it's an incorrect use of oneof field.

cc @htuch

Java code generation

What would it take to add Java code generation to protoc-gen-validate? Looking at the code, it looks like

  • Implement a new set of java-specific templates in /templates/java
  • Add tests

Is Java support something you are interested in?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.