validate: initial constraints support. (#226)

* Import @com_lyft_protoc_gen_validate for validate.proto annotations.

* Example annotation in address.proto (BindConfig).

* Process optional/required annotations in protodoc.

Signed-off-by: Harvey Tuch <htuch@google.com>
pull/228/head
htuch 7 years ago committed by GitHub
parent 7d9e737e85
commit 29278b52a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 2
      WORKSPACE
  3. 2
      api/BUILD
  4. 4
      api/address.proto
  5. 6
      bazel/api_build_system.bzl
  6. 9
      bazel/repositories.bzl
  7. 2
      ci/do_ci.sh
  8. BIN
      tools/generate_listeners.pyc
  9. 5
      tools/protodoc/BUILD
  10. 34
      tools/protodoc/protodoc.py

1
.gitignore vendored

@ -1,3 +1,4 @@
bazel-*
*.pyc
build_docs/
generated/

@ -42,6 +42,8 @@ git_repository(
commit = "4374be38e9a75ff5957c3922adb155d32086fe14",
)
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
load("@com_lyft_protoc_gen_validate//bazel:go_proto_library.bzl", "go_proto_repositories")
go_proto_repositories(shared=0)
go_rules_dependencies()
go_register_toolchains()
load("@io_bazel_rules_go//proto:def.bzl", "proto_register_toolchains")

@ -30,6 +30,7 @@ proto_library(
"@com_google_protobuf//:struct_proto",
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:wrappers_proto",
"@com_lyft_protoc_gen_validate//validate:validate_proto",
"@googleapis//:http_api_protos_lib",
],
)
@ -46,6 +47,7 @@ go_grpc_library(
"@com_github_golang_protobuf//ptypes/timestamp:go_default_library",
"@com_github_golang_protobuf//ptypes/wrappers:go_default_library",
"@org_golang_google_genproto//googleapis/api/annotations:go_default_library",
"@com_lyft_protoc_gen_validate//validate:go_default_library",
],
)

@ -4,6 +4,8 @@ package envoy.api.v2;
import "google/protobuf/wrappers.proto";
import "validate/validate.proto";
// protodoc-title: Network related configuration elements
// [V2-API-DIFF] Addresses now have .proto structure.
@ -37,7 +39,7 @@ message SocketAddress {
message BindConfig {
// The address to bind to when creating a socket.
SocketAddress source_address = 1;
SocketAddress source_address = 1 [(validate.rules).message.required = true];
}
// Addresses specify either a logical or physical address and port, which are

@ -17,7 +17,10 @@ def api_py_proto_library(name, srcs = [], deps = [], has_services = 0):
srcs = srcs,
default_runtime = "@com_google_protobuf//:protobuf_python",
protoc = "@com_google_protobuf//:protoc",
deps = [_PySuffix(d) for d in deps] + ["@googleapis//:http_api_protos_py"],
deps = [_PySuffix(d) for d in deps] + [
"@com_lyft_protoc_gen_validate//validate:validate_py",
"@googleapis//:http_api_protos_py",
],
visibility = ["//visibility:public"],
)
@ -35,6 +38,7 @@ def api_proto_library(name, srcs = [], deps = [], has_services = 0, require_py =
"@com_google_protobuf//:timestamp_proto",
"@com_google_protobuf//:wrappers_proto",
"@googleapis//:http_api_protos_lib",
"@com_lyft_protoc_gen_validate//validate:validate_proto",
],
visibility = ["//visibility:public"],
)

@ -1,7 +1,16 @@
GOOGLEAPIS_SHA = "5c6df0cd18c6a429eab739fb711c27f6e1393366" # May 14, 2017
PROMETHEUS_SHA = "6f3806018612930941127f2a7c6c453ba2c527d2" # Nov 02, 2017
PGV_GIT_SHA = "f3332cbd75bb28f711377dfb84761ef0d52eca0f"
PGV_TAR_SHA = "039ffa842eb62495b6aca305a4eb3d6dc3ac1dd056e228fba5e720161ddfb9c1"
def api_dependencies():
native.http_archive(
name = "com_lyft_protoc_gen_validate",
strip_prefix = "protoc-gen-validate-" + PGV_GIT_SHA,
sha256 = PGV_TAR_SHA,
url = "https://github.com/lyft/protoc-gen-validate/archive/" + PGV_GIT_SHA + ".tar.gz",
)
native.new_http_archive(
name = "googleapis",
strip_prefix = "googleapis-" + GOOGLEAPIS_SHA,

@ -17,6 +17,6 @@ elif [[ "$1" == "bazel.docs" ]]; then
echo "generating docs..."
./docs/build.sh
else
echo "Invalid do_ci.sh target. The only valid target is bazel.build."
echo "Invalid do_ci.sh target. The only valid targets are bazel.{docs,test}."
exit 1
fi

Binary file not shown.

@ -3,6 +3,9 @@ licenses(["notice"]) # Apache 2
py_binary(
name = "protodoc",
srcs = ["protodoc.py"],
deps = ["@com_google_protobuf//:protobuf_python"],
deps = [
"@com_lyft_protoc_gen_validate//validate:validate_py",
"@com_google_protobuf//:protobuf_python",
],
visibility = ["//visibility:public"],
)

@ -10,6 +10,7 @@ import sys
import re
from google.protobuf.compiler import plugin_pb2
from validate import validate_pb2
# Namespace prefix for Envoy APIs.
ENVOY_API_NAMESPACE_PREFIX = '.envoy.api.v2.'
@ -23,6 +24,7 @@ UNICODE_INVISIBLE_SEPARATOR = u'\u2063'
# Page/section titles with special prefixes in the proto comments
DOC_TITLE_REGEX = 'protodoc-title:\s([^\n]+)\n\n?'
class ProtodocError(Exception):
"""Base error class for the protodoc module."""
@ -151,9 +153,11 @@ def FormatHeaderFromFile(style, file_level_comment, alt):
m = re.search(DOC_TITLE_REGEX, file_level_comment)
if m:
# remove title hint and any new lines that follow
return FormatHeader(style, m.group(1)), re.sub(DOC_TITLE_REGEX, '', file_level_comment)
return FormatHeader(style, m.group(1)), re.sub(DOC_TITLE_REGEX, '',
file_level_comment)
return FormatHeader(style, alt), file_level_comment
def FormatFieldTypeAsJson(type_context, field):
"""Format FieldDescriptorProto.Type as a pseudo-JSON string.
@ -242,8 +246,14 @@ def FormatFieldType(type_context, field):
field.TYPE_DOUBLE: 'double',
field.TYPE_FLOAT: 'float',
field.TYPE_INT32: 'int32',
field.TYPE_SFIXED32: 'int32',
field.TYPE_SINT32: 'int32',
field.TYPE_FIXED32: 'uint32',
field.TYPE_UINT32: 'uint32',
field.TYPE_INT64: 'int64',
field.TYPE_SFIXED64: 'int64',
field.TYPE_SINT64: 'int64',
field.TYPE_FIXED64: 'uint64',
field.TYPE_UINT64: 'uint64',
field.TYPE_BOOL: 'bool',
field.TYPE_STRING: 'string',
@ -301,8 +311,15 @@ def FormatFieldAsDefinitionListItem(type_context, field):
else:
oneof_comment = ''
anchor = FormatAnchor(FieldCrossRefLabel(type_context.name))
comment = '(%s) ' % FormatFieldType(
type_context, field) + type_context.LeadingCommentPathLookup()
annotations = []
if field.options.HasExtension(validate_pb2.rules):
rule = field.options.Extensions[validate_pb2.rules]
if rule.HasField('message'):
if rule.message.required:
annotations.append('*REQUIRED*')
comment = '(%s) ' % ', '.join(
[FormatFieldType(type_context, field)] + annotations
) + type_context.LeadingCommentPathLookup()
return anchor + field.name + '\n' + MapLines(
functools.partial(Indent, 2), comment + oneof_comment)
@ -345,7 +362,8 @@ def FormatMessage(type_context, msg):
map(
functools.partial(FormatFieldType, type_context),
nested_msg.field))
for nested_msg in msg.nested_type if nested_msg.options.map_entry
for nested_msg in msg.nested_type
if nested_msg.options.map_entry
}
nested_msgs = '\n'.join(
FormatMessage(
@ -425,16 +443,16 @@ def GenerateRst(proto_file):
source_code_info = SourceCodeInfo(proto_file.source_code_info)
# Find the earliest detached comment, attribute it to file level.
# Also extract file level titles if any.
header, comment = FormatHeaderFromFile('=', source_code_info.file_level_comment,
proto_file.name)
header, comment = FormatHeaderFromFile(
'=', source_code_info.file_level_comment, proto_file.name)
msgs = '\n'.join(
FormatMessage(TypeContext(source_code_info, [4, index], msg.name), msg)
for index, msg in enumerate(proto_file.message_type))
enums = '\n'.join(
FormatEnum(TypeContext(source_code_info, [5, index], enum.name), enum)
for index, enum in enumerate(proto_file.enum_type))
#debug_proto = FormatProtoAsBlockComment(proto_file.source_code_info)
return header + comment + msgs + enums #+ debug_proto
debug_proto = FormatProtoAsBlockComment(proto_file)
return header + comment + msgs + enums #+ debug_proto
if __name__ == '__main__':

Loading…
Cancel
Save