diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index f4cc2ba40c2..940be4691f5 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -19,7 +19,10 @@
"bazel.buildifierExecutable": "/go/bin/buildifier",
"bazel.buildifierFixOnFormat": true,
"bazel.enableCodeLens": true,
- "extensions": ["golang.Go", "bazelbuild.vscode-bazel"]
+ "extensions": [
+ "golang.Go",
+ "bazelbuild.vscode-bazel"
+ ]
}
}
-}
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index c8791a64f37..2ba12195685 100644
--- a/Makefile
+++ b/Makefile
@@ -151,6 +151,10 @@ proto:
--template ./protoc-gen-openapiv2/options/buf.gen.yaml \
--path ./protoc-gen-openapiv2/options/annotations.proto \
--path ./protoc-gen-openapiv2/options/openapiv2.proto
+ buf generate \
+ --template ./protoc-gen-openapiv3/options/buf.gen.yaml \
+ --path ./protoc-gen-openapiv3/options/annotations.proto \
+ --path ./protoc-gen-openapiv3/options/openapiv3.proto
generate: proto $(ECHO_EXAMPLE_SRCS) $(ABE_EXAMPLE_SRCS) $(UNANNOTATED_ECHO_EXAMPLE_SRCS) $(RESPONSE_BODY_EXAMPLE_SRCS) $(GENERATE_UNBOUND_METHODS_EXAMPLE_SRCS)
diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index 28b6e25834b..145b1cf75f7 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -17,8 +17,10 @@ go_library(
"//internal/codegenerator",
"//internal/descriptor/apiconfig",
"//internal/descriptor/openapiconfig",
+ "//internal/descriptor/openapiconfigv3:openapiconfig",
"//internal/httprule",
"//protoc-gen-openapiv2/options",
+ "//protoc-gen-openapiv3/options",
"@in_gopkg_yaml_v3//:yaml_v3",
"@org_golang_google_genproto_googleapis_api//annotations",
"@org_golang_google_grpc//grpclog",
diff --git a/internal/descriptor/openapiconfigv3/BUILD.bazel b/internal/descriptor/openapiconfigv3/BUILD.bazel
new file mode 100644
index 00000000000..2fdba650d89
--- /dev/null
+++ b/internal/descriptor/openapiconfigv3/BUILD.bazel
@@ -0,0 +1,32 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+proto_library(
+ name = "openapiconfig_proto",
+ srcs = ["openapiconfig.proto"],
+ visibility = ["//:__subpackages__"],
+ deps = ["//protoc-gen-openapiv3/options:options_proto"],
+)
+
+go_proto_library(
+ name = "openapiconfig_go_proto",
+ compilers = ["//:go_apiv2"],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfigv3",
+ proto = ":openapiconfig_proto",
+ visibility = ["//:__subpackages__"],
+ deps = ["//protoc-gen-openapiv3/options"],
+)
+
+go_library(
+ name = "openapiconfig",
+ embed = [":openapiconfig_go_proto"],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfigv3",
+ visibility = ["//:__subpackages__"],
+)
+
+alias(
+ name = "go_default_library",
+ actual = ":openapiconfigv3",
+ visibility = ["//:__subpackages__"],
+)
diff --git a/internal/descriptor/openapiconfigv3/openapiconfig.pb.go b/internal/descriptor/openapiconfigv3/openapiconfig.pb.go
new file mode 100644
index 00000000000..4c700b32fda
--- /dev/null
+++ b/internal/descriptor/openapiconfigv3/openapiconfig.pb.go
@@ -0,0 +1,584 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.35.1
+// protoc (unknown)
+// source: internal/descriptor/openapiconfigv3/openapiconfig.proto
+
+package openapiconfigv3
+
+import (
+ options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// OpenAPIFileOption represents OpenAPI options on a file
+type OpenAPIFileOption struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ File string `protobuf:"bytes,1,opt,name=file,proto3" json:"file,omitempty"`
+ Option *options.Swagger `protobuf:"bytes,2,opt,name=option,proto3" json:"option,omitempty"`
+}
+
+func (x *OpenAPIFileOption) Reset() {
+ *x = OpenAPIFileOption{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIFileOption) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIFileOption) ProtoMessage() {}
+
+func (x *OpenAPIFileOption) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIFileOption.ProtoReflect.Descriptor instead.
+func (*OpenAPIFileOption) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *OpenAPIFileOption) GetFile() string {
+ if x != nil {
+ return x.File
+ }
+ return ""
+}
+
+func (x *OpenAPIFileOption) GetOption() *options.Swagger {
+ if x != nil {
+ return x.Option
+ }
+ return nil
+}
+
+// OpenAPIMethodOption represents OpenAPI options on a method
+type OpenAPIMethodOption struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"`
+ Option *options.Operation `protobuf:"bytes,2,opt,name=option,proto3" json:"option,omitempty"`
+}
+
+func (x *OpenAPIMethodOption) Reset() {
+ *x = OpenAPIMethodOption{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIMethodOption) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIMethodOption) ProtoMessage() {}
+
+func (x *OpenAPIMethodOption) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIMethodOption.ProtoReflect.Descriptor instead.
+func (*OpenAPIMethodOption) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *OpenAPIMethodOption) GetMethod() string {
+ if x != nil {
+ return x.Method
+ }
+ return ""
+}
+
+func (x *OpenAPIMethodOption) GetOption() *options.Operation {
+ if x != nil {
+ return x.Option
+ }
+ return nil
+}
+
+// OpenAPIMessageOption represents OpenAPI options on a message
+type OpenAPIMessageOption struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+ Option *options.Schema `protobuf:"bytes,2,opt,name=option,proto3" json:"option,omitempty"`
+}
+
+func (x *OpenAPIMessageOption) Reset() {
+ *x = OpenAPIMessageOption{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIMessageOption) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIMessageOption) ProtoMessage() {}
+
+func (x *OpenAPIMessageOption) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIMessageOption.ProtoReflect.Descriptor instead.
+func (*OpenAPIMessageOption) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *OpenAPIMessageOption) GetMessage() string {
+ if x != nil {
+ return x.Message
+ }
+ return ""
+}
+
+func (x *OpenAPIMessageOption) GetOption() *options.Schema {
+ if x != nil {
+ return x.Option
+ }
+ return nil
+}
+
+// OpenAPIServiceOption represents OpenAPI options on a service
+type OpenAPIServiceOption struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` // ex: Service
+ Option *options.Tag `protobuf:"bytes,2,opt,name=option,proto3" json:"option,omitempty"`
+}
+
+func (x *OpenAPIServiceOption) Reset() {
+ *x = OpenAPIServiceOption{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIServiceOption) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIServiceOption) ProtoMessage() {}
+
+func (x *OpenAPIServiceOption) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIServiceOption.ProtoReflect.Descriptor instead.
+func (*OpenAPIServiceOption) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *OpenAPIServiceOption) GetService() string {
+ if x != nil {
+ return x.Service
+ }
+ return ""
+}
+
+func (x *OpenAPIServiceOption) GetOption() *options.Tag {
+ if x != nil {
+ return x.Option
+ }
+ return nil
+}
+
+// OpenAPIFieldOption represents OpenAPI options on a field
+type OpenAPIFieldOption struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
+ Option *options.JSONSchema `protobuf:"bytes,2,opt,name=option,proto3" json:"option,omitempty"`
+}
+
+func (x *OpenAPIFieldOption) Reset() {
+ *x = OpenAPIFieldOption{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIFieldOption) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIFieldOption) ProtoMessage() {}
+
+func (x *OpenAPIFieldOption) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIFieldOption.ProtoReflect.Descriptor instead.
+func (*OpenAPIFieldOption) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{4}
+}
+
+func (x *OpenAPIFieldOption) GetField() string {
+ if x != nil {
+ return x.Field
+ }
+ return ""
+}
+
+func (x *OpenAPIFieldOption) GetOption() *options.JSONSchema {
+ if x != nil {
+ return x.Option
+ }
+ return nil
+}
+
+// OpenAPIOptions represents OpenAPI protobuf options
+type OpenAPIOptions struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ File []*OpenAPIFileOption `protobuf:"bytes,1,rep,name=file,proto3" json:"file,omitempty"`
+ Method []*OpenAPIMethodOption `protobuf:"bytes,2,rep,name=method,proto3" json:"method,omitempty"`
+ Message []*OpenAPIMessageOption `protobuf:"bytes,3,rep,name=message,proto3" json:"message,omitempty"`
+ Service []*OpenAPIServiceOption `protobuf:"bytes,4,rep,name=service,proto3" json:"service,omitempty"`
+ Field []*OpenAPIFieldOption `protobuf:"bytes,5,rep,name=field,proto3" json:"field,omitempty"`
+}
+
+func (x *OpenAPIOptions) Reset() {
+ *x = OpenAPIOptions{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIOptions) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIOptions) ProtoMessage() {}
+
+func (x *OpenAPIOptions) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIOptions.ProtoReflect.Descriptor instead.
+func (*OpenAPIOptions) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *OpenAPIOptions) GetFile() []*OpenAPIFileOption {
+ if x != nil {
+ return x.File
+ }
+ return nil
+}
+
+func (x *OpenAPIOptions) GetMethod() []*OpenAPIMethodOption {
+ if x != nil {
+ return x.Method
+ }
+ return nil
+}
+
+func (x *OpenAPIOptions) GetMessage() []*OpenAPIMessageOption {
+ if x != nil {
+ return x.Message
+ }
+ return nil
+}
+
+func (x *OpenAPIOptions) GetService() []*OpenAPIServiceOption {
+ if x != nil {
+ return x.Service
+ }
+ return nil
+}
+
+func (x *OpenAPIOptions) GetField() []*OpenAPIFieldOption {
+ if x != nil {
+ return x.Field
+ }
+ return nil
+}
+
+// OpenAPIConfig represents a set of OpenAPI options
+type OpenAPIConfig struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ OpenapiOptions *OpenAPIOptions `protobuf:"bytes,1,opt,name=openapi_options,json=openapiOptions,proto3" json:"openapi_options,omitempty"`
+}
+
+func (x *OpenAPIConfig) Reset() {
+ *x = OpenAPIConfig{}
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *OpenAPIConfig) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenAPIConfig) ProtoMessage() {}
+
+func (x *OpenAPIConfig) ProtoReflect() protoreflect.Message {
+ mi := &file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenAPIConfig.ProtoReflect.Descriptor instead.
+func (*OpenAPIConfig) Descriptor() ([]byte, []int) {
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *OpenAPIConfig) GetOpenapiOptions() *OpenAPIOptions {
+ if x != nil {
+ return x.OpenapiOptions
+ }
+ return nil
+}
+
+var File_internal_descriptor_openapiconfigv3_openapiconfig_proto protoreflect.FileDescriptor
+
+var file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDesc = []byte{
+ 0x0a, 0x37, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x30, 0x67, 0x72, 0x70, 0x63, 0x2e,
+ 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, 0x33, 0x1a, 0x2c, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x73, 0x0a, 0x11, 0x4f, 0x70, 0x65,
+ 0x6e, 0x41, 0x50, 0x49, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12,
+ 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69,
+ 0x6c, 0x65, 0x12, 0x4a, 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53,
+ 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7b,
+ 0x0a, 0x13, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x4c, 0x0a,
+ 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x7b, 0x0a, 0x14, 0x4f,
+ 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x49, 0x0a,
+ 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x78, 0x0a, 0x14, 0x4f, 0x70, 0x65, 0x6e,
+ 0x41, 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x46, 0x0a, 0x06, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x12, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x46, 0x69, 0x65,
+ 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x4d,
+ 0x0a, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69,
+ 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xe8, 0x03,
+ 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x12, 0x57, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e,
+ 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
+ 0x72, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76,
+ 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x5d, 0x0a, 0x06, 0x6d, 0x65, 0x74,
+ 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+ 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65,
+ 0x6e, 0x41, 0x50, 0x49, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x60, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73,
+ 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61,
+ 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65,
+ 0x6e, 0x41, 0x50, 0x49, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x60, 0x0a, 0x07, 0x73, 0x65,
+ 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x46, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, 0x33, 0x2e, 0x4f,
+ 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5a, 0x0a, 0x05,
+ 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+ 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, 0x33, 0x2e, 0x4f,
+ 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x7a, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x6e,
+ 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x0f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x76, 0x33, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+ 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x65, 0x63, 0x6f, 0x73, 0x79, 0x73, 0x74, 0x65,
+ 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x76,
+ 0x32, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e,
+ 0x66, 0x69, 0x67, 0x76, 0x33, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescOnce sync.Once
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescData = file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDesc
+)
+
+func file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescGZIP() []byte {
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescOnce.Do(func() {
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescData)
+ })
+ return file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDescData
+}
+
+var file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_internal_descriptor_openapiconfigv3_openapiconfig_proto_goTypes = []any{
+ (*OpenAPIFileOption)(nil), // 0: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFileOption
+ (*OpenAPIMethodOption)(nil), // 1: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMethodOption
+ (*OpenAPIMessageOption)(nil), // 2: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMessageOption
+ (*OpenAPIServiceOption)(nil), // 3: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIServiceOption
+ (*OpenAPIFieldOption)(nil), // 4: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFieldOption
+ (*OpenAPIOptions)(nil), // 5: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions
+ (*OpenAPIConfig)(nil), // 6: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIConfig
+ (*options.Swagger)(nil), // 7: grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ (*options.Operation)(nil), // 8: grpc.gateway.protoc_gen_openapiv3.options.Operation
+ (*options.Schema)(nil), // 9: grpc.gateway.protoc_gen_openapiv3.options.Schema
+ (*options.Tag)(nil), // 10: grpc.gateway.protoc_gen_openapiv3.options.Tag
+ (*options.JSONSchema)(nil), // 11: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+}
+var file_internal_descriptor_openapiconfigv3_openapiconfig_proto_depIdxs = []int32{
+ 7, // 0: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFileOption.option:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ 8, // 1: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMethodOption.option:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation
+ 9, // 2: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMessageOption.option:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Schema
+ 10, // 3: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIServiceOption.option:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag
+ 11, // 4: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFieldOption.option:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ 0, // 5: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions.file:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFileOption
+ 1, // 6: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions.method:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMethodOption
+ 2, // 7: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions.message:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIMessageOption
+ 3, // 8: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions.service:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIServiceOption
+ 4, // 9: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions.field:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIFieldOption
+ 5, // 10: grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIConfig.openapi_options:type_name -> grpc.gateway.internal.descriptor.openapiconfigv3.OpenAPIOptions
+ 11, // [11:11] is the sub-list for method output_type
+ 11, // [11:11] is the sub-list for method input_type
+ 11, // [11:11] is the sub-list for extension type_name
+ 11, // [11:11] is the sub-list for extension extendee
+ 0, // [0:11] is the sub-list for field type_name
+}
+
+func init() { file_internal_descriptor_openapiconfigv3_openapiconfig_proto_init() }
+func file_internal_descriptor_openapiconfigv3_openapiconfig_proto_init() {
+ if File_internal_descriptor_openapiconfigv3_openapiconfig_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 7,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_internal_descriptor_openapiconfigv3_openapiconfig_proto_goTypes,
+ DependencyIndexes: file_internal_descriptor_openapiconfigv3_openapiconfig_proto_depIdxs,
+ MessageInfos: file_internal_descriptor_openapiconfigv3_openapiconfig_proto_msgTypes,
+ }.Build()
+ File_internal_descriptor_openapiconfigv3_openapiconfig_proto = out.File
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_rawDesc = nil
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_goTypes = nil
+ file_internal_descriptor_openapiconfigv3_openapiconfig_proto_depIdxs = nil
+}
diff --git a/internal/descriptor/openapiconfigv3/openapiconfig.proto b/internal/descriptor/openapiconfigv3/openapiconfig.proto
new file mode 100644
index 00000000000..0ef2d290de3
--- /dev/null
+++ b/internal/descriptor/openapiconfigv3/openapiconfig.proto
@@ -0,0 +1,51 @@
+syntax = "proto3";
+
+package grpc.gateway.internal.descriptor.openapiconfigv3;
+
+import "protoc-gen-openapiv3/options/openapiv3.proto";
+
+option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfigv3";
+
+// OpenAPIFileOption represents OpenAPI options on a file
+message OpenAPIFileOption {
+ string file = 1;
+ grpc.gateway.protoc_gen_openapiv3.options.Swagger option = 2;
+}
+
+// OpenAPIMethodOption represents OpenAPI options on a method
+message OpenAPIMethodOption {
+ string method = 1;
+ grpc.gateway.protoc_gen_openapiv3.options.Operation option = 2;
+}
+
+// OpenAPIMessageOption represents OpenAPI options on a message
+message OpenAPIMessageOption {
+ string message = 1;
+ grpc.gateway.protoc_gen_openapiv3.options.Schema option = 2;
+}
+
+// OpenAPIServiceOption represents OpenAPI options on a service
+message OpenAPIServiceOption {
+ string service = 1; // ex: Service
+ grpc.gateway.protoc_gen_openapiv3.options.Tag option = 2;
+}
+
+// OpenAPIFieldOption represents OpenAPI options on a field
+message OpenAPIFieldOption {
+ string field = 1;
+ grpc.gateway.protoc_gen_openapiv3.options.JSONSchema option = 2;
+}
+
+// OpenAPIOptions represents OpenAPI protobuf options
+message OpenAPIOptions {
+ repeated OpenAPIFileOption file = 1;
+ repeated OpenAPIMethodOption method = 2;
+ repeated OpenAPIMessageOption message = 3;
+ repeated OpenAPIServiceOption service = 4;
+ repeated OpenAPIFieldOption field = 5;
+}
+
+// OpenAPIConfig represents a set of OpenAPI options
+message OpenAPIConfig {
+ OpenAPIOptions openapi_options = 1;
+}
diff --git a/internal/descriptor/openapiconfigv3/openapiconfig.swagger.json b/internal/descriptor/openapiconfigv3/openapiconfig.swagger.json
new file mode 100644
index 00000000000..6517bd1a2fa
--- /dev/null
+++ b/internal/descriptor/openapiconfigv3/openapiconfig.swagger.json
@@ -0,0 +1,44 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "internal/descriptor/openapiconfigv3/openapiconfig.proto",
+ "version": "version not set"
+ },
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {},
+ "definitions": {
+ "protobufAny": {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": {}
+ },
+ "rpcStatus": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/protobufAny"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go
index 743cea8f126..0f7955eb8f9 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -7,7 +7,9 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/codegenerator"
"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfigv3"
"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
+ optionsv3 "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"google.golang.org/genproto/googleapis/api/annotations"
@@ -132,6 +134,22 @@ type Registry struct {
// field name and a period to additional OpenAPI field options
fieldOptions map[string]*options.JSONSchema
+ // fileOptions is a mapping of file name to additional OpenAPI file options
+ fileOptionsv3 map[string]*optionsv3.Swagger
+
+ // methodOptions is a mapping of fully-qualified method name to additional OpenAPI method options
+ methodOptionsv3 map[string]*optionsv3.Operation
+
+ // messageOptions is a mapping of fully-qualified message name to additional OpenAPI message options
+ messageOptionsv3 map[string]*optionsv3.Schema
+
+ //serviceOptions is a mapping of fully-qualified service name to additional OpenAPI service options
+ serviceOptionsv3 map[string]*optionsv3.Tag
+
+ // fieldOptions is a mapping of the fully-qualified name of the parent message concat
+ // field name and a period to additional OpenAPI field options
+ fieldOptionsv3 map[string]*optionsv3.JSONSchema
+
// generateUnboundMethods causes the registry to generate proxy methods even for
// RPC methods that have no HttpRule annotation.
generateUnboundMethods bool
@@ -809,36 +827,132 @@ func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) er
return nil
}
+// RegisterOpenAPIOptions registers OpenAPI options
+func (r *Registry) RegisterOpenAPIOptionsv3(opts *openapiconfigv3.OpenAPIOptions) error {
+ if opts == nil {
+ return nil
+ }
+
+ for _, opt := range opts.File {
+ if _, ok := r.files[opt.File]; !ok {
+ return fmt.Errorf("no file %s found", opt.File)
+ }
+ r.fileOptionsv3[opt.File] = opt.Option
+ }
+
+ // build map of all registered methods
+ methods := make(map[string]struct{})
+ services := make(map[string]struct{})
+ for _, f := range r.files {
+ for _, s := range f.Services {
+ services[s.FQSN()] = struct{}{}
+ for _, m := range s.Methods {
+ methods[m.FQMN()] = struct{}{}
+ }
+ }
+ }
+
+ for _, opt := range opts.Method {
+ qualifiedMethod := "." + opt.Method
+ if _, ok := methods[qualifiedMethod]; !ok {
+ return fmt.Errorf("no method %s found", opt.Method)
+ }
+ r.methodOptionsv3[qualifiedMethod] = opt.Option
+ }
+
+ for _, opt := range opts.Message {
+ qualifiedMessage := "." + opt.Message
+ if _, ok := r.msgs[qualifiedMessage]; !ok {
+ return fmt.Errorf("no message %s found", opt.Message)
+ }
+ r.messageOptionsv3[qualifiedMessage] = opt.Option
+ }
+
+ for _, opt := range opts.Service {
+ qualifiedService := "." + opt.Service
+ if _, ok := services[qualifiedService]; !ok {
+ return fmt.Errorf("no service %s found", opt.Service)
+ }
+ r.serviceOptionsv3[qualifiedService] = opt.Option
+ }
+
+ // build map of all registered fields
+ fields := make(map[string]struct{})
+ for _, m := range r.msgs {
+ for _, f := range m.Fields {
+ fields[f.FQFN()] = struct{}{}
+ }
+ }
+ for _, opt := range opts.Field {
+ qualifiedField := "." + opt.Field
+ if _, ok := fields[qualifiedField]; !ok {
+ return fmt.Errorf("no field %s found", opt.Field)
+ }
+ r.fieldOptionsv3[qualifiedField] = opt.Option
+ }
+ return nil
+}
+
// GetOpenAPIFileOption returns a registered OpenAPI option for a file
func (r *Registry) GetOpenAPIFileOption(file string) (*options.Swagger, bool) {
opt, ok := r.fileOptions[file]
return opt, ok
}
+// GetOpenAPIFileOption returns a registered OpenAPI option for a file
+func (r *Registry) GetOpenAPIFileOptionv3(file string) (*optionsv3.Swagger, bool) {
+ opt, ok := r.fileOptionsv3[file]
+ return opt, ok
+}
+
// GetOpenAPIMethodOption returns a registered OpenAPI option for a method
func (r *Registry) GetOpenAPIMethodOption(qualifiedMethod string) (*options.Operation, bool) {
opt, ok := r.methodOptions[qualifiedMethod]
return opt, ok
}
+// GetOpenAPIMethodOptionv3 returns a registered OpenAPI option for a method
+func (r *Registry) GetOpenAPIMethodOptionv3(qualifiedMethod string) (*optionsv3.Operation, bool) {
+ opt, ok := r.methodOptionsv3[qualifiedMethod]
+ return opt, ok
+}
+
// GetOpenAPIMessageOption returns a registered OpenAPI option for a message
func (r *Registry) GetOpenAPIMessageOption(qualifiedMessage string) (*options.Schema, bool) {
opt, ok := r.messageOptions[qualifiedMessage]
return opt, ok
}
+// GetOpenAPIMessageOptionv3 returns a registered OpenAPI option for a message
+func (r *Registry) GetOpenAPIMessageOptionv3(qualifiedMessage string) (*optionsv3.Schema, bool) {
+ opt, ok := r.messageOptionsv3[qualifiedMessage]
+ return opt, ok
+}
+
// GetOpenAPIServiceOption returns a registered OpenAPI option for a service
func (r *Registry) GetOpenAPIServiceOption(qualifiedService string) (*options.Tag, bool) {
opt, ok := r.serviceOptions[qualifiedService]
return opt, ok
}
+// GetOpenAPIServiceOptionv3 returns a registered OpenAPI option for a service
+func (r *Registry) GetOpenAPIServiceOptionv3(qualifiedService string) (*optionsv3.Tag, bool) {
+ opt, ok := r.serviceOptionsv3[qualifiedService]
+ return opt, ok
+}
+
// GetOpenAPIFieldOption returns a registered OpenAPI option for a field
func (r *Registry) GetOpenAPIFieldOption(qualifiedField string) (*options.JSONSchema, bool) {
opt, ok := r.fieldOptions[qualifiedField]
return opt, ok
}
+// GetOpenAPIFieldOption returns a registered OpenAPI option for a field
+func (r *Registry) GetOpenAPIFieldOptionv3(qualifiedField string) (*optionsv3.JSONSchema, bool) {
+ opt, ok := r.fieldOptionsv3[qualifiedField]
+ return opt, ok
+}
+
func (r *Registry) FieldName(f *Field) string {
if r.useJSONNamesForFields {
return f.GetJsonName()
diff --git a/protoc-gen-openapiv3/BUILD.bazel b/protoc-gen-openapiv3/BUILD.bazel
new file mode 100644
index 00000000000..27b50a3779b
--- /dev/null
+++ b/protoc-gen-openapiv3/BUILD.bazel
@@ -0,0 +1,31 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test")
+
+package(default_visibility = ["//visibility:private"])
+
+go_library(
+ name = "protoc-gen-openapiv3_lib",
+ srcs = ["main.go"],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3",
+ deps = [
+ "//internal/codegenerator",
+ "//internal/descriptor",
+ "//protoc-gen-openapiv3/internal/genopenapi",
+ "//utilities",
+ "@org_golang_google_grpc//grpclog",
+ "@org_golang_google_protobuf//proto",
+ "@org_golang_google_protobuf//types/pluginpb",
+ ],
+)
+
+go_binary(
+ name = "protoc-gen-openapiv3",
+ embed = [":protoc-gen-openapiv3_lib"],
+ visibility = ["//visibility:public"],
+)
+
+go_test(
+ name = "protoc-gen-openapiv3_test",
+ size = "small",
+ srcs = ["main_test.go"],
+ embed = [":protoc-gen-openapiv3_lib"],
+)
diff --git a/protoc-gen-openapiv3/defs.bzl b/protoc-gen-openapiv3/defs.bzl
new file mode 100644
index 00000000000..18e8d36d3ec
--- /dev/null
+++ b/protoc-gen-openapiv3/defs.bzl
@@ -0,0 +1,478 @@
+"""Generated an open-api spec for a grpc api spec.
+
+Reads the api spec in protobuf format and generate an open-api spec.
+Optionally applies settings from the grpc-service configuration.
+"""
+
+load("@rules_proto//proto:defs.bzl", "ProtoInfo")
+
+# TODO(yannic): Replace with |proto_common.direct_source_infos| when
+# https://github.com/bazelbuild/rules_proto/pull/22 lands.
+def _direct_source_infos(proto_info, provided_sources = []):
+ """Returns sequence of `ProtoFileInfo` for `proto_info`'s direct sources.
+
+ Files that are both in `proto_info`'s direct sources and in
+ `provided_sources` are skipped. This is useful, e.g., for well-known
+ protos that are already provided by the Protobuf runtime.
+
+ Args:
+ proto_info: An instance of `ProtoInfo`.
+ provided_sources: Optional. A sequence of files to ignore.
+ Usually, these files are already provided by the
+ Protocol Buffer runtime (e.g. Well-Known protos).
+
+ Returns: A sequence of `ProtoFileInfo` containing information about
+ `proto_info`'s direct sources.
+ """
+
+ source_root = proto_info.proto_source_root
+ if "." == source_root:
+ return [struct(file = src, import_path = src.path) for src in proto_info.check_deps_sources.to_list()]
+
+ offset = len(source_root) + 1 # + '/'.
+
+ infos = []
+ for src in proto_info.check_deps_sources.to_list():
+ # TODO(yannic): Remove this hack when we drop support for Bazel < 1.0.
+ local_offset = offset
+ if src.root.path and not source_root.startswith(src.root.path):
+ # Before Bazel 1.0, `proto_source_root` wasn't guaranteed to be a
+ # prefix of `src.path`. This could happened, e.g., if `file` was
+ # generated (https://github.com/bazelbuild/bazel/issues/9215).
+ local_offset += len(src.root.path) + 1 # + '/'.
+ infos.append(struct(file = src, import_path = src.path[local_offset:]))
+
+ return infos
+
+def _run_proto_gen_openapi(
+ actions,
+ proto_info,
+ target_name,
+ transitive_proto_srcs,
+ protoc,
+ protoc_gen_openapiv3,
+ single_output,
+ allow_delete_body,
+ grpc_api_configuration,
+ json_names_for_fields,
+ repeated_path_param_separator,
+ include_package_in_tags,
+ fqn_for_openapi_name,
+ openapi_naming_strategy,
+ use_go_templates,
+ go_template_args,
+ ignore_comments,
+ remove_internal_comments,
+ disable_default_errors,
+ disable_service_tags,
+ enums_as_ints,
+ omit_enum_default_value,
+ output_format,
+ simple_operation_ids,
+ proto3_optional_nullable,
+ openapi_configuration,
+ generate_unbound_methods,
+ visibility_restriction_selectors,
+ use_allof_for_refs,
+ disable_default_responses,
+ enable_rpc_deprecation,
+ expand_slashed_path_patterns,
+ preserve_rpc_order,
+ generate_x_go_type):
+ args = actions.args()
+
+ args.add("--plugin", "protoc-gen-openapiv3=%s" % protoc_gen_openapiv3.path)
+
+ extra_inputs = []
+ if grpc_api_configuration:
+ extra_inputs.append(grpc_api_configuration)
+ args.add("--openapiv3_opt", "grpc_api_configuration=%s" % grpc_api_configuration.path)
+
+ if openapi_configuration:
+ extra_inputs.append(openapi_configuration)
+ args.add("--openapiv3_opt", "openapi_configuration=%s" % openapi_configuration.path)
+
+ if not json_names_for_fields:
+ args.add("--openapiv3_opt", "json_names_for_fields=false")
+
+ if fqn_for_openapi_name:
+ args.add("--openapiv3_opt", "fqn_for_openapi_name=true")
+
+ if openapi_naming_strategy:
+ args.add("--openapiv3_opt", "openapi_naming_strategy=%s" % openapi_naming_strategy)
+
+ if generate_unbound_methods:
+ args.add("--openapiv3_opt", "generate_unbound_methods=true")
+
+ if simple_operation_ids:
+ args.add("--openapiv3_opt", "simple_operation_ids=true")
+
+ if allow_delete_body:
+ args.add("--openapiv3_opt", "allow_delete_body=true")
+
+ if include_package_in_tags:
+ args.add("--openapiv3_opt", "include_package_in_tags=true")
+
+ if use_go_templates:
+ args.add("--openapiv3_opt", "use_go_templates=true")
+
+ for go_template_arg in go_template_args:
+ args.add("--openapiv3_opt", "go_template_args=%s" % go_template_arg)
+
+ if ignore_comments:
+ args.add("--openapiv3_opt", "ignore_comments=true")
+
+ if remove_internal_comments:
+ args.add("--openapiv3_opt", "remove_internal_comments=true")
+
+ if disable_default_errors:
+ args.add("--openapiv3_opt", "disable_default_errors=true")
+
+ if disable_service_tags:
+ args.add("--openapiv3_opt", "disable_service_tags=true")
+
+ if enums_as_ints:
+ args.add("--openapiv3_opt", "enums_as_ints=true")
+
+ if omit_enum_default_value:
+ args.add("--openapiv3_opt", "omit_enum_default_value=true")
+
+ if output_format:
+ args.add("--openapiv3_opt", "output_format=%s" % output_format)
+
+ if proto3_optional_nullable:
+ args.add("--openapiv3_opt", "proto3_optional_nullable=true")
+
+ for visibility_restriction_selector in visibility_restriction_selectors:
+ args.add("--openapiv3_opt", "visibility_restriction_selectors=%s" % visibility_restriction_selector)
+
+ if use_allof_for_refs:
+ args.add("--openapiv3_opt", "use_allof_for_refs=true")
+
+ if disable_default_responses:
+ args.add("--openapiv3_opt", "disable_default_responses=true")
+
+ if enable_rpc_deprecation:
+ args.add("--openapiv3_opt", "enable_rpc_deprecation=true")
+
+ if expand_slashed_path_patterns:
+ args.add("--openapiv3_opt", "expand_slashed_path_patterns=true")
+
+ if preserve_rpc_order:
+ args.add("--openapiv3_opt", "preserve_rpc_order=true")
+ if generate_x_go_type:
+ args.add("--openapiv3_opt", "generate_x_go_type=true")
+
+ args.add("--openapiv3_opt", "repeated_path_param_separator=%s" % repeated_path_param_separator)
+
+ proto_file_infos = _direct_source_infos(proto_info)
+
+ # TODO(yannic): Use |proto_info.transitive_descriptor_sets| when
+ # https://github.com/bazelbuild/bazel/issues/9337 is fixed.
+ args.add_all(proto_info.transitive_proto_path, format_each = "--proto_path=%s")
+
+ if single_output:
+ args.add("--openapiv3_opt", "allow_merge=true")
+ args.add("--openapiv3_opt", "merge_file_name=%s" % target_name)
+
+ openapi_file = actions.declare_file("%s.swagger.json" % target_name)
+ args.add("--openapiv3_out", openapi_file.dirname)
+
+ args.add_all([f.import_path for f in proto_file_infos])
+
+ actions.run(
+ executable = protoc,
+ tools = [protoc_gen_openapiv3],
+ inputs = depset(
+ direct = extra_inputs,
+ transitive = [transitive_proto_srcs],
+ ),
+ outputs = [openapi_file],
+ arguments = [args],
+ )
+
+ return [openapi_file]
+
+ # TODO(yannic): We may be able to generate all files in a single action,
+ # but that will change at least the semantics of `use_go_template.proto`.
+ openapi_files = []
+ for proto_file_info in proto_file_infos:
+ # TODO(yannic): This probably doesn't work as expected: we only add this
+ # option after we have seen it, so `.proto` sources that happen to be
+ # in the list of `.proto` files before `use_go_template.proto` will be
+ # compiled without this option, and all sources that get compiled after
+ # `use_go_template.proto` will have this option on.
+ if proto_file_info.file.basename == "use_go_template.proto":
+ args.add("--openapiv3_opt", "use_go_templates=true")
+
+ file_name = "%s.swagger.json" % proto_file_info.import_path[:-len(".proto")]
+ openapi_file = actions.declare_file(
+ "_virtual_imports/%s/%s" % (target_name, file_name),
+ )
+
+ file_args = actions.args()
+
+ offset = len(file_name) + 1 # + '/'.
+ file_args.add("--openapiv3_out", openapi_file.path[:-offset])
+
+ file_args.add(proto_file_info.import_path)
+
+ actions.run(
+ executable = protoc,
+ tools = [protoc_gen_openapiv3],
+ inputs = depset(
+ direct = extra_inputs,
+ transitive = [transitive_proto_srcs],
+ ),
+ outputs = [openapi_file],
+ arguments = [args, file_args],
+ )
+ openapi_files.append(openapi_file)
+
+ return openapi_files
+
+def _proto_gen_openapi_impl(ctx):
+ proto = ctx.attr.proto[ProtoInfo]
+ return [
+ DefaultInfo(
+ files = depset(
+ _run_proto_gen_openapi(
+ actions = ctx.actions,
+ proto_info = proto,
+ target_name = ctx.attr.name,
+ transitive_proto_srcs = depset(
+ direct = ctx.files._well_known_protos,
+ transitive = [proto.transitive_sources],
+ ),
+ protoc = ctx.executable._protoc,
+ protoc_gen_openapiv3 = ctx.executable._protoc_gen_openapi,
+ single_output = ctx.attr.single_output,
+ allow_delete_body = ctx.attr.allow_delete_body,
+ grpc_api_configuration = ctx.file.grpc_api_configuration,
+ json_names_for_fields = ctx.attr.json_names_for_fields,
+ repeated_path_param_separator = ctx.attr.repeated_path_param_separator,
+ include_package_in_tags = ctx.attr.include_package_in_tags,
+ fqn_for_openapi_name = ctx.attr.fqn_for_openapi_name,
+ openapi_naming_strategy = ctx.attr.openapi_naming_strategy,
+ use_go_templates = ctx.attr.use_go_templates,
+ go_template_args = ctx.attr.go_template_args,
+ ignore_comments = ctx.attr.ignore_comments,
+ remove_internal_comments = ctx.attr.remove_internal_comments,
+ disable_default_errors = ctx.attr.disable_default_errors,
+ disable_service_tags = ctx.attr.disable_service_tags,
+ enums_as_ints = ctx.attr.enums_as_ints,
+ omit_enum_default_value = ctx.attr.omit_enum_default_value,
+ output_format = ctx.attr.output_format,
+ simple_operation_ids = ctx.attr.simple_operation_ids,
+ proto3_optional_nullable = ctx.attr.proto3_optional_nullable,
+ openapi_configuration = ctx.file.openapi_configuration,
+ generate_unbound_methods = ctx.attr.generate_unbound_methods,
+ visibility_restriction_selectors = ctx.attr.visibility_restriction_selectors,
+ use_allof_for_refs = ctx.attr.use_allof_for_refs,
+ disable_default_responses = ctx.attr.disable_default_responses,
+ enable_rpc_deprecation = ctx.attr.enable_rpc_deprecation,
+ expand_slashed_path_patterns = ctx.attr.expand_slashed_path_patterns,
+ preserve_rpc_order = ctx.attr.preserve_rpc_order,
+ generate_x_go_type = ctx.attr.generate_x_go_type,
+ ),
+ ),
+ ),
+ ]
+
+protoc_gen_openapiv3 = rule(
+ attrs = {
+ "proto": attr.label(
+ mandatory = True,
+ providers = [ProtoInfo],
+ ),
+ "single_output": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, the rule will generate a single OpenAPI file",
+ ),
+ "allow_delete_body": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "unless set, HTTP DELETE methods may not have a body",
+ ),
+ "grpc_api_configuration": attr.label(
+ allow_single_file = True,
+ mandatory = False,
+ doc = "path to file which describes the gRPC API Configuration in YAML format",
+ ),
+ "json_names_for_fields": attr.bool(
+ default = True,
+ mandatory = False,
+ doc = "if disabled, the original proto name will be used for generating OpenAPI definitions",
+ ),
+ "repeated_path_param_separator": attr.string(
+ default = "csv",
+ mandatory = False,
+ values = ["csv", "pipes", "ssv", "tsv"],
+ doc = "configures how repeated fields should be split." +
+ " Allowed values are `csv`, `pipes`, `ssv` and `tsv`",
+ ),
+ "include_package_in_tags": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if unset, the gRPC service name is added to the `Tags`" +
+ " field of each operation. If set and the `package` directive" +
+ " is shown in the proto file, the package name will be " +
+ " prepended to the service name",
+ ),
+ "fqn_for_openapi_name": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, the object's OpenAPI names will use the fully" +
+ " qualified names from the proto definition" +
+ " (ie my.package.MyMessage.MyInnerMessage",
+ ),
+ "openapi_naming_strategy": attr.string(
+ default = "",
+ mandatory = False,
+ values = ["", "simple", "package", "legacy", "fqn"],
+ doc = "configures how OpenAPI names are determined." +
+ " Allowed values are `` (empty), `simple`, `package`, `legacy` and `fqn`." +
+ " If unset, either `legacy` or `fqn` are selected, depending" +
+ " on the value of the `fqn_for_openapi_name` setting",
+ ),
+ "use_go_templates": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, you can use Go templates in protofile comments",
+ ),
+ "go_template_args": attr.string_list(
+ mandatory = False,
+ doc = "specify a key value pair as inputs to the Go template of the protofile" +
+ " comments. Repeat this option to specify multiple template arguments." +
+ " Requires the `use_go_templates` option to be set.",
+ ),
+ "ignore_comments": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, all protofile comments are excluded from output",
+ ),
+ "remove_internal_comments": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, removes all substrings in comments that start with " +
+ "`(--` and end with `--)` as specified in " +
+ "https://google.aip.dev/192#internal-comments",
+ ),
+ "disable_default_errors": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, disables generation of default errors." +
+ " This is useful if you have defined custom error handling",
+ ),
+ "disable_service_tags": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, disables generation of service tags." +
+ " This is useful if you do not want to expose the names of your backend grpc services.",
+ ),
+ "enums_as_ints": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "whether to render enum values as integers, as opposed to string values",
+ ),
+ "omit_enum_default_value": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, omit default enum value",
+ ),
+ "output_format": attr.string(
+ default = "json",
+ mandatory = False,
+ values = ["json", "yaml"],
+ doc = "output content format. Allowed values are: `json`, `yaml`",
+ ),
+ "simple_operation_ids": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "whether to remove the service prefix in the operationID" +
+ " generation. Can introduce duplicate operationIDs, use with caution.",
+ ),
+ "proto3_optional_nullable": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "whether Proto3 Optional fields should be marked as x-nullable",
+ ),
+ "openapi_configuration": attr.label(
+ allow_single_file = True,
+ mandatory = False,
+ doc = "path to file which describes the OpenAPI Configuration in YAML format",
+ ),
+ "generate_unbound_methods": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "generate swagger metadata even for RPC methods that have" +
+ " no HttpRule annotation",
+ ),
+ "visibility_restriction_selectors": attr.string_list(
+ mandatory = False,
+ doc = "list of `google.api.VisibilityRule` visibility labels to include" +
+ " in the generated output when a visibility annotation is defined." +
+ " Repeat this option to supply multiple values. Elements without" +
+ " visibility annotations are unaffected by this setting.",
+ ),
+ "use_allof_for_refs": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, will use allOf as container for $ref to preserve" +
+ " same-level properties.",
+ ),
+ "disable_default_responses": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, disables generation of default responses. Useful" +
+ " if you have to support custom response codes that are" +
+ " not 200.",
+ ),
+ "enable_rpc_deprecation": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "whether to process grpc method's deprecated option.",
+ ),
+ "expand_slashed_path_patterns": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, expands path patterns containing slashes into URI." +
+ " It also creates a new path parameter for each wildcard in " +
+ " the path pattern.",
+ ),
+ "preserve_rpc_order": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, ensures the order of paths emitted in OpenAPI files" +
+ " mirrors the order of RPC methods found in proto files." +
+ " If false, emitted paths will be ordered alphabetically.",
+ ),
+ "use_proto3_field_semantics": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "if set, uses proto3 field semantics for the OpenAPI schema." +
+ " This means that fields are required by default.",
+ ),
+ "generate_x_go_type": attr.bool(
+ default = False,
+ mandatory = False,
+ doc = "Generate x-go-type extension using the go_package option from proto files",
+ ),
+ "_protoc": attr.label(
+ default = "@com_google_protobuf//:protoc",
+ executable = True,
+ cfg = "exec",
+ ),
+ "_well_known_protos": attr.label(
+ default = "@com_google_protobuf//:well_known_type_protos",
+ allow_files = True,
+ ),
+ "_protoc_gen_openapi": attr.label(
+ default = Label("//protoc-gen-openapiv3:protoc-gen-openapiv3"),
+ executable = True,
+ cfg = "exec",
+ ),
+ },
+ implementation = _proto_gen_openapi_impl,
+)
diff --git a/protoc-gen-openapiv3/internal/genopenapi/BUILD.bazel b/protoc-gen-openapiv3/internal/genopenapi/BUILD.bazel
new file mode 100644
index 00000000000..9b9db250e1f
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/BUILD.bazel
@@ -0,0 +1,87 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+
+package(default_visibility = ["//protoc-gen-openapiv3:__subpackages__"])
+
+go_library(
+ name = "genopenapi",
+ srcs = [
+ "doc.go",
+ "format.go",
+ "generator.go",
+ "helpers.go",
+ "helpers_go111_old.go",
+ "naming.go",
+ "template.go",
+ "types.go",
+ ],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/internal/genopenapi",
+ deps = [
+ "//internal/casing",
+ "//internal/descriptor",
+ "//internal/generator",
+ "//protoc-gen-openapiv3/options",
+ "@in_gopkg_yaml_v3//:yaml_v3",
+ "@org_golang_google_genproto_googleapis_api//annotations",
+ "@org_golang_google_genproto_googleapis_api//visibility",
+ "@org_golang_google_genproto_googleapis_rpc//status",
+ "@org_golang_google_grpc//grpclog",
+ "@org_golang_google_protobuf//encoding/protojson",
+ "@org_golang_google_protobuf//proto",
+ "@org_golang_google_protobuf//reflect/protodesc",
+ "@org_golang_google_protobuf//types/descriptorpb",
+ "@org_golang_google_protobuf//types/known/anypb",
+ "@org_golang_google_protobuf//types/known/structpb",
+ "@org_golang_google_protobuf//types/pluginpb",
+ "@org_golang_x_text//cases",
+ "@org_golang_x_text//language",
+ ],
+)
+
+go_test(
+ name = "genopenapi_test",
+ size = "small",
+ srcs = [
+ "cycle_test.go",
+ "format_test.go",
+ "generator_test.go",
+ "helpers_test.go",
+ "naming_test.go",
+ "template_fuzz_test.go",
+ "template_test.go",
+ "types_test.go",
+ ],
+ data = glob(["testdata/**"]),
+ embed = [":genopenapi"],
+ deps = [
+ "//internal/descriptor",
+ "//internal/descriptor/openapiconfigv3:openapiconfig",
+ "//internal/httprule",
+ "//protoc-gen-openapiv3/options",
+ "//runtime",
+ "@com_github_google_go_cmp//cmp",
+ "@in_gopkg_yaml_v3//:yaml_v3",
+ "@org_golang_google_genproto_googleapis_api//annotations",
+ "@org_golang_google_genproto_googleapis_api//visibility",
+ "@org_golang_google_protobuf//encoding/protojson",
+ "@org_golang_google_protobuf//encoding/prototext",
+ "@org_golang_google_protobuf//proto",
+ "@org_golang_google_protobuf//reflect/protodesc",
+ "@org_golang_google_protobuf//reflect/protoreflect",
+ "@org_golang_google_protobuf//reflect/protoregistry",
+ "@org_golang_google_protobuf//types/descriptorpb",
+ "@org_golang_google_protobuf//types/known/anypb",
+ "@org_golang_google_protobuf//types/known/durationpb",
+ "@org_golang_google_protobuf//types/known/emptypb",
+ "@org_golang_google_protobuf//types/known/fieldmaskpb",
+ "@org_golang_google_protobuf//types/known/structpb",
+ "@org_golang_google_protobuf//types/known/timestamppb",
+ "@org_golang_google_protobuf//types/known/wrapperspb",
+ "@org_golang_google_protobuf//types/pluginpb",
+ ],
+)
+
+alias(
+ name = "go_default_library",
+ actual = ":genopenapi",
+ visibility = ["//protoc-gen-openapiv3:__subpackages__"],
+)
diff --git a/protoc-gen-openapiv3/internal/genopenapi/cycle_test.go b/protoc-gen-openapiv3/internal/genopenapi/cycle_test.go
new file mode 100644
index 00000000000..88613155610
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/cycle_test.go
@@ -0,0 +1,41 @@
+package genopenapi
+
+import (
+ "testing"
+)
+
+func TestCycle(t *testing.T) {
+ for _, tt := range []struct {
+ max int
+ attempt int
+ e bool
+ }{
+ {
+ max: 3,
+ attempt: 3,
+ e: true,
+ },
+ {
+ max: 5,
+ attempt: 6,
+ },
+ {
+ max: 1000,
+ attempt: 1001,
+ },
+ } {
+
+ c := newCycleChecker(tt.max)
+ var final bool
+ for i := 0; i < tt.attempt; i++ {
+ final = c.Check("a")
+ if !final {
+ break
+ }
+ }
+
+ if final != tt.e {
+ t.Errorf("got: %t wanted: %t", final, tt.e)
+ }
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/doc.go b/protoc-gen-openapiv3/internal/genopenapi/doc.go
new file mode 100644
index 00000000000..42f9237f66e
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/doc.go
@@ -0,0 +1,2 @@
+// Package genopenapi provides a code generator for OpenAPI v2.
+package genopenapi
diff --git a/protoc-gen-openapiv3/internal/genopenapi/format.go b/protoc-gen-openapiv3/internal/genopenapi/format.go
new file mode 100644
index 00000000000..6f0faa8e538
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/format.go
@@ -0,0 +1,46 @@
+package genopenapi
+
+import (
+ "encoding/json"
+ "errors"
+ "io"
+
+ "gopkg.in/yaml.v3"
+)
+
+type Format string
+
+const (
+ FormatJSON Format = "json"
+ FormatYAML Format = "yaml"
+)
+
+type ContentEncoder interface {
+ Encode(v interface{}) (err error)
+}
+
+func (f Format) Validate() error {
+ switch f {
+ case FormatJSON, FormatYAML:
+ return nil
+ default:
+ return errors.New("unknown format: " + string(f))
+ }
+}
+
+func (f Format) NewEncoder(w io.Writer) (ContentEncoder, error) {
+ switch f {
+ case FormatYAML:
+ enc := yaml.NewEncoder(w)
+ enc.SetIndent(2)
+
+ return enc, nil
+ case FormatJSON:
+ enc := json.NewEncoder(w)
+ enc.SetIndent("", " ")
+
+ return enc, nil
+ default:
+ return nil, errors.New("unknown format: " + string(f))
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/format_test.go b/protoc-gen-openapiv3/internal/genopenapi/format_test.go
new file mode 100644
index 00000000000..9c2ea42f999
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/format_test.go
@@ -0,0 +1,105 @@
+package genopenapi_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "reflect"
+ "testing"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/internal/genopenapi"
+ "gopkg.in/yaml.v3"
+)
+
+func TestFormatValidate(t *testing.T) {
+ t.Parallel()
+
+ testCases := [...]struct {
+ Format genopenapi.Format
+ Valid bool
+ }{{
+ Format: genopenapi.FormatJSON,
+ Valid: true,
+ }, {
+ Format: genopenapi.FormatYAML,
+ Valid: true,
+ }, {
+ Format: genopenapi.Format("unknown"),
+ Valid: false,
+ }, {
+ Format: genopenapi.Format(""),
+ Valid: false,
+ }}
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(string(tc.Format), func(t *testing.T) {
+ t.Parallel()
+
+ err := tc.Format.Validate()
+ switch {
+ case tc.Valid && err != nil:
+ t.Fatalf("expect no validation error, got: %s", err)
+ case !tc.Valid && err == nil:
+ t.Fatal("expect validation error, got nil")
+ }
+ })
+ }
+}
+
+func TestFormatEncode(t *testing.T) {
+ t.Parallel()
+
+ type contentDecoder interface {
+ Decode(v interface{}) error
+ }
+
+ testCases := [...]struct {
+ Format genopenapi.Format
+ NewDecoder func(r io.Reader) contentDecoder
+ }{{
+ Format: genopenapi.FormatJSON,
+ NewDecoder: func(r io.Reader) contentDecoder {
+ return json.NewDecoder(r)
+ },
+ }, {
+ Format: genopenapi.FormatYAML,
+ NewDecoder: func(r io.Reader) contentDecoder {
+ return yaml.NewDecoder(r)
+ },
+ }}
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(string(tc.Format), func(t *testing.T) {
+ t.Parallel()
+
+ expParams := map[string]string{
+ "hello": "world",
+ }
+
+ var buf bytes.Buffer
+ enc, err := tc.Format.NewEncoder(&buf)
+ if err != nil {
+ t.Fatalf("expect no encoder creating error, got: %s", err)
+ }
+
+ err = enc.Encode(expParams)
+ if err != nil {
+ t.Fatalf("expect no encoding error, got: %s", err)
+ }
+
+ gotParams := make(map[string]string)
+ err = tc.NewDecoder(&buf).Decode(&gotParams)
+ if err != nil {
+ t.Fatalf("expect no decoding error, got: %s", err)
+ }
+
+ if !reflect.DeepEqual(expParams, gotParams) {
+ t.Fatalf("expected: %+v, actual: %+v", expParams, gotParams)
+ }
+ })
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/generator.go b/protoc-gen-openapiv3/internal/genopenapi/generator.go
new file mode 100644
index 00000000000..59b9f1c17c2
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/generator.go
@@ -0,0 +1,480 @@
+package genopenapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "path/filepath"
+ "reflect"
+ "sort"
+ "strings"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ gen "github.com/grpc-ecosystem/grpc-gateway/v2/internal/generator"
+ openapioptions "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
+ statuspb "google.golang.org/genproto/googleapis/rpc/status"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protodesc"
+ "google.golang.org/protobuf/types/descriptorpb"
+ "google.golang.org/protobuf/types/known/anypb"
+ "google.golang.org/protobuf/types/pluginpb"
+ "gopkg.in/yaml.v3"
+)
+
+var errNoTargetService = errors.New("no target service defined in the file")
+
+type generator struct {
+ reg *descriptor.Registry
+ format Format
+}
+
+type wrapper struct {
+ fileName string
+ swagger *openapiSwaggerObject
+}
+
+type GeneratorOptions struct {
+ Registry *descriptor.Registry
+ RecursiveDepth int
+}
+
+// New returns a new generator which generates grpc gateway files.
+func New(reg *descriptor.Registry, format Format) gen.Generator {
+ return &generator{
+ reg: reg,
+ format: format,
+ }
+}
+
+// Merge a lot of OpenAPI file (wrapper) to single one OpenAPI file
+func mergeTargetFile(targets []*wrapper, mergeFileName string) *wrapper {
+ var mergedTarget *wrapper
+ for _, f := range targets {
+ if mergedTarget == nil {
+ mergedTarget = &wrapper{
+ fileName: mergeFileName,
+ swagger: f.swagger,
+ }
+ } else {
+ for k, v := range f.swagger.Definitions {
+ mergedTarget.swagger.Definitions[k] = v
+ }
+ for k, v := range f.swagger.SecurityDefinitions {
+ mergedTarget.swagger.SecurityDefinitions[k] = v
+ }
+ copy(mergedTarget.swagger.Paths, f.swagger.Paths)
+ mergedTarget.swagger.Security = append(mergedTarget.swagger.Security, f.swagger.Security...)
+ }
+ }
+ return mergedTarget
+}
+
+// Q: What's up with the alias types here?
+// A: We don't want to completely override how these structs are marshaled into
+// JSON, we only want to add fields (see below, extensionMarshalJSON).
+// An infinite recursion would happen if we'd call json.Marshal on the struct
+// that has swaggerObject as an embedded field. To avoid that, we'll create
+// type aliases, and those don't have the custom MarshalJSON methods defined
+// on them. See http://choly.ca/post/go-json-marshalling/ (or, if it ever
+// goes away, use
+// https://web.archive.org/web/20190806073003/http://choly.ca/post/go-json-marshalling/).
+func (so openapiSwaggerObject) MarshalJSON() ([]byte, error) {
+ type alias openapiSwaggerObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+// MarshalYAML implements yaml.Marshaler interface.
+//
+// It is required in order to pass extensions inline.
+//
+// Example:
+//
+// extensions: {x-key: x-value}
+// type: string
+//
+// It will be rendered as:
+//
+// x-key: x-value
+// type: string
+//
+// Use generics when the project will be upgraded to go 1.18+.
+func (so openapiSwaggerObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiSwaggerObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+// Custom json marshaller for openapiPathsObject. Ensures
+// openapiPathsObject is marshalled into expected format in generated
+// swagger.json.
+func (po openapiPathsObject) MarshalJSON() ([]byte, error) {
+ var buf bytes.Buffer
+
+ buf.WriteString("{")
+ for i, pd := range po {
+ if i != 0 {
+ buf.WriteString(",")
+ }
+ // marshal key
+ key, err := json.Marshal(pd.Path)
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(key)
+ buf.WriteString(":")
+ // marshal value
+ val, err := json.Marshal(pd.PathItemObject)
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(val)
+ }
+
+ buf.WriteString("}")
+ return buf.Bytes(), nil
+}
+
+// Custom yaml marshaller for openapiPathsObject. Ensures
+// openapiPathsObject is marshalled into expected format in generated
+// swagger.yaml.
+func (po openapiPathsObject) MarshalYAML() (interface{}, error) {
+ var pathObjectNode yaml.Node
+ pathObjectNode.Kind = yaml.MappingNode
+
+ for _, pathData := range po {
+ var pathNode yaml.Node
+
+ pathNode.SetString(pathData.Path)
+ pathItemObjectNode, err := pathData.PathItemObject.toYAMLNode()
+ if err != nil {
+ return nil, err
+ }
+ pathObjectNode.Content = append(pathObjectNode.Content, &pathNode, pathItemObjectNode)
+ }
+
+ return pathObjectNode, nil
+}
+
+// We can simplify this implementation once the go-yaml bug is resolved. See: https://github.com/go-yaml/yaml/issues/643.
+//
+// func (pio *openapiPathItemObject) toYAMLNode() (*yaml.Node, error) {
+// var node yaml.Node
+// if err := node.Encode(pio); err != nil {
+// return nil, err
+// }
+// return &node, nil
+// }
+func (pio *openapiPathItemObject) toYAMLNode() (*yaml.Node, error) {
+ var doc yaml.Node
+ var buf bytes.Buffer
+ ec := yaml.NewEncoder(&buf)
+ ec.SetIndent(2)
+ if err := ec.Encode(pio); err != nil {
+ return nil, err
+ }
+ if err := yaml.Unmarshal(buf.Bytes(), &doc); err != nil {
+ return nil, err
+ }
+ if len(doc.Content) == 0 {
+ return nil, errors.New("unexpected number of yaml nodes")
+ }
+ return doc.Content[0], nil
+}
+
+func (so openapiInfoObject) MarshalJSON() ([]byte, error) {
+ type alias openapiInfoObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiInfoObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiInfoObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiSecuritySchemeObject) MarshalJSON() ([]byte, error) {
+ type alias openapiSecuritySchemeObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiSecuritySchemeObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiSecuritySchemeObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiOperationObject) MarshalJSON() ([]byte, error) {
+ type alias openapiOperationObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiOperationObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiOperationObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiResponseObject) MarshalJSON() ([]byte, error) {
+ type alias openapiResponseObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiResponseObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiResponseObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiSchemaObject) MarshalJSON() ([]byte, error) {
+ type alias openapiSchemaObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiSchemaObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiSchemaObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiParameterObject) MarshalJSON() ([]byte, error) {
+ type alias openapiParameterObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiParameterObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiParameterObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func (so openapiTagObject) MarshalJSON() ([]byte, error) {
+ type alias openapiTagObject
+ return extensionMarshalJSON(alias(so), so.extensions)
+}
+
+func (so openapiTagObject) MarshalYAML() (interface{}, error) {
+ type Alias openapiTagObject
+
+ return struct {
+ Extension map[string]interface{} `yaml:",inline"`
+ Alias `yaml:",inline"`
+ }{
+ Extension: extensionsToMap(so.extensions),
+ Alias: Alias(so),
+ }, nil
+}
+
+func extensionMarshalJSON(so interface{}, extensions []extension) ([]byte, error) {
+ // To append arbitrary keys to the struct we'll render into json,
+ // we're creating another struct that embeds the original one, and
+ // its extra fields:
+ //
+ // The struct will look like
+ // struct {
+ // *openapiCore
+ // XGrpcGatewayFoo json.RawMessage `json:"x-grpc-gateway-foo"`
+ // XGrpcGatewayBar json.RawMessage `json:"x-grpc-gateway-bar"`
+ // }
+ // and thus render into what we want -- the JSON of openapiCore with the
+ // extensions appended.
+ fields := []reflect.StructField{
+ { // embedded
+ Name: "Embedded",
+ Type: reflect.TypeOf(so),
+ Anonymous: true,
+ },
+ }
+ for _, ext := range extensions {
+ fields = append(fields, reflect.StructField{
+ Name: fieldName(ext.key),
+ Type: reflect.TypeOf(ext.value),
+ Tag: reflect.StructTag(fmt.Sprintf("json:\"%s\"", ext.key)),
+ })
+ }
+
+ t := reflect.StructOf(fields)
+ s := reflect.New(t).Elem()
+ s.Field(0).Set(reflect.ValueOf(so))
+ for _, ext := range extensions {
+ s.FieldByName(fieldName(ext.key)).Set(reflect.ValueOf(ext.value))
+ }
+ return json.Marshal(s.Interface())
+}
+
+// encodeOpenAPI converts OpenAPI file obj to pluginpb.CodeGeneratorResponse_File
+func encodeOpenAPI(file *wrapper, format Format) (*descriptor.ResponseFile, error) {
+ var contentBuf bytes.Buffer
+ enc, err := format.NewEncoder(&contentBuf)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := enc.Encode(*file.swagger); err != nil {
+ return nil, err
+ }
+
+ name := file.fileName
+ ext := filepath.Ext(name)
+ base := strings.TrimSuffix(name, ext)
+ output := fmt.Sprintf("%s.swagger."+string(format), base)
+ return &descriptor.ResponseFile{
+ CodeGeneratorResponse_File: &pluginpb.CodeGeneratorResponse_File{
+ Name: proto.String(output),
+ Content: proto.String(contentBuf.String()),
+ },
+ }, nil
+}
+
+func (g *generator) Generate(targets []*descriptor.File) ([]*descriptor.ResponseFile, error) {
+ var files []*descriptor.ResponseFile
+ if g.reg.IsAllowMerge() {
+ var mergedTarget *descriptor.File
+ // try to find proto leader
+ for _, f := range targets {
+ if proto.HasExtension(f.Options, openapioptions.E_Openapiv2Swagger) {
+ mergedTarget = f
+ break
+ }
+ }
+ // merge protos to leader
+ for _, f := range targets {
+ if mergedTarget == nil {
+ mergedTarget = f
+ } else if mergedTarget != f {
+ mergedTarget.Enums = append(mergedTarget.Enums, f.Enums...)
+ mergedTarget.Messages = append(mergedTarget.Messages, f.Messages...)
+ mergedTarget.Services = append(mergedTarget.Services, f.Services...)
+ }
+ }
+
+ targets = nil
+ targets = append(targets, mergedTarget)
+ }
+
+ var openapis []*wrapper
+ for _, file := range targets {
+ if grpclog.V(1) {
+ grpclog.Infof("Processing %s", file.GetName())
+ }
+ swagger, err := applyTemplate(param{File: file, reg: g.reg})
+ if errors.Is(err, errNoTargetService) {
+ if grpclog.V(1) {
+ grpclog.Infof("%s: %v", file.GetName(), err)
+ }
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+ openapis = append(openapis, &wrapper{
+ fileName: file.GetName(),
+ swagger: swagger,
+ })
+ }
+
+ if g.reg.IsAllowMerge() {
+ targetOpenAPI := mergeTargetFile(openapis, g.reg.GetMergeFileName())
+ if !g.reg.IsPreserveRPCOrder() {
+ targetOpenAPI.swagger.sortPathsAlphabetically()
+ }
+ f, err := encodeOpenAPI(targetOpenAPI, g.format)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", g.reg.GetMergeFileName(), err)
+ }
+ files = append(files, f)
+ if grpclog.V(1) {
+ grpclog.Infof("New OpenAPI file will emit")
+ }
+ } else {
+ for _, file := range openapis {
+ if !g.reg.IsPreserveRPCOrder() {
+ file.swagger.sortPathsAlphabetically()
+ }
+ f, err := encodeOpenAPI(file, g.format)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode OpenAPI for %s: %w", file.fileName, err)
+ }
+ files = append(files, f)
+ if grpclog.V(1) {
+ grpclog.Infof("New OpenAPI file will emit")
+ }
+ }
+ }
+ return files, nil
+}
+
+func (so openapiSwaggerObject) sortPathsAlphabetically() {
+ sort.Slice(so.Paths, func(i, j int) bool {
+ return so.Paths[i].Path < so.Paths[j].Path
+ })
+}
+
+// AddErrorDefs Adds google.rpc.Status and google.protobuf.Any
+// to registry (used for error-related API responses)
+func AddErrorDefs(reg *descriptor.Registry) error {
+ // load internal protos
+ any := protodesc.ToFileDescriptorProto((&anypb.Any{}).ProtoReflect().Descriptor().ParentFile())
+ any.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
+ status := protodesc.ToFileDescriptorProto((&statuspb.Status{}).ProtoReflect().Descriptor().ParentFile())
+ status.SourceCodeInfo = new(descriptorpb.SourceCodeInfo)
+ return reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ any,
+ status,
+ },
+ })
+}
+
+func extensionsToMap(extensions []extension) map[string]interface{} {
+ m := make(map[string]interface{}, len(extensions))
+
+ for _, v := range extensions {
+ m[v.key] = RawExample(v.value)
+ }
+
+ return m
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/generator_test.go b/protoc-gen-openapiv3/internal/genopenapi/generator_test.go
new file mode 100644
index 00000000000..c9d6c687b8c
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/generator_test.go
@@ -0,0 +1,1940 @@
+package genopenapi_test
+
+import (
+ "bytes"
+ "os"
+ "reflect"
+ "sort"
+ "strings"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/internal/genopenapi"
+ "gopkg.in/yaml.v3"
+
+ "google.golang.org/protobuf/encoding/prototext"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/descriptorpb"
+ "google.golang.org/protobuf/types/pluginpb"
+)
+
+func TestGenerate_YAML(t *testing.T) {
+ t.Parallel()
+
+ req := &pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{{
+ Name: proto.String("file.proto"),
+ Package: proto.String("example"),
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("goexample/v1;goexample"),
+ },
+ }},
+ FileToGenerate: []string{
+ "file.proto",
+ },
+ }
+
+ resp := requireGenerate(t, req, genopenapi.FormatYAML, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ var p map[string]interface{}
+ err := yaml.Unmarshal([]byte(resp[0].GetContent()), &p)
+ if err != nil {
+ t.Fatalf("failed to unmarshall yaml: %s", err)
+ }
+}
+
+func TestGenerateExtension(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ options: {
+ [grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field]: {
+ description: "This is bar"
+ extensions: {
+ key: "x-go-default"
+ value: {
+ string_value: "0.5s"
+ }
+ }
+ }
+ }
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {}
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ if !strings.Contains(content, "x-go-default") {
+ t.Fatal("x-go-default not found in content message")
+ }
+ })
+ }
+}
+
+func TestGenerateYAML(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ inputProtoText string
+ wantYAML string
+ }{
+ {
+ // It tests https://github.com/grpc-ecosystem/grpc-gateway/issues/3557.
+ name: "path item object",
+ inputProtoText: "testdata/generator/path_item_object.prototext",
+ wantYAML: "testdata/generator/path_item_object.swagger.yaml",
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ b, err := os.ReadFile(tt.inputProtoText)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal(b, &req); err != nil {
+ t.Fatal(err)
+ }
+
+ resp := requireGenerate(t, &req, genopenapi.FormatYAML, false, true)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+ got := resp[0].GetContent()
+
+ want, err := os.ReadFile(tt.wantYAML)
+ if err != nil {
+ t.Fatal(err)
+ }
+ diff := cmp.Diff(string(want), got)
+ if diff != "" {
+ t.Fatalf("content not match\n%s", diff)
+ }
+ })
+ }
+}
+
+func requireGenerate(
+ tb testing.TB,
+ req *pluginpb.CodeGeneratorRequest,
+ format genopenapi.Format,
+ preserveRPCOrder bool,
+ allowMerge bool,
+) []*descriptor.ResponseFile {
+ tb.Helper()
+
+ reg := descriptor.NewRegistry()
+ reg.SetPreserveRPCOrder(preserveRPCOrder)
+ reg.SetAllowMerge(allowMerge)
+
+ if err := reg.Load(req); err != nil {
+ tb.Fatalf("failed to load request: %s", err)
+ }
+
+ var targets []*descriptor.File
+ for _, target := range req.FileToGenerate {
+ f, err := reg.LookupFile(target)
+ if err != nil {
+ tb.Fatalf("failed to lookup file: %s", err)
+ }
+
+ targets = append(targets, f)
+ }
+
+ g := genopenapi.New(reg, format)
+
+ resp, err := g.Generate(targets)
+ switch {
+ case err != nil:
+ tb.Fatalf("failed to generate targets: %s", err)
+ case len(resp) != len(targets) && !allowMerge:
+ tb.Fatalf("invalid count, expected: %d, actual: %d", len(targets), len(resp))
+ }
+
+ return resp
+}
+
+func TestGeneratedYAMLIndent(t *testing.T) {
+ // It tests https://github.com/grpc-ecosystem/grpc-gateway/issues/2745.
+ const in = `
+ file_to_generate: "exampleproto/v1/exampleproto.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/exampleproto.proto"
+ package: "repro"
+ message_type: {
+ name: "RollupRequest"
+ field: {
+ name: "type"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_ENUM
+ type_name: ".repro.RollupType"
+ json_name: "type"
+ }
+ }
+ message_type: {
+ name: "RollupResponse"
+ }
+ enum_type: {
+ name: "RollupType"
+ value: {
+ name: "UNKNOWN_ROLLUP"
+ number: 0
+ }
+ value: {
+ name: "APPLE"
+ number: 1
+ }
+ value: {
+ name: "BANANA"
+ number: 2
+ }
+ value: {
+ name: "CARROT"
+ number: 3
+ }
+ }
+ service: {
+ name: "Repro"
+ method: {
+ name: "GetRollup"
+ input_type: ".repro.RollupRequest"
+ output_type: ".repro.RollupResponse"
+ options: {
+ [google.api.http]: {
+ get: "/rollup"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "repro/foobar"
+ }
+ source_code_info: {
+ location: {
+ path: 5
+ path: 0
+ path: 2
+ path: 1
+ span: 24
+ span: 4
+ span: 14
+ leading_comments: " Apples are good\n"
+ }
+ location: {
+ path: 5
+ path: 0
+ path: 2
+ path: 3
+ span: 28
+ span: 4
+ span: 15
+ leading_comments: " Carrots are mediocre\n"
+ }
+ }
+ syntax: "proto3"
+ }
+ `
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ resp := requireGenerate(t, &req, genopenapi.FormatYAML, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ err := yaml.Unmarshal([]byte(content), map[string]interface{}{})
+ if err != nil {
+ t.Log(content)
+ t.Fatalf("got invalid yaml: %s", err)
+ }
+}
+
+func TestGenerateRPCOrderPreserved(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, true, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/b/first", "/a/second", "/c/third"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+
+}
+
+func TestGenerateRPCOrderNotPreserved(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/a/second", "/b/first", "/c/third"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+
+}
+
+func TestGenerateRPCOrderPreservedMultipleServices(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestServiceOne"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ service: {
+ name: "TestServiceTwo"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/g/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, true, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/d/first", "/e/second", "/c/third", "/b/first", "/a/second", "/g/third"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderNotPreservedMultipleServices(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestServiceOne"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ service: {
+ name: "TestServiceTwo"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/g/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/d/first", "/e/second", "/c/third", "/b/first", "/a/second", "/g/third"}
+ sort.Strings(expectedPaths)
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderPreservedMergeFiles(t *testing.T) {
+ t.Parallel()
+
+ const in1 = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/cpath"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/bpath"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/apath"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ const in2 = `
+ file_to_generate: "exampleproto/v2/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v2/example.proto"
+ package: "example.v2"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/f/fpath"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/epath"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/dpath"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v2;exampleproto"
+ }
+ }`
+
+ var req1, req2 pluginpb.CodeGeneratorRequest
+
+ if err := prototext.Unmarshal([]byte(in1), &req1); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+ if err := prototext.Unmarshal([]byte(in2), &req2); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ req1.ProtoFile = append(req1.ProtoFile, req2.ProtoFile...)
+ req1.FileToGenerate = append(req1.FileToGenerate, req2.FileToGenerate...)
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req1, format, true, true)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/c/cpath", "/b/bpath", "/a/apath", "/f/fpath", "/e/epath", "/d/dpath"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderNotPreservedMergeFiles(t *testing.T) {
+ t.Parallel()
+
+ const in1 = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/cpath"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/bpath"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/apath"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ const in2 = `
+ file_to_generate: "exampleproto/v2/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v2/example.proto"
+ package: "example.v2"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/f/fpath"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/epath"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/dpath"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v2;exampleproto"
+ }
+ }`
+
+ var req1, req2 pluginpb.CodeGeneratorRequest
+
+ if err := prototext.Unmarshal([]byte(in1), &req1); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+ if err := prototext.Unmarshal([]byte(in2), &req2); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ req1.ProtoFile = append(req1.ProtoFile, req2.ProtoFile...)
+ req1.FileToGenerate = append(req1.FileToGenerate, req2.FileToGenerate...)
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req1, format, false, true)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/c/cpath", "/b/bpath", "/a/apath", "/f/fpath", "/e/epath", "/d/dpath"}
+ sort.Strings(expectedPaths)
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderPreservedAdditionalBindings(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ additional_bindings {
+ get: "/a/additional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ additional_bindings {
+ get: "/z/zAdditional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ additional_bindings {
+ get: "/b/bAdditional"
+ }
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, true, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/b/first", "/a/additional", "/a/second", "/z/zAdditional", "/c/third", "/b/bAdditional"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOneOfFieldBodyAdditionalBindings(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ oneof_decl: {
+ name: "foo"
+ }
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ oneof_index: 0
+ }
+ field: {
+ name: "baz"
+ number: 2
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ oneof_index: 0
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ post: "/b/foo"
+ body: "*"
+ additional_bindings {
+ post: "/b/foo/bar"
+ body: "bar"
+ }
+ additional_bindings {
+ post: "/b/foo/baz"
+ body: "baz"
+ }
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, true, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/b/foo", "/b/foo/bar", "/b/foo/baz"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+
+ // The input message only contains oneof fields, so no other fields should be mapped to the query.
+ if strings.Contains(content, "query") {
+ t.Fatalf("Found query in content, expected not to find any")
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderNotPreservedAdditionalBindings(t *testing.T) {
+ t.Parallel()
+
+ const in = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ additional_bindings {
+ get: "/a/additional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ additional_bindings {
+ get: "/z/zAdditional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ additional_bindings {
+ get: "/b/bAdditional"
+ }
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal([]byte(in), &req); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req, format, false, false)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/b/first", "/a/additional", "/a/second", "/z/zAdditional", "/c/third", "/b/bAdditional"}
+ sort.Strings(expectedPaths)
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderPreservedMergeFilesAdditionalBindingsMultipleServices(t *testing.T) {
+ t.Parallel()
+
+ const in1 = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestServiceOne"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ service: {
+ name: "TestServiceTwo"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/g/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ const in2 = `
+ file_to_generate: "exampleproto/v2/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v2/example.proto"
+ package: "example.v2"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/bpath"
+ additional_bindings {
+ get: "/a/additional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/apath"
+ additional_bindings {
+ get: "/z/zAdditional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/cpath"
+ additional_bindings {
+ get: "/b/bAdditional"
+ }
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v2;exampleproto"
+ }
+ }`
+
+ var req1, req2 pluginpb.CodeGeneratorRequest
+
+ if err := prototext.Unmarshal([]byte(in1), &req1); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+ if err := prototext.Unmarshal([]byte(in2), &req2); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ req1.ProtoFile = append(req1.ProtoFile, req2.ProtoFile...)
+ req1.FileToGenerate = append(req1.FileToGenerate, req2.FileToGenerate...)
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req1, format, true, true)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/d/first", "/e/second", "/c/third",
+ "/b/first", "/a/second", "/g/third", "/b/bpath", "/a/additional",
+ "/a/apath", "/z/zAdditional", "/c/cpath", "/b/bAdditional"}
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+func TestGenerateRPCOrderNotPreservedMergeFilesAdditionalBindingsMultipleServices(t *testing.T) {
+ t.Parallel()
+
+ const in1 = `
+ file_to_generate: "exampleproto/v1/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v1/example.proto"
+ package: "example.v1"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestServiceOne"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/d/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/e/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/third"
+ }
+ }
+ }
+ }
+ service: {
+ name: "TestServiceTwo"
+ method: {
+ name: "Test1"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/first"
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/second"
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v1.Foo"
+ output_type: ".example.v1.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/g/third"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v1;exampleproto"
+ }
+ }`
+
+ const in2 = `
+ file_to_generate: "exampleproto/v2/example.proto"
+ parameter: "output_format=yaml,allow_delete_body=true"
+ proto_file: {
+ name: "exampleproto/v2/example.proto"
+ package: "example.v2"
+ message_type: {
+ name: "Foo"
+ field: {
+ name: "bar"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "bar"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test1"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/b/bpath"
+ additional_bindings {
+ get: "/a/additional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test2"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/a/apath"
+ additional_bindings {
+ get: "/z/zAdditional"
+ }
+ }
+ }
+ }
+ method: {
+ name: "Test3"
+ input_type: ".example.v2.Foo"
+ output_type: ".example.v2.Foo"
+ options: {
+ [google.api.http]: {
+ get: "/c/cpath"
+ additional_bindings {
+ get: "/b/bAdditional"
+ }
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "exampleproto/v2;exampleproto"
+ }
+ }`
+
+ var req1, req2 pluginpb.CodeGeneratorRequest
+
+ if err := prototext.Unmarshal([]byte(in1), &req1); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+ if err := prototext.Unmarshal([]byte(in2), &req2); err != nil {
+ t.Fatalf("failed to marshall yaml: %s", err)
+ }
+
+ req1.ProtoFile = append(req1.ProtoFile, req2.ProtoFile...)
+ req1.FileToGenerate = append(req1.FileToGenerate, req2.FileToGenerate...)
+ formats := [...]genopenapi.Format{
+ genopenapi.FormatJSON,
+ genopenapi.FormatYAML,
+ }
+
+ for _, format := range formats {
+ format := format
+ t.Run(string(format), func(t *testing.T) {
+ t.Parallel()
+
+ resp := requireGenerate(t, &req1, format, false, true)
+ if len(resp) != 1 {
+ t.Fatalf("invalid count, expected: 1, actual: %d", len(resp))
+ }
+
+ content := resp[0].GetContent()
+
+ t.Log(content)
+
+ contentsSlice := strings.Fields(content)
+ expectedPaths := []string{"/d/first", "/e/second", "/c/third",
+ "/b/first", "/a/second", "/g/third", "/b/bpath", "/a/additional",
+ "/a/apath", "/z/zAdditional", "/c/cpath", "/b/bAdditional"}
+ sort.Strings(expectedPaths)
+
+ foundPaths := []string{}
+ for _, contentValue := range contentsSlice {
+ findExpectedPaths(&foundPaths, expectedPaths, contentValue)
+ }
+
+ if allPresent := reflect.DeepEqual(foundPaths, expectedPaths); !allPresent {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, expectedPaths)
+ }
+ })
+ }
+}
+
+// Tries to find expected paths from a provided substring and store them in the foundPaths
+// slice.
+func findExpectedPaths(foundPaths *[]string, expectedPaths []string, potentialPath string) {
+ seenPaths := map[string]struct{}{}
+
+ // foundPaths may not be empty when this function is called multiple times,
+ // so we add them to seenPaths map to avoid duplicates.
+ for _, path := range *foundPaths {
+ seenPaths[path] = struct{}{}
+ }
+
+ for _, path := range expectedPaths {
+ _, pathAlreadySeen := seenPaths[path]
+ if strings.Contains(potentialPath, path) && !pathAlreadySeen {
+ *foundPaths = append(*foundPaths, path)
+ seenPaths[path] = struct{}{}
+ }
+ }
+}
+
+func TestFindExpectedPaths(t *testing.T) {
+ t.Parallel()
+
+ testCases := [...]struct {
+ testName string
+ requiredPaths []string
+ potentialPath string
+ expectedPathsFound []string
+ }{
+ {
+ testName: "One potential path present",
+ requiredPaths: []string{"/d/first", "/e/second", "/c/third", "/b/first"},
+ potentialPath: "[{\"path: \"/d/first\"",
+ expectedPathsFound: []string{"/d/first"},
+ },
+ {
+ testName: "No potential Paths present",
+ requiredPaths: []string{"/d/first", "/e/second", "/c/third", "/b/first"},
+ potentialPath: "[{\"path: \"/z/zpath\"",
+ expectedPathsFound: []string{},
+ },
+ {
+ testName: "Multiple potential paths present",
+ requiredPaths: []string{"/d/first", "/e/second", "/c/third", "/b/first", "/d/first"},
+ potentialPath: "[{\"path: \"/d/first\"someData\"/c/third\"someData\"/b/third\"",
+ expectedPathsFound: []string{"/d/first", "/c/third"},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.testName, func(t *testing.T) {
+ t.Parallel()
+
+ foundPaths := []string{}
+ findExpectedPaths(&foundPaths, tc.requiredPaths, tc.potentialPath)
+ if correctPathsFound := reflect.DeepEqual(foundPaths, tc.expectedPathsFound); !correctPathsFound {
+ t.Fatalf("Found paths differed from expected paths. Got: %#v, want %#v", foundPaths, tc.expectedPathsFound)
+ }
+ })
+ }
+}
+
+func TestGenerateXGoType(t *testing.T) {
+ t.Parallel()
+
+ tests := []struct {
+ name string
+ inputProtoText string
+ wantYAML string
+ }{
+ {
+ name: "x-go-type extension",
+ inputProtoText: "testdata/generator/x_go_type.prototext",
+ wantYAML: "testdata/generator/x_go_type.swagger.yaml",
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ b, err := os.ReadFile(tt.inputProtoText)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var req pluginpb.CodeGeneratorRequest
+ if err := prototext.Unmarshal(b, &req); err != nil {
+ t.Fatal(err)
+ }
+
+ reg := descriptor.NewRegistry()
+ reg.SetGenerateXGoType(true)
+ if err := reg.Load(&req); err != nil {
+ t.Fatalf("failed to load request: %s", err)
+ }
+
+ var targets []*descriptor.File
+ for _, target := range req.FileToGenerate {
+ f, err := reg.LookupFile(target)
+ if err != nil {
+ t.Fatalf("failed to lookup file: %s", err)
+ }
+ targets = append(targets, f)
+ }
+
+ g := genopenapi.New(reg, genopenapi.FormatYAML)
+ resp, err := g.Generate(targets)
+ if err != nil {
+ t.Fatalf("failed to generate: %s", err)
+ }
+
+ if len(resp) != 1 {
+ t.Fatalf("expected 1 file, got %d", len(resp))
+ }
+
+ want, err := os.ReadFile(tt.wantYAML)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var gotMap, wantMap map[string]interface{}
+ if err := yaml.Unmarshal([]byte(resp[0].GetContent()), &gotMap); err != nil {
+ t.Fatalf("failed to unmarshal generated YAML: %v", err)
+ }
+ if err := yaml.Unmarshal(want, &wantMap); err != nil {
+ t.Fatalf("failed to unmarshal expected YAML: %v", err)
+ }
+
+ gotYAML, err := yaml.Marshal(gotMap)
+ if err != nil {
+ t.Fatalf("failed to marshal got YAML: %v", err)
+ }
+ wantYAML, err := yaml.Marshal(wantMap)
+ if err != nil {
+ t.Fatalf("failed to marshal want YAML: %v", err)
+ }
+
+ if !bytes.Equal(gotYAML, wantYAML) {
+ t.Errorf("YAMLs don't match:\ngot:\n%s\nwant:\n%s", gotYAML, wantYAML)
+ }
+ })
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/helpers.go b/protoc-gen-openapiv3/internal/genopenapi/helpers.go
new file mode 100644
index 00000000000..3eb0921b449
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/helpers.go
@@ -0,0 +1,36 @@
+//go:build go1.12
+// +build go1.12
+
+package genopenapi
+
+import (
+ "strings"
+
+ "golang.org/x/text/cases"
+ "golang.org/x/text/language"
+)
+
+func fieldName(k string) string {
+ return strings.ReplaceAll(cases.Title(language.AmericanEnglish).String(k), "-", "_")
+}
+
+// this method will filter the same fields and return the unique one
+func getUniqueFields(schemaFieldsRequired []string, fieldsRequired []string) []string {
+ var unique []string
+ var index *int
+
+ for j, schemaFieldRequired := range schemaFieldsRequired {
+ index = nil
+ for i, fieldRequired := range fieldsRequired {
+ i := i
+ if schemaFieldRequired == fieldRequired {
+ index = &i
+ break
+ }
+ }
+ if index == nil {
+ unique = append(unique, schemaFieldsRequired[j])
+ }
+ }
+ return unique
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/helpers_go111_old.go b/protoc-gen-openapiv3/internal/genopenapi/helpers_go111_old.go
new file mode 100644
index 00000000000..d2b504a9ab8
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/helpers_go111_old.go
@@ -0,0 +1,10 @@
+//go:build !go1.12
+// +build !go1.12
+
+package genopenapi
+
+import "strings"
+
+func fieldName(k string) string {
+ return strings.Replace(strings.Title(k), "-", "_", -1)
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/helpers_test.go b/protoc-gen-openapiv3/internal/genopenapi/helpers_test.go
new file mode 100644
index 00000000000..f27f589cb1c
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/helpers_test.go
@@ -0,0 +1,66 @@
+package genopenapi
+
+import (
+ "reflect"
+ "testing"
+)
+
+func Test_getUniqueFields(t *testing.T) {
+ type args struct {
+ schemaFieldsRequired []string
+ fieldsRequired []string
+ }
+ var tests = []struct {
+ name string
+ args args
+ want []string
+ }{
+ {
+ name: "test_1",
+ args: args{
+ schemaFieldsRequired: []string{"Field_1", "Field_2", "Field_3"},
+ fieldsRequired: []string{"Field_2"},
+ },
+ want: []string{"Field_1", "Field_3"},
+ },
+ {
+ name: "test_2",
+ args: args{
+ schemaFieldsRequired: []string{"Field_1", "Field_2", "Field_3"},
+ fieldsRequired: []string{"Field_3"},
+ },
+ want: []string{"Field_1", "Field_2"},
+ },
+ {
+ name: "test_3",
+ args: args{
+ schemaFieldsRequired: []string{"Field_1", "Field_2", "Field_3"},
+ fieldsRequired: []string{"Field_4"},
+ },
+ want: []string{"Field_1", "Field_2", "Field_3"},
+ },
+ {
+ name: "test_4",
+ args: args{
+ schemaFieldsRequired: []string{"Field_1", "Field_2", "Field_3", "Field_4", "Field_5", "Field_6"},
+ fieldsRequired: []string{"Field_6", "Field_4", "Field_1"},
+ },
+ want: []string{"Field_2", "Field_3", "Field_5"},
+ },
+ {
+ name: "test_5",
+ args: args{
+ schemaFieldsRequired: []string{"Field_1", "Field_2", "Field_3"},
+ fieldsRequired: []string{},
+ },
+ want: []string{"Field_1", "Field_2", "Field_3"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := getUniqueFields(tt.args.schemaFieldsRequired, tt.args.fieldsRequired); !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("getUniqueFields() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/naming.go b/protoc-gen-openapiv3/internal/genopenapi/naming.go
new file mode 100644
index 00000000000..2a9ac069036
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/naming.go
@@ -0,0 +1,141 @@
+package genopenapi
+
+import (
+ "reflect"
+ "regexp"
+ "strings"
+)
+
+// LookupNamingStrategy looks up the given naming strategy and returns the naming
+// strategy function for it. The naming strategy function takes in the list of all
+// fully-qualified proto message names, and returns a mapping from fully-qualified
+// name to OpenAPI name.
+func LookupNamingStrategy(strategyName string) func([]string) map[string]string {
+ switch strings.ToLower(strategyName) {
+ case "fqn":
+ return resolveNamesFQN
+ case "legacy":
+ return resolveNamesLegacy
+ case "simple":
+ return resolveNamesSimple
+ case "package":
+ return resolveNamesPackage
+ }
+ return nil
+}
+
+// resolveNamesFQN uses the fully-qualified proto message name as the
+// OpenAPI name, stripping the leading dot.
+func resolveNamesFQN(messages []string) map[string]string {
+ uniqueNames := make(map[string]string, len(messages))
+ for _, p := range messages {
+ // strip leading dot from proto fqn
+ uniqueNames[p] = p[1:]
+ }
+ return uniqueNames
+}
+
+// resolveNamesLegacy takes the names of all proto messages and generates unique references by
+// applying the legacy heuristics for deriving unique names: starting from the bottom of the name hierarchy, it
+// determines the minimum number of components necessary to yield a unique name, adds one
+// to that number, and then concatenates those last components with no separator in between
+// to form a unique name.
+//
+// E.g., if the fully qualified name is `.a.b.C.D`, and there are other messages with fully
+// qualified names ending in `.D` but not in `.C.D`, it assigns the unique name `bCD`.
+func resolveNamesLegacy(messages []string) map[string]string {
+ return resolveNamesUniqueWithContext(messages, 1, "", false)
+}
+
+// resolveNamesSimple takes the names of all proto messages and generates unique references by using a simple
+// heuristic: starting from the bottom of the name hierarchy, it determines the minimum
+// number of components necessary to yield a unique name, and then concatenates those last
+// components with a "." separator in between to form a unique name.
+//
+// E.g., if the fully qualified name is `.a.b.C.D`, and there are other messages with
+// fully qualified names ending in `.D` but not in `.C.D`, it assigns the unique name `C.D`.
+func resolveNamesSimple(messages []string) map[string]string {
+ return resolveNamesUniqueWithContext(messages, 0, ".", false)
+}
+
+// resolveNamesPackage takes the names of all proto messages and generates unique references by
+// starting with the package-scoped name (with nested message types qualified by their containing
+// "parent" types), and then following the "simple" heuristic above to add package name components
+// until each message has a unique name with a "." between each component.
+//
+// E.g., if the fully qualified name is `.a.b.C.D`, the name is `C.D` unless there is another
+// package-scoped name ending in "C.D", in which case it would be `b.C.D` (unless that also
+// conflicted, in which case the name would be the fully-qualified `a.b.C`).
+func resolveNamesPackage(messages []string) map[string]string {
+ return resolveNamesUniqueWithContext(messages, 0, ".", true)
+}
+
+// For the "package" naming strategy, we rely on the convention that package names are lowercase
+// but message names are capitalized.
+var pkgEndRegexp = regexp.MustCompile(`\.[A-Z]`)
+
+// Take the names of every proto message and generates a unique reference by:
+// first, separating each message name into its components by splitting at dots. Then,
+// take the shortest suffix slice from each components slice that is unique among all
+// messages, and convert it into a component name by taking extraContext additional
+// components into consideration and joining all components with componentSeparator.
+func resolveNamesUniqueWithContext(messages []string, extraContext int, componentSeparator string, qualifyNestedMessages bool) map[string]string {
+ packagesByDepth := make(map[int][][]string)
+ uniqueNames := make(map[string]string)
+
+ hierarchy := func(pkg string) []string {
+ if !qualifyNestedMessages {
+ return strings.Split(pkg, ".")
+ }
+ pkgEnd := pkgEndRegexp.FindStringIndex(pkg)
+ if pkgEnd == nil {
+ // Fall back to non-qualified behavior if search based on convention fails.
+ return strings.Split(pkg, ".")
+ }
+ // Return each package component as an element, followed by the full message name
+ // (potentially qualified, if nested) as a single element.
+ qualifiedPkgName := pkg[:pkgEnd[0]]
+ nestedTypeName := pkg[pkgEnd[0]+1:]
+ return append(strings.Split(qualifiedPkgName, "."), nestedTypeName)
+ }
+
+ for _, p := range messages {
+ h := hierarchy(p)
+ for depth := range h {
+ if _, ok := packagesByDepth[depth]; !ok {
+ packagesByDepth[depth] = make([][]string, 0)
+ }
+ packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:])
+ }
+ }
+
+ count := func(list [][]string, item []string) int {
+ i := 0
+ for _, element := range list {
+ if reflect.DeepEqual(element, item) {
+ i++
+ }
+ }
+ return i
+ }
+
+ for _, p := range messages {
+ h := hierarchy(p)
+ depth := 0
+ for ; depth < len(h); depth++ {
+ // depth + extraContext > 0 ensures that we only break for values of depth when the
+ // resulting slice of name components is non-empty. Otherwise, we would return the
+ // empty string as the concise unique name is len(messages) == 1 (which is
+ // technically correct).
+ if depth+extraContext > 0 && count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
+ break
+ }
+ }
+ start := len(h) - depth - extraContext
+ if start < 0 {
+ start = 0
+ }
+ uniqueNames[p] = strings.Join(h[start:], componentSeparator)
+ }
+ return uniqueNames
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/naming_test.go b/protoc-gen-openapiv3/internal/genopenapi/naming_test.go
new file mode 100644
index 00000000000..22fa5ae9b55
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/naming_test.go
@@ -0,0 +1,63 @@
+package genopenapi
+
+import "testing"
+
+func TestNaming(t *testing.T) {
+ type expectedNames struct {
+ fqn, legacy, simple, pkg string
+ }
+ messageNameToExpected := map[string]expectedNames{
+ ".A": {"A", "A", "A", "A"},
+ ".a.B.C": {"a.B.C", "aBC", "B.C", "B.C"},
+ ".a.D.C": {"a.D.C", "aDC", "D.C", "D.C"},
+ ".a.E.F": {"a.E.F", "aEF", "a.E.F", "a.E.F"},
+ ".b.E.F": {"b.E.F", "bEF", "b.E.F", "b.E.F"},
+ ".c.G.H": {"c.G.H", "GH", "H", "G.H"},
+ }
+
+ allMessageNames := make([]string, 0, len(messageNameToExpected))
+ for msgName := range messageNameToExpected {
+ allMessageNames = append(allMessageNames, msgName)
+ }
+
+ t.Run("fqn", func(t *testing.T) {
+ uniqueNames := resolveNamesFQN(allMessageNames)
+ for _, msgName := range allMessageNames {
+ expected := messageNameToExpected[msgName].fqn
+ actual := uniqueNames[msgName]
+ if expected != actual {
+ t.Errorf("fqn unique name %q does not match expected name %q", actual, expected)
+ }
+ }
+ })
+ t.Run("legacy", func(t *testing.T) {
+ uniqueNames := resolveNamesLegacy(allMessageNames)
+ for _, msgName := range allMessageNames {
+ expected := messageNameToExpected[msgName].legacy
+ actual := uniqueNames[msgName]
+ if expected != actual {
+ t.Errorf("legacy unique name %q does not match expected name %q", actual, expected)
+ }
+ }
+ })
+ t.Run("simple", func(t *testing.T) {
+ uniqueNames := resolveNamesSimple(allMessageNames)
+ for _, msgName := range allMessageNames {
+ expected := messageNameToExpected[msgName].simple
+ actual := uniqueNames[msgName]
+ if expected != actual {
+ t.Errorf("simple unique name %q does not match expected name %q", actual, expected)
+ }
+ }
+ })
+ t.Run("package", func(t *testing.T) {
+ uniqueNames := resolveNamesPackage(allMessageNames)
+ for _, msgName := range allMessageNames {
+ expected := messageNameToExpected[msgName].pkg
+ actual := uniqueNames[msgName]
+ if expected != actual {
+ t.Errorf("package unique name %q does not match expected name %q", actual, expected)
+ }
+ }
+ })
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/template.go b/protoc-gen-openapiv3/internal/genopenapi/template.go
new file mode 100644
index 00000000000..312e834c9ab
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/template.go
@@ -0,0 +1,3469 @@
+package genopenapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "net/textproto"
+ "os"
+ "reflect"
+ "regexp"
+ "slices"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+ "text/template"
+ "time"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
+ "google.golang.org/genproto/googleapis/api/annotations"
+ "google.golang.org/genproto/googleapis/api/visibility"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/descriptorpb"
+ "google.golang.org/protobuf/types/known/structpb"
+)
+
+// The OpenAPI specification does not allow for more than one endpoint with the same HTTP method and path.
+// This prevents multiple gRPC service methods from sharing the same stripped version of the path and method.
+// For example: `GET /v1/{name=organizations/*}/roles` and `GET /v1/{name=users/*}/roles` both get stripped to `GET /v1/{name}/roles`.
+// We must make the URL unique by adding a suffix and an incrementing index to each path parameter
+// to differentiate the endpoints.
+// Since path parameter names do not affect the request contents (i.e. they're replaced in the path)
+// this will be hidden from the real grpc gateway consumer.
+const pathParamUniqueSuffixDeliminator = "_"
+
+const paragraphDeliminator = "\n\n"
+
+// wktSchemas are the schemas of well-known-types.
+// The schemas must match with the behavior of the JSON unmarshaler in
+// https://github.com/protocolbuffers/protobuf-go/blob/v1.25.0/encoding/protojson/well_known_types.go
+var wktSchemas = map[string]schemaCore{
+ ".google.protobuf.FieldMask": {
+ Type: "string",
+ },
+ ".google.protobuf.Timestamp": {
+ Type: "string",
+ Format: "date-time",
+ },
+ ".google.protobuf.Duration": {
+ Type: "string",
+ },
+ ".google.protobuf.StringValue": {
+ Type: "string",
+ },
+ ".google.protobuf.BytesValue": {
+ Type: "string",
+ Format: "byte",
+ },
+ ".google.protobuf.Int32Value": {
+ Type: "integer",
+ Format: "int32",
+ },
+ ".google.protobuf.UInt32Value": {
+ Type: "integer",
+ Format: "int64",
+ },
+ ".google.protobuf.Int64Value": {
+ Type: "string",
+ Format: "int64",
+ },
+ ".google.protobuf.UInt64Value": {
+ Type: "string",
+ Format: "uint64",
+ },
+ ".google.protobuf.FloatValue": {
+ Type: "number",
+ Format: "float",
+ },
+ ".google.protobuf.DoubleValue": {
+ Type: "number",
+ Format: "double",
+ },
+ ".google.protobuf.BoolValue": {
+ Type: "boolean",
+ },
+ ".google.protobuf.Empty": {
+ Type: "object",
+ },
+ ".google.protobuf.Struct": {
+ Type: "object",
+ },
+ ".google.protobuf.Value": {},
+ ".google.protobuf.ListValue": {
+ Type: "array",
+ Items: (*openapiItemsObject)(&openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ }),
+ },
+ ".google.protobuf.NullValue": {
+ Type: "string",
+ },
+}
+
+func listEnumNames(reg *descriptor.Registry, enum *descriptor.Enum) interface{} {
+ var names []string
+ for _, value := range enum.GetValue() {
+ if !isVisible(getEnumValueVisibilityOption(value), reg) {
+ continue
+ }
+ if reg.GetOmitEnumDefaultValue() && value.GetNumber() == 0 {
+ continue
+ }
+ names = append(names, value.GetName())
+ }
+
+ if len(names) > 0 {
+ return names
+ }
+
+ return nil
+}
+
+func listEnumNumbers(reg *descriptor.Registry, enum *descriptor.Enum) interface{} {
+ var numbers []int
+ for _, value := range enum.GetValue() {
+ if reg.GetOmitEnumDefaultValue() && value.GetNumber() == 0 {
+ continue
+ }
+ if !isVisible(getEnumValueVisibilityOption(value), reg) {
+ continue
+ }
+ numbers = append(numbers, int(value.GetNumber()))
+ }
+
+ if len(numbers) > 0 {
+ return numbers
+ }
+
+ return nil
+}
+
+func getEnumDefault(reg *descriptor.Registry, enum *descriptor.Enum) interface{} {
+ if !reg.GetOmitEnumDefaultValue() {
+ for _, value := range enum.GetValue() {
+ if value.GetNumber() == 0 {
+ if !isVisible(getEnumValueVisibilityOption(value), reg) {
+ return nil
+ }
+ return value.GetName()
+ }
+ }
+ }
+ return nil
+}
+
+func getEnumDefaultNumber(reg *descriptor.Registry, enum *descriptor.Enum) interface{} {
+ if !reg.GetOmitEnumDefaultValue() {
+ for _, value := range enum.GetValue() {
+ if value.GetNumber() == 0 {
+ return int(value.GetNumber())
+ }
+ }
+ }
+ return nil
+}
+
+// messageToQueryParameters converts a message to a list of OpenAPI query parameters.
+func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, httpMethod string) (params []openapiParameterObject, err error) {
+ for _, field := range message.Fields {
+ // When body is set to oneof field, we want to skip other fields in the oneof group.
+ if isBodySameOneOf(body, field) {
+ continue
+ }
+
+ if !isVisible(getFieldVisibilityOption(field), reg) {
+ continue
+ }
+ if reg.GetAllowPatchFeature() && field.GetTypeName() == ".google.protobuf.FieldMask" && field.GetName() == "update_mask" && httpMethod == "PATCH" && len(body.FieldPath) != 0 {
+ continue
+ }
+
+ p, err := queryParams(message, field, "", reg, pathParams, body, reg.GetRecursiveDepth())
+ if err != nil {
+ return nil, err
+ }
+ params = append(params, p...)
+ }
+ return params, nil
+}
+
+func isBodySameOneOf(body *descriptor.Body, field *descriptor.Field) bool {
+ if field.OneofIndex == nil {
+ return false
+ }
+
+ if body == nil || len(body.FieldPath) == 0 {
+ return false
+ }
+
+ if body.FieldPath[0].Target.OneofIndex == nil {
+ return false
+ }
+
+ return *body.FieldPath[0].Target.OneofIndex == *field.OneofIndex
+}
+
+// queryParams converts a field to a list of OpenAPI query parameters recursively through the use of nestedQueryParams.
+func queryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, recursiveCount int) (params []openapiParameterObject, err error) {
+ return nestedQueryParams(message, field, prefix, reg, pathParams, body, newCycleChecker(recursiveCount))
+}
+
+type cycleChecker struct {
+ m map[string]int
+ count int
+}
+
+func newCycleChecker(recursive int) *cycleChecker {
+ return &cycleChecker{
+ m: make(map[string]int),
+ count: recursive,
+ }
+}
+
+// Check returns whether name is still within recursion
+// toleration
+func (c *cycleChecker) Check(name string) bool {
+ count, ok := c.m[name]
+ count += 1
+ isCycle := count > c.count
+
+ if isCycle {
+ return false
+ }
+
+ // provision map entry if not available
+ if !ok {
+ c.m[name] = 1
+ return true
+ }
+
+ c.m[name] = count
+
+ return true
+}
+
+func (c *cycleChecker) Branch() *cycleChecker {
+ copy := &cycleChecker{
+ count: c.count,
+ m: make(map[string]int, len(c.m)),
+ }
+
+ for k, v := range c.m {
+ copy.m[k] = v
+ }
+
+ return copy
+}
+
+// nestedQueryParams converts a field to a list of OpenAPI query parameters recursively.
+// This function is a helper function for queryParams, that keeps track of cyclical message references
+// through the use of
+//
+// touched map[string]int
+//
+// If a cycle is discovered, an error is returned, as cyclical data structures are dangerous
+// in query parameters.
+func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, cycle *cycleChecker) (params []openapiParameterObject, err error) {
+ // make sure the parameter is not already listed as a path parameter
+ for _, pathParam := range pathParams {
+ if pathParam.Target == field {
+ return nil, nil
+ }
+ }
+ // make sure the parameter is not already listed as a body parameter
+ if body != nil {
+ if body.FieldPath == nil {
+ return nil, nil
+ }
+ for _, fieldPath := range body.FieldPath {
+ if fieldPath.Target == field {
+ return nil, nil
+ }
+ }
+ }
+ schema := schemaOfField(field, reg, nil)
+ fieldType := field.GetTypeName()
+ if message.File != nil {
+ comments := fieldProtoComments(reg, message, field)
+ if err := updateOpenAPIDataFromComments(reg, &schema, message, comments, false); err != nil {
+ return nil, err
+ }
+ }
+
+ isEnum := field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_ENUM
+ items := schema.Items
+ if schema.Type != "" || isEnum {
+ if schema.Type == "object" {
+ location := ""
+ if ix := strings.LastIndex(field.Message.FQMN(), "."); ix > 0 {
+ location = field.Message.FQMN()[0:ix]
+ }
+ if m, err := reg.LookupMsg(location, field.GetTypeName()); err == nil {
+ if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
+ k := m.GetField()[0]
+ kType, err := getMapParamKey(k.GetType())
+ if err != nil {
+ return nil, err
+ }
+ // This will generate a query in the format map_name[key_type]
+ fName := fmt.Sprintf("%s[%s]", *field.Name, kType)
+ field.Name = proto.String(fName)
+ schema.Type = schema.AdditionalProperties.schemaCore.Type
+ }
+ }
+ }
+ if items != nil && (items.Type == "" || items.Type == "object") && !isEnum {
+ return nil, nil // TODO: currently, mapping object in query parameter is not supported
+ }
+ desc := mergeDescription(schema)
+
+ // verify if the field is required
+ required := false
+ for _, fieldName := range schema.Required {
+ if fieldName == reg.FieldName(field) {
+ required = true
+ break
+ }
+ }
+ // verify if the field is required in message options
+ if messageSchema, err := extractSchemaOptionFromMessageDescriptor(message.DescriptorProto); err == nil {
+ for _, fieldName := range messageSchema.GetJsonSchema().GetRequired() {
+ // Required fields can be field names or json_name values
+ if fieldName == field.GetJsonName() || fieldName == field.GetName() {
+ required = true
+ break
+ }
+ }
+ }
+
+ param := openapiParameterObject{
+ Description: desc,
+ In: "query",
+ Default: schema.Default,
+ Type: schema.Type,
+ Items: schema.Items,
+ Format: schema.Format,
+ Pattern: schema.Pattern,
+ Required: required,
+ UniqueItems: schema.UniqueItems,
+ extensions: schema.extensions,
+ Enum: schema.Enum,
+ }
+ if param.Type == "array" {
+ param.CollectionFormat = "multi"
+ }
+
+ param.Name = prefix + reg.FieldName(field)
+
+ if isEnum {
+ enum, err := reg.LookupEnum("", fieldType)
+ if err != nil {
+ return nil, fmt.Errorf("unknown enum type %s", fieldType)
+ }
+ if items != nil { // array
+ param.Items = &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Enum: listEnumNames(reg, enum),
+ },
+ }
+ if reg.GetEnumsAsInts() {
+ param.Items.Type = "integer"
+ param.Items.Enum = listEnumNumbers(reg, enum)
+ }
+ } else {
+ param.Type = "string"
+ param.Enum = listEnumNames(reg, enum)
+ param.Default = getEnumDefault(reg, enum)
+ if reg.GetEnumsAsInts() {
+ param.Type = "integer"
+ param.Enum = listEnumNumbers(reg, enum)
+ param.Default = getEnumDefaultNumber(reg, enum)
+ }
+ }
+ valueComments := enumValueProtoComments(reg, enum)
+ if valueComments != "" {
+ param.Description = strings.TrimLeft(param.Description+"\n\n "+valueComments, "\n")
+ }
+ }
+ return []openapiParameterObject{param}, nil
+ }
+
+ // nested type, recurse
+ msg, err := reg.LookupMsg("", fieldType)
+ if err != nil {
+ return nil, fmt.Errorf("unknown message type %s", fieldType)
+ }
+
+ // Check for cyclical message reference:
+ if ok := cycle.Check(*msg.Name); !ok {
+ return nil, fmt.Errorf("exceeded recursive count (%d) for query parameter %q", cycle.count, fieldType)
+ }
+
+ // Construct a new map with the message name so a cycle further down the recursive path can be detected.
+ // Do not keep anything in the original touched reference and do not pass that reference along. This will
+ // prevent clobbering adjacent records while recursing.
+ touchedOut := cycle.Branch()
+
+ for _, nestedField := range msg.Fields {
+ if !isVisible(getFieldVisibilityOption(nestedField), reg) {
+ continue
+ }
+
+ fieldName := reg.FieldName(field)
+ p, err := nestedQueryParams(msg, nestedField, prefix+fieldName+".", reg, pathParams, body, touchedOut)
+ if err != nil {
+ return nil, err
+ }
+ params = append(params, p...)
+ }
+ return params, nil
+}
+
+func getMapParamKey(t descriptorpb.FieldDescriptorProto_Type) (string, error) {
+ tType, f, ok := primitiveSchema(t)
+ if !ok || f == "byte" || f == "float" || f == "double" {
+ return "", fmt.Errorf("unsupported type: %q", f)
+ }
+ return tType, nil
+}
+
+// findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service.
+func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, ms messageMap, e enumMap, refs refMap) {
+ for _, svc := range s {
+ if !isVisible(getServiceVisibilityOption(svc), reg) {
+ continue
+ }
+
+ for _, meth := range svc.Methods {
+ // Request may be fully included in query
+ {
+ if !isVisible(getMethodVisibilityOption(meth), reg) {
+ continue
+ }
+
+ swgReqName, ok := fullyQualifiedNameToOpenAPIName(meth.RequestType.FQMN(), reg)
+ if !ok {
+ grpclog.Errorf("couldn't resolve OpenAPI name for FQMN %q", meth.RequestType.FQMN())
+ continue
+ }
+ if _, ok := refs[fmt.Sprintf("#/definitions/%s", swgReqName)]; ok {
+ if !skipRenderingRef(meth.RequestType.FQMN()) {
+ m[swgReqName] = meth.RequestType
+ }
+ }
+ }
+
+ swgRspName, ok := fullyQualifiedNameToOpenAPIName(meth.ResponseType.FQMN(), reg)
+ if !ok && !skipRenderingRef(meth.ResponseType.FQMN()) {
+ grpclog.Errorf("couldn't resolve OpenAPI name for FQMN %q", meth.ResponseType.FQMN())
+ continue
+ }
+
+ findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
+
+ if !skipRenderingRef(meth.ResponseType.FQMN()) {
+ m[swgRspName] = meth.ResponseType
+ }
+ findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
+ }
+ }
+}
+
+// findNestedMessagesAndEnumerations those can be generated by the services.
+func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descriptor.Registry, m messageMap, e enumMap) {
+ // Iterate over all the fields that
+ for _, t := range message.Fields {
+ if !isVisible(getFieldVisibilityOption(t), reg) {
+ continue
+ }
+
+ fieldType := t.GetTypeName()
+ // If the type is an empty string then it is a proto primitive
+ if fieldType != "" {
+ if _, ok := m[fieldType]; !ok {
+ msg, err := reg.LookupMsg("", fieldType)
+ if err != nil {
+ enum, err := reg.LookupEnum("", fieldType)
+ if err != nil {
+ panic(err)
+ }
+ e[fieldType] = enum
+ continue
+ }
+ m[fieldType] = msg
+ findNestedMessagesAndEnumerations(msg, reg, m, e)
+ }
+ }
+ }
+}
+
+func skipRenderingRef(refName string) bool {
+ _, ok := wktSchemas[refName]
+ return ok
+}
+
+func renderMessageAsDefinition(msg *descriptor.Message, reg *descriptor.Registry, customRefs refMap, pathParams []descriptor.Parameter) (openapiSchemaObject, error) {
+ schema := openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ }
+
+ if reg.GetGenerateXGoType() && msg.File.GoPkg.Path != "" {
+ if schema.extensions == nil {
+ schema.extensions = []extension{}
+ }
+ goTypeName := msg.GetName()
+
+ goTypeName = casing.JSONCamelCase(goTypeName)
+ schema.extensions = append(schema.extensions, extension{
+ key: "x-go-type",
+ value: json.RawMessage(`{
+ "import": {
+ "package": "` + msg.File.GoPkg.Path + `"
+ },
+ "type": "` + goTypeName + `"
+ }`),
+ })
+ }
+
+ msgComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index))
+ if err := updateOpenAPIDataFromComments(reg, &schema, msg, msgComments, false); err != nil {
+ return openapiSchemaObject{}, err
+ }
+ opts, err := getMessageOpenAPIOption(reg, msg)
+ if err != nil {
+ return openapiSchemaObject{}, err
+ }
+ if opts != nil {
+ protoSchema := openapiSchemaFromProtoSchema(opts, reg, customRefs, msg)
+
+ // Warning: Make sure not to overwrite any fields already set on the schema type.
+ schema.ExternalDocs = protoSchema.ExternalDocs
+ schema.ReadOnly = protoSchema.ReadOnly
+ schema.MultipleOf = protoSchema.MultipleOf
+ schema.Maximum = protoSchema.Maximum
+ schema.ExclusiveMaximum = protoSchema.ExclusiveMaximum
+ schema.Minimum = protoSchema.Minimum
+ schema.ExclusiveMinimum = protoSchema.ExclusiveMinimum
+ schema.MaxLength = protoSchema.MaxLength
+ schema.MinLength = protoSchema.MinLength
+ schema.Pattern = protoSchema.Pattern
+ schema.Default = protoSchema.Default
+ schema.MaxItems = protoSchema.MaxItems
+ schema.MinItems = protoSchema.MinItems
+ schema.UniqueItems = protoSchema.UniqueItems
+ schema.MaxProperties = protoSchema.MaxProperties
+ schema.MinProperties = protoSchema.MinProperties
+ schema.Required = protoSchema.Required
+ schema.XNullable = protoSchema.XNullable
+ schema.extensions = protoSchema.extensions
+ if protoSchema.schemaCore.Type != "" || protoSchema.schemaCore.Ref != "" {
+ schema.schemaCore = protoSchema.schemaCore
+ }
+ if protoSchema.Title != "" {
+ schema.Title = protoSchema.Title
+ }
+ if protoSchema.Description != "" {
+ schema.Description = protoSchema.Description
+ }
+ if protoSchema.Example != nil {
+ schema.Example = protoSchema.Example
+ }
+ }
+
+ schema.Required = filterOutExcludedFields(schema.Required, pathParams)
+
+ for _, f := range msg.Fields {
+ if !isVisible(getFieldVisibilityOption(f), reg) {
+ continue
+ }
+
+ if shouldExcludeField(f.GetName(), pathParams) {
+ continue
+ }
+ subPathParams := subPathParams(f.GetName(), pathParams)
+ fieldSchema, err := renderFieldAsDefinition(f, reg, customRefs, subPathParams)
+ if err != nil {
+ return openapiSchemaObject{}, err
+ }
+ comments := fieldProtoComments(reg, msg, f)
+ if err := updateOpenAPIDataFromComments(reg, &fieldSchema, f, comments, false); err != nil {
+ return openapiSchemaObject{}, err
+ }
+
+ if requiredIdx := find(schema.Required, *f.Name); requiredIdx != -1 && reg.GetUseJSONNamesForFields() {
+ schema.Required[requiredIdx] = f.GetJsonName()
+ }
+
+ if fieldSchema.Required != nil {
+ schema.Required = getUniqueFields(schema.Required, fieldSchema.Required)
+ schema.Required = append(schema.Required, fieldSchema.Required...)
+ // To avoid populating both the field schema require and message schema require, unset the field schema require.
+ // See issue #2635.
+ fieldSchema.Required = nil
+ }
+
+ if reg.GetUseAllOfForRefs() {
+ if fieldSchema.Ref != "" {
+ // Per the JSON Reference syntax: Any members other than "$ref" in a JSON Reference object SHALL be ignored.
+ // https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3
+ // However, use allOf to specify Title/Description/Example/readOnly fields.
+ if fieldSchema.Title != "" || fieldSchema.Description != "" || len(fieldSchema.Example) > 0 || fieldSchema.ReadOnly {
+ fieldSchema = openapiSchemaObject{
+ Title: fieldSchema.Title,
+ Description: fieldSchema.Description,
+ schemaCore: schemaCore{
+ Example: fieldSchema.Example,
+ },
+ ReadOnly: fieldSchema.ReadOnly,
+ AllOf: []allOfEntry{{Ref: fieldSchema.Ref}},
+ }
+ } else {
+ fieldSchema = openapiSchemaObject{schemaCore: schemaCore{Ref: fieldSchema.Ref}}
+ }
+ }
+ }
+
+ kv := keyVal{Value: fieldSchema}
+ kv.Key = reg.FieldName(f)
+ if schema.Properties == nil {
+ schema.Properties = &openapiSchemaObjectProperties{}
+ }
+ *schema.Properties = append(*schema.Properties, kv)
+ }
+
+ if msg.FQMN() == ".google.protobuf.Any" {
+ transformAnyForJSON(&schema, reg.GetUseJSONNamesForFields())
+ }
+
+ return schema, nil
+}
+
+func renderFieldAsDefinition(f *descriptor.Field, reg *descriptor.Registry, refs refMap, pathParams []descriptor.Parameter) (openapiSchemaObject, error) {
+ if len(pathParams) == 0 {
+ return schemaOfField(f, reg, refs), nil
+ }
+ location := ""
+ if ix := strings.LastIndex(f.Message.FQMN(), "."); ix > 0 {
+ location = f.Message.FQMN()[0:ix]
+ }
+ msg, err := reg.LookupMsg(location, f.GetTypeName())
+ if err != nil {
+ return openapiSchemaObject{}, err
+ }
+ schema, err := renderMessageAsDefinition(msg, reg, refs, pathParams)
+ if err != nil {
+ return openapiSchemaObject{}, err
+ }
+ comments := fieldProtoComments(reg, f.Message, f)
+ if len(comments) > 0 {
+ // Use title and description from field instead of nested message if present.
+ paragraphs := strings.Split(comments, paragraphDeliminator)
+ schema.Title = strings.TrimSpace(paragraphs[0])
+ schema.Description = strings.TrimSpace(strings.Join(paragraphs[1:], paragraphDeliminator))
+ }
+
+ // to handle case where path param is present inside the field of descriptorpb.FieldDescriptorProto_TYPE_MESSAGE type
+ // it still needs to consider the behaviour of the field which was being done by schemaOfField() in case there are no path params
+ if j, err := getFieldBehaviorOption(reg, f); err == nil {
+ updateSwaggerObjectFromFieldBehavior(&schema, j, reg, f)
+ }
+
+ return schema, nil
+}
+
+// transformAnyForJSON should be called when the schema object represents a google.protobuf.Any, and will replace the
+// Properties slice with a single value for '@type'. We mutate the incorrectly named field so that we inherit the same
+// documentation as specified on the original field in the protobuf descriptors.
+func transformAnyForJSON(schema *openapiSchemaObject, useJSONNames bool) {
+ var typeFieldName string
+ if useJSONNames {
+ typeFieldName = "typeUrl"
+ } else {
+ typeFieldName = "type_url"
+ }
+
+ for _, property := range *schema.Properties {
+ if property.Key == typeFieldName {
+ schema.AdditionalProperties = &openapiSchemaObject{}
+ schema.Properties = &openapiSchemaObjectProperties{keyVal{
+ Key: "@type",
+ Value: property.Value,
+ }}
+ break
+ }
+ }
+}
+
+func renderMessagesAsDefinition(messages messageMap, d openapiDefinitionsObject, reg *descriptor.Registry, customRefs refMap, pathParams []descriptor.Parameter) error {
+ for name, msg := range messages {
+ swgName, ok := fullyQualifiedNameToOpenAPIName(msg.FQMN(), reg)
+ if !ok {
+ return fmt.Errorf("can't resolve OpenAPI name from %q", msg.FQMN())
+ }
+ if skipRenderingRef(name) {
+ continue
+ }
+
+ if opt := msg.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
+ continue
+ }
+ var err error
+ d[swgName], err = renderMessageAsDefinition(msg, reg, customRefs, pathParams)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// isVisible checks if a field/RPC is visible based on the visibility restriction
+// combined with the `visibility_restriction_selectors`.
+// Elements with an overlap on `visibility_restriction_selectors` are visible, those without are not visible.
+// Elements without `google.api.VisibilityRule` annotations entirely are always visible.
+func isVisible(r *visibility.VisibilityRule, reg *descriptor.Registry) bool {
+ if r == nil {
+ return true
+ }
+
+ restrictions := strings.Split(strings.TrimSpace(r.Restriction), ",")
+ // No restrictions results in the element always being visible
+ if len(restrictions) == 0 {
+ return true
+ }
+
+ for _, restriction := range restrictions {
+ if reg.GetVisibilityRestrictionSelectors()[strings.TrimSpace(restriction)] {
+ return true
+ }
+ }
+
+ return false
+}
+
+func shouldExcludeField(name string, excluded []descriptor.Parameter) bool {
+ for _, p := range excluded {
+ if len(p.FieldPath) == 1 && name == p.FieldPath[0].Name {
+ return true
+ }
+ }
+ return false
+}
+
+func filterOutExcludedFields(fields []string, excluded []descriptor.Parameter) []string {
+ var filtered []string
+ for _, f := range fields {
+ if !shouldExcludeField(f, excluded) {
+ filtered = append(filtered, f)
+ }
+ }
+ return filtered
+}
+
+// schemaOfField returns a OpenAPI Schema Object for a protobuf field.
+func schemaOfField(f *descriptor.Field, reg *descriptor.Registry, refs refMap) openapiSchemaObject {
+ const (
+ singular = 0
+ array = 1
+ object = 2
+ )
+ var (
+ core schemaCore
+ aggregate int
+ )
+
+ fd := f.FieldDescriptorProto
+ location := ""
+ if ix := strings.LastIndex(f.Message.FQMN(), "."); ix > 0 {
+ location = f.Message.FQMN()[0:ix]
+ }
+ if m, err := reg.LookupMsg(location, f.GetTypeName()); err == nil {
+ if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
+ fd = m.GetField()[1]
+ aggregate = object
+ }
+ }
+ if fd.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
+ aggregate = array
+ }
+
+ var props *openapiSchemaObjectProperties
+
+ switch ft := fd.GetType(); ft {
+ case descriptorpb.FieldDescriptorProto_TYPE_ENUM, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, descriptorpb.FieldDescriptorProto_TYPE_GROUP:
+ if wktSchema, ok := wktSchemas[fd.GetTypeName()]; ok {
+ core = wktSchema
+ if fd.GetTypeName() == ".google.protobuf.Empty" {
+ props = &openapiSchemaObjectProperties{}
+ }
+ } else {
+ swgRef, ok := fullyQualifiedNameToOpenAPIName(fd.GetTypeName(), reg)
+ if !ok {
+ panic(fmt.Sprintf("can't resolve OpenAPI ref from typename %q", fd.GetTypeName()))
+ }
+ core = schemaCore{
+ Ref: "#/definitions/" + swgRef,
+ }
+ if refs != nil {
+ refs[fd.GetTypeName()] = struct{}{}
+ }
+ }
+ default:
+ ftype, format, ok := primitiveSchema(ft)
+ if ok {
+ core = schemaCore{Type: ftype, Format: format}
+ } else {
+ core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
+ }
+ }
+
+ ret := openapiSchemaObject{}
+
+ switch aggregate {
+ case array:
+ if _, ok := wktSchemas[fd.GetTypeName()]; !ok && fd.GetType() == descriptorpb.FieldDescriptorProto_TYPE_MESSAGE {
+ core.Type = "object"
+ }
+ ret = openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: (*openapiItemsObject)(&openapiSchemaObject{schemaCore: core}),
+ },
+ }
+ case object:
+ ret = openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ AdditionalProperties: &openapiSchemaObject{Properties: props, schemaCore: core},
+ }
+ default:
+ ret = openapiSchemaObject{
+ schemaCore: core,
+ Properties: props,
+ }
+ }
+
+ if j, err := getFieldOpenAPIOption(reg, f); err == nil {
+ updateswaggerObjectFromJSONSchema(&ret, j, reg, f)
+ }
+
+ if j, err := getFieldBehaviorOption(reg, f); err == nil {
+ updateSwaggerObjectFromFieldBehavior(&ret, j, reg, f)
+ }
+
+ for i, required := range ret.Required {
+ if required == f.GetName() {
+ ret.Required[i] = reg.FieldName(f)
+ }
+ }
+
+ slices.Sort(ret.Required)
+ ret.Required = slices.Compact(ret.Required)
+
+ if reg.GetProto3OptionalNullable() && f.GetProto3Optional() {
+ ret.XNullable = true
+ }
+
+ return ret
+}
+
+// primitiveSchema returns a pair of "Type" and "Format" in JSON Schema for
+// the given primitive field type.
+// The last return parameter is true iff the field type is actually primitive.
+func primitiveSchema(t descriptorpb.FieldDescriptorProto_Type) (ftype, format string, ok bool) {
+ switch t {
+ case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
+ return "number", "double", true
+ case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
+ return "number", "float", true
+ case descriptorpb.FieldDescriptorProto_TYPE_INT64:
+ return "string", "int64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_UINT64:
+ // 64bit integer types are marshaled as string in the default JSONPb marshaler.
+ // TODO(yugui) Add an option to declare 64bit integers as int64.
+ //
+ // NOTE: uint64 is not a predefined format of integer type in OpenAPI spec.
+ // So we cannot expect that uint64 is commonly supported by OpenAPI processor.
+ return "string", "uint64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_INT32:
+ return "integer", "int32", true
+ case descriptorpb.FieldDescriptorProto_TYPE_FIXED64:
+ // Ditto.
+ return "string", "uint64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_FIXED32:
+ // Ditto.
+ return "integer", "int64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
+ // NOTE: in OpenAPI specification, format should be empty on boolean type
+ return "boolean", "", true
+ case descriptorpb.FieldDescriptorProto_TYPE_STRING:
+ // NOTE: in OpenAPI specification, can be empty on string type
+ // see: https://swagger.io/specification/v2/#data-types
+ return "string", "", true
+ case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
+ return "string", "byte", true
+ case descriptorpb.FieldDescriptorProto_TYPE_UINT32:
+ // Ditto.
+ return "integer", "int64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32:
+ return "integer", "int32", true
+ case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64:
+ return "string", "int64", true
+ case descriptorpb.FieldDescriptorProto_TYPE_SINT32:
+ return "integer", "int32", true
+ case descriptorpb.FieldDescriptorProto_TYPE_SINT64:
+ return "string", "int64", true
+ default:
+ return "", "", false
+ }
+}
+
+// renderEnumerationsAsDefinition inserts enums into the definitions object.
+func renderEnumerationsAsDefinition(enums enumMap, d openapiDefinitionsObject, reg *descriptor.Registry, customRefs refMap) {
+ for _, enum := range enums {
+ swgName, ok := fullyQualifiedNameToOpenAPIName(enum.FQEN(), reg)
+ if !ok {
+ panic(fmt.Sprintf("can't resolve OpenAPI name from FQEN %q", enum.FQEN()))
+ }
+ enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
+
+ // it may be necessary to sort the result of the GetValue function.
+ enumNames := listEnumNames(reg, enum)
+ defaultValue := getEnumDefault(reg, enum)
+ valueComments := enumValueProtoComments(reg, enum)
+ if valueComments != "" {
+ enumComments = strings.TrimLeft(enumComments+"\n\n "+valueComments, "\n")
+ }
+ enumSchemaObject := openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Enum: enumNames,
+ Default: defaultValue,
+ },
+ }
+
+ if reg.GetEnumsAsInts() {
+ enumSchemaObject.Type = "integer"
+ enumSchemaObject.Format = "int32"
+ enumSchemaObject.Default = getEnumDefaultNumber(reg, enum)
+ enumSchemaObject.Enum = listEnumNumbers(reg, enum)
+ }
+ opts, err := getEnumOpenAPIOption(reg, enum)
+ if err != nil {
+ panic(err)
+ }
+ if opts != nil {
+ protoSchema := openapiSchemaFromProtoEnumSchema(opts, reg, customRefs, enum)
+ // Warning: Make sure not to overwrite any fields already set on the schema type.
+ // This is only a subset of the fields from JsonSchema since most of them only apply to arrays or objects not enums
+ enumSchemaObject.ExternalDocs = protoSchema.ExternalDocs
+ enumSchemaObject.ReadOnly = protoSchema.ReadOnly
+ enumSchemaObject.extensions = protoSchema.extensions
+ if protoSchema.Type != "" || protoSchema.Ref != "" {
+ enumSchemaObject.schemaCore = protoSchema.schemaCore
+ }
+ if protoSchema.Title != "" {
+ enumSchemaObject.Title = protoSchema.Title
+ }
+ if protoSchema.Description != "" {
+ enumSchemaObject.Description = protoSchema.Description
+ }
+ if protoSchema.Example != nil {
+ enumSchemaObject.Example = protoSchema.Example
+ }
+ }
+ if err := updateOpenAPIDataFromComments(reg, &enumSchemaObject, enum, enumComments, false); err != nil {
+ panic(err)
+ }
+
+ d[swgName] = enumSchemaObject
+ }
+}
+
+// Take in a FQMN or FQEN and return a OpenAPI safe version of the FQMN and
+// a boolean indicating if FQMN was properly resolved.
+func fullyQualifiedNameToOpenAPIName(fqn string, reg *descriptor.Registry) (string, bool) {
+ registriesSeenMutex.Lock()
+ defer registriesSeenMutex.Unlock()
+ if mapping, present := registriesSeen[reg]; present {
+ ret, ok := mapping[fqn]
+ return ret, ok
+ }
+ mapping := resolveFullyQualifiedNameToOpenAPINames(append(reg.GetAllFQMNs(), append(reg.GetAllFQENs(), reg.GetAllFQMethNs()...)...), reg.GetOpenAPINamingStrategy())
+ registriesSeen[reg] = mapping
+ ret, ok := mapping[fqn]
+ return ret, ok
+}
+
+// Lookup message type by location.name and return an openapiv2-safe version
+// of its FQMN.
+func lookupMsgAndOpenAPIName(location, name string, reg *descriptor.Registry) (*descriptor.Message, string, error) {
+ msg, err := reg.LookupMsg(location, name)
+ if err != nil {
+ return nil, "", err
+ }
+ swgName, ok := fullyQualifiedNameToOpenAPIName(msg.FQMN(), reg)
+ if !ok {
+ return nil, "", fmt.Errorf("can't map OpenAPI name from FQMN %q", msg.FQMN())
+ }
+ return msg, swgName, nil
+}
+
+// registriesSeen is used to memoise calls to resolveFullyQualifiedNameToOpenAPINames so
+// we don't repeat it unnecessarily, since it can take some time.
+var (
+ registriesSeen = map[*descriptor.Registry]map[string]string{}
+ registriesSeenMutex sync.Mutex
+)
+
+// Take the names of every proto message and generate a unique reference for each, according to the given strategy.
+func resolveFullyQualifiedNameToOpenAPINames(messages []string, namingStrategy string) map[string]string {
+ strategyFn := LookupNamingStrategy(namingStrategy)
+ if strategyFn == nil {
+ return nil
+ }
+ return strategyFn(messages)
+}
+
+var canRegexp = regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*)([^}]*)}")
+
+// templateToParts splits a URL template into path segments for use by `partsToOpenAPIPath` and `partsToRegexpMap`.
+//
+// Parameters:
+// - path: The URL template as defined by https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
+// - reg: The descriptor registry used to read compiler flags
+// - fields: The fields of the request message, only used when `useJSONNamesForFields` is true
+// - msgs: The Messages of the service binding, only used when `useJSONNamesForFields` is true
+//
+// Returns:
+//
+// The path segments of the URL template.
+func templateToParts(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) []string {
+ // It seems like the right thing to do here is to just use
+ // strings.Split(path, "/") but that breaks badly when you hit a url like
+ // /{my_field=prefix/*}/ and end up with 2 sections representing my_field.
+ // Instead do the right thing and write a small pushdown (counter) automata
+ // for it.
+ var parts []string
+ depth := 0
+ buffer := ""
+ for i, char := range path {
+ switch char {
+ case '{':
+ // Push on the stack
+ depth++
+ buffer += string(char)
+ case '}':
+ if depth == 0 {
+ panic("Encountered } without matching { before it.")
+ }
+ // Pop from the stack
+ depth--
+ if !reg.GetUseJSONNamesForFields() {
+ buffer += string(char)
+ continue
+ }
+ paramNameProto := strings.SplitN(buffer[1:], "=", 2)[0]
+ paramNameCamelCase := lowerCamelCase(paramNameProto, fields, msgs)
+ buffer = strings.Join([]string{"{", paramNameCamelCase, buffer[len(paramNameProto)+1:], "}"}, "")
+ case '/':
+ if depth == 0 {
+ parts = append(parts, buffer)
+ buffer = ""
+ // Since the stack was empty when we hit the '/' we are done with this
+ // section.
+ continue
+ }
+ buffer += string(char)
+ case ':':
+ if depth == 0 {
+ // Only treat this as a verb if we're at the end of the path or
+ // if there are no more path segments (only more literals after the colon)
+ remainingPath := path[i:]
+ if !strings.Contains(remainingPath, "/") {
+ parts = append(parts, buffer)
+ verbSegment := remainingPath
+ if reg.GetUseJSONNamesForFields() {
+ verbSegment = processParametersInSegment(verbSegment, fields, msgs)
+ }
+ parts = append(parts, verbSegment)
+ return parts
+ }
+ }
+ buffer += string(char)
+ default:
+ buffer += string(char)
+ }
+ }
+
+ // Now append the last element to parts
+ parts = append(parts, buffer)
+
+ return parts
+}
+
+// processParametersInSegment processes a path segment (like ":verb/{param}") to convert
+// parameter names to camelCase while preserving the overall structure
+func processParametersInSegment(segment string, fields []*descriptor.Field, msgs []*descriptor.Message) string {
+ result := segment
+ depth := 0
+ var paramStart int
+ for i, char := range segment {
+ switch char {
+ case '{':
+ if depth == 0 {
+ paramStart = i
+ }
+ depth++
+ case '}':
+ depth--
+ if depth == 0 {
+ paramContent := segment[paramStart+1 : i]
+ paramNameProto := strings.SplitN(paramContent, "=", 2)[0]
+ paramNameCamelCase := lowerCamelCase(paramNameProto, fields, msgs)
+
+ oldParam := "{" + paramContent + "}"
+ newParam := "{" + paramNameCamelCase
+ if strings.Contains(paramContent, "=") {
+ newParam += paramContent[len(paramNameProto):]
+ }
+ newParam += "}"
+
+ result = strings.Replace(result, oldParam, newParam, 1)
+ }
+ }
+ }
+ return result
+}
+
+// partsToOpenAPIPath converts each path part of the form /path/{string_value=strprefix/*} which is defined in
+// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto to the OpenAPI expected form /path/{string_value}.
+// For example this would replace the path segment of "{foo=bar/*}" with "{foo}" or "prefix{bang=bash/**}" with "prefix{bang}".
+// OpenAPI 2 only allows simple path parameters with the constraints on that parameter specified in the OpenAPI
+// schema's "pattern" instead of in the path parameter itself.
+func partsToOpenAPIPath(parts []string, overrides map[string]string) string {
+ for index, part := range parts {
+ part = canRegexp.ReplaceAllString(part, "{$1}")
+
+ if override, ok := overrides[part]; ok {
+ part = override
+ }
+ parts[index] = part
+ }
+ if last := len(parts) - 1; strings.HasPrefix(parts[last], ":") {
+ // Last item is a verb (":" LITERAL).
+ return strings.Join(parts[:last], "/") + parts[last]
+ }
+ return strings.Join(parts, "/")
+}
+
+// partsToRegexpMap returns a map of parameter name to ECMA 262 patterns
+// which is what the "pattern" field on an OpenAPI parameter expects.
+// See https://swagger.io/specification/v2/ (Parameter Object) and
+// https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
+// The expression is generated based on expressions defined by https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
+// "Path Template Syntax" section which allow for a "param_name=foobar/*/bang/**" style expressions inside
+// the path parameter placeholders that indicate constraints on the values of those parameters.
+// This function will scan the split parts of a path template for parameters and
+// outputs a map of the name of the parameter to a ECMA regular expression. See the http.proto file for descriptions
+// of the supported syntax. This function will ignore any path parameters that don't contain a "=" after the
+// parameter name. For supported parameters, we assume "*" represent all characters except "/" as it's
+// intended to match a single path element and we assume "**" matches any character as it's intended to match multiple
+// path elements.
+// For example "{name=organizations/*/roles/*}" would produce the regular expression for the "name" parameter of
+// "organizations/[^/]+/roles/[^/]+" or "{bar=bing/*/bang/**}" would produce the regular expression for the "bar"
+// parameter of "bing/[^/]+/bang/.+".
+//
+// Note that OpenAPI does not actually support path parameters with "/", see https://github.com/OAI/OpenAPI-Specification/issues/892
+func partsToRegexpMap(parts []string) map[string]string {
+ regExps := make(map[string]string)
+ for _, part := range parts {
+ if strings.Contains(part, "/") {
+ grpclog.Warningf("Path parameter %q contains '/', which is not supported in OpenAPI", part)
+ }
+ if submatch := canRegexp.FindStringSubmatch(part); len(submatch) > 2 {
+ if strings.HasPrefix(submatch[2], "=") { // this part matches the standard and should be made into a regular expression
+ // assume the string's characters other than "**" and "*" are literals (not necessarily a good assumption 100% of the times, but it will support most use cases)
+ regex := submatch[2][1:]
+ regex = strings.ReplaceAll(regex, "**", ".+") // ** implies any character including "/"
+ regex = strings.ReplaceAll(regex, "*", "[^/]+") // * implies any character except "/"
+ regExps[submatch[1]] = regex
+ }
+ }
+ }
+ return regExps
+}
+
+func renderServiceTags(services []*descriptor.Service, reg *descriptor.Registry) []openapiTagObject {
+ var tags []openapiTagObject
+ for _, svc := range services {
+ if !isVisible(getServiceVisibilityOption(svc), reg) {
+ continue
+ }
+ tagName := svc.GetName()
+ if pkg := svc.File.GetPackage(); pkg != "" && reg.IsIncludePackageInTags() {
+ tagName = pkg + "." + tagName
+ }
+
+ tag := openapiTagObject{
+ Name: tagName,
+ }
+
+ opts, err := getServiceOpenAPIOption(reg, svc)
+ if err != nil {
+ grpclog.Error(err)
+ return nil
+ }
+ if opts != nil {
+ tag.Description = opts.Description
+ if reg.GetUseGoTemplate() {
+ tag.Description = goTemplateComments(tag.Description, svc, reg)
+ }
+ if opts.ExternalDocs != nil {
+ tag.ExternalDocs = &openapiExternalDocumentationObject{
+ Description: opts.ExternalDocs.Description,
+ URL: opts.ExternalDocs.Url,
+ }
+ if reg.GetUseGoTemplate() {
+ tag.ExternalDocs.Description = goTemplateComments(opts.ExternalDocs.Description, svc, reg)
+ }
+ }
+ if opts.GetName() != "" {
+ tag.Name = opts.GetName()
+ }
+ }
+ tags = append(tags, tag)
+ }
+ return tags
+}
+
+// expandPathPatterns searches the URI parts for path parameters with pattern and when the pattern contains a sub-path,
+// it expands the pattern into the URI parts and adds the new path parameters to the pathParams slice.
+//
+// Parameters:
+// - pathParts: the URI parts parsed from the path template with `templateToParts` function
+// - pathParams: the path parameters of the service binding
+//
+// Returns:
+//
+// The modified pathParts and pathParams slice.
+func expandPathPatterns(pathParts []string, pathParams []descriptor.Parameter, reg *descriptor.Registry) ([]string, []descriptor.Parameter) {
+ expandedPathParts := []string{}
+ modifiedPathParams := pathParams
+ for _, pathPart := range pathParts {
+ if !strings.HasPrefix(pathPart, "{") || !strings.HasSuffix(pathPart, "}") {
+ expandedPathParts = append(expandedPathParts, pathPart)
+ continue
+ }
+ woBraces := pathPart[1 : len(pathPart)-1]
+ paramPattern := strings.SplitN(woBraces, "=", 2)
+ if len(paramPattern) != 2 {
+ expandedPathParts = append(expandedPathParts, pathPart)
+ continue
+ }
+ paramName := paramPattern[0]
+ pattern := paramPattern[1]
+ if pattern == "*" {
+ expandedPathParts = append(expandedPathParts, pathPart)
+ continue
+ }
+ pathParamIndex := slices.IndexFunc(modifiedPathParams, func(p descriptor.Parameter) bool {
+ if !reg.GetUseJSONNamesForFields() {
+ return p.FieldPath.String() == paramName
+ }
+ fieldPath := casing.JSONCamelCase(p.FieldPath.String())
+ return fieldPath == paramName
+ })
+ if pathParamIndex == -1 {
+ panic(fmt.Sprintf("Path parameter %q not found in path parameters", paramName))
+ }
+ pathParam := modifiedPathParams[pathParamIndex]
+ patternParts := strings.Split(pattern, "/")
+ for _, patternPart := range patternParts {
+ if patternPart != "*" {
+ expandedPathParts = append(expandedPathParts, patternPart)
+ continue
+ }
+ lastPart := expandedPathParts[len(expandedPathParts)-1]
+ paramName := strings.TrimSuffix(lastPart, "s")
+ if reg.GetUseJSONNamesForFields() {
+ paramName = casing.JSONCamelCase(paramName)
+ }
+ expandedPathParts = append(expandedPathParts, "{"+paramName+"}")
+ newParam := descriptor.Parameter{
+ Target: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String(paramName),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ Message: pathParam.Target.Message,
+ FieldMessage: pathParam.Target.FieldMessage,
+ ForcePrefixedName: pathParam.Target.ForcePrefixedName,
+ },
+ FieldPath: []descriptor.FieldPathComponent{{
+ Name: paramName,
+ Target: nil,
+ }},
+ Method: nil,
+ }
+ modifiedPathParams = append(modifiedPathParams, newParam)
+ if pathParamIndex != -1 {
+ // the new parameter from the pattern replaces the old path parameter
+ modifiedPathParams = append(modifiedPathParams[:pathParamIndex], modifiedPathParams[pathParamIndex+1:]...)
+ pathParamIndex = -1
+ }
+ }
+ }
+ return expandedPathParts, modifiedPathParams
+}
+
+func renderServices(services []*descriptor.Service, paths *openapiPathsObject, reg *descriptor.Registry, requestResponseRefs, customRefs refMap, msgs []*descriptor.Message, defs openapiDefinitionsObject) error {
+ // Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array.
+ svcBaseIdx := 0
+ var lastFile *descriptor.File = nil
+ for svcIdx, svc := range services {
+ if svc.File != lastFile {
+ lastFile = svc.File
+ svcBaseIdx = svcIdx
+ }
+
+ if !isVisible(getServiceVisibilityOption(svc), reg) {
+ continue
+ }
+
+ for methIdx, meth := range svc.Methods {
+ if !isVisible(getMethodVisibilityOption(meth), reg) {
+ continue
+ }
+
+ deprecated := reg.GetEnableRpcDeprecation() && meth.GetOptions().GetDeprecated()
+
+ for bIdx, b := range meth.Bindings {
+ operationFunc := operationForMethod(b.HTTPMethod)
+ // Iterate over all the OpenAPI parameters
+ parameters := openapiParametersObject{}
+ // split the path template into its parts
+ parts := templateToParts(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)
+ pathParams := b.PathParams
+ if reg.GetExpandSlashedPathPatterns() {
+ parts, pathParams = expandPathPatterns(parts, pathParams, reg)
+ }
+ // extract any constraints specified in the path placeholders into ECMA regular expressions
+ pathParamRegexpMap := partsToRegexpMap(parts)
+ // Keep track of path parameter overrides
+ pathParamNames := make(map[string]string)
+ for _, parameter := range pathParams {
+
+ var paramType, paramFormat, desc, collectionFormat string
+ var defaultValue interface{}
+ var enumNames interface{}
+ var items *openapiItemsObject
+ var minItems *int
+ var extensions []extension
+ switch pt := parameter.Target.GetType(); pt {
+ case descriptorpb.FieldDescriptorProto_TYPE_GROUP, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE:
+ if descriptor.IsWellKnownType(parameter.Target.GetTypeName()) {
+ if parameter.IsRepeated() {
+ return errors.New("only primitive and enum types are allowed in repeated path parameters")
+ }
+ schema := schemaOfField(parameter.Target, reg, customRefs)
+ paramType = schema.Type
+ paramFormat = schema.Format
+ desc = schema.Description
+ defaultValue = schema.Default
+ extensions = schema.extensions
+ } else {
+ return errors.New("only primitive and well-known types are allowed in path parameters")
+ }
+ case descriptorpb.FieldDescriptorProto_TYPE_ENUM:
+ enum, err := reg.LookupEnum("", parameter.Target.GetTypeName())
+ if err != nil {
+ return err
+ }
+ paramType = "string"
+ paramFormat = ""
+ enumNames = listEnumNames(reg, enum)
+ if reg.GetEnumsAsInts() {
+ paramType = "integer"
+ paramFormat = ""
+ enumNames = listEnumNumbers(reg, enum)
+ }
+
+ schema := schemaOfField(parameter.Target, reg, customRefs)
+ desc = schema.Description
+ defaultValue = schema.Default
+ extensions = schema.extensions
+ default:
+ var ok bool
+ paramType, paramFormat, ok = primitiveSchema(pt)
+ if !ok {
+ return fmt.Errorf("unknown field type %v", pt)
+ }
+
+ schema := schemaOfField(parameter.Target, reg, customRefs)
+ desc = schema.Description
+ defaultValue = schema.Default
+ extensions = schema.extensions
+ // If there is no mandatory format based on the field,
+ // allow it to be overridden by the user
+ if paramFormat == "" {
+ paramFormat = schema.Format
+ }
+ }
+
+ if parameter.IsRepeated() {
+ core := schemaCore{Type: paramType, Format: paramFormat}
+ if parameter.IsEnum() {
+ core.Enum = enumNames
+ enumNames = nil
+ }
+ items = (*openapiItemsObject)(&openapiSchemaObject{schemaCore: core})
+ paramType = "array"
+ paramFormat = ""
+ collectionFormat = reg.GetRepeatedPathParamSeparatorName()
+ minItems = new(int)
+ *minItems = 1
+ }
+
+ if desc == "" {
+ desc = fieldProtoComments(reg, parameter.Target.Message, parameter.Target)
+ }
+ parameterString := parameter.String()
+ if reg.GetUseJSONNamesForFields() {
+ parameterString = lowerCamelCase(parameterString, meth.RequestType.Fields, msgs)
+ }
+ var pattern string
+ if regExp, ok := pathParamRegexpMap[parameterString]; ok {
+ pattern = regExp
+ }
+ if fc := getFieldConfiguration(reg, parameter.Target); fc != nil {
+ pathParamName := fc.GetPathParamName()
+ if pathParamName != "" && pathParamName != parameterString {
+ pathParamNames["{"+parameterString+"}"] = "{" + pathParamName + "}"
+ parameterString, _, _ = strings.Cut(pathParamName, "=")
+ }
+ }
+ parameters = append(parameters, openapiParameterObject{
+ Name: parameterString,
+ Description: desc,
+ In: "path",
+ Required: true,
+ Default: defaultValue,
+ // Parameters in gRPC-Gateway can only be strings?
+ Type: paramType,
+ Format: paramFormat,
+ Enum: enumNames,
+ Items: items,
+ CollectionFormat: collectionFormat,
+ MinItems: minItems,
+ Pattern: pattern,
+ extensions: extensions,
+ })
+ }
+ // Now check if there is a body parameter
+ if b.Body != nil {
+ // Recursively render fields as definitions as long as they contain path parameters.
+ // Special case for top level body if we don't have a body field.
+ var schema openapiSchemaObject
+ desc := ""
+ var bodyFieldName string
+ schema = openapiSchemaObject{
+ schemaCore: schemaCore{},
+ }
+ if len(b.Body.FieldPath) == 0 {
+ // No field for body, use type.
+ bodyFieldName = "body"
+ wknSchemaCore, isWkn := wktSchemas[meth.RequestType.FQMN()]
+ if isWkn {
+ schema.schemaCore = wknSchemaCore
+ // Special workaround for Empty: it's well-known type but wknSchemas only returns schema.schemaCore; but we need to set schema.Properties which is a level higher.
+ if meth.RequestType.FQMN() == ".google.protobuf.Empty" {
+ schema.Properties = &openapiSchemaObjectProperties{}
+ }
+ } else {
+ messageSchema, err := renderMessageAsDefinition(meth.RequestType, reg, customRefs, b.PathParams)
+ if err != nil {
+ return err
+ }
+ if len(b.PathParams) == 0 {
+ if err := schema.setRefFromFQN(meth.RequestType.FQMN(), reg); err != nil {
+ return err
+ }
+ desc = messageSchema.Description
+ } else {
+ if meth.Name != nil {
+ methFQN, ok := fullyQualifiedNameToOpenAPIName(meth.FQMN(), reg)
+ if !ok {
+ panic(fmt.Errorf("failed to resolve method FQN: '%s'", meth.FQMN()))
+ }
+ defName := methFQN + "Body"
+ schema.Ref = fmt.Sprintf("#/definitions/%s", defName)
+ defs[defName] = messageSchema
+ } else {
+ schema = messageSchema
+ if schema.Properties == nil || len(*schema.Properties) == 0 {
+ grpclog.Warningf("created a body with 0 properties in the message, this might be unintended: %s", *meth.RequestType)
+ }
+ }
+ }
+ }
+ } else {
+ // Body field path is limited to one path component. From google.api.HttpRule.body:
+ // "NOTE: the referred field must be present at the top-level of the request message type."
+ // Ref: https://github.com/googleapis/googleapis/blob/b3397f5febbf21dfc69b875ddabaf76bee765058/google/api/http.proto#L350-L352
+ if len(b.Body.FieldPath) > 1 {
+ return fmt.Errorf("body of request %q is not a top level field: '%v'", meth.Service.GetName(), b.Body.FieldPath)
+ }
+ bodyField := b.Body.FieldPath[0]
+ if reg.GetUseJSONNamesForFields() {
+ bodyFieldName = lowerCamelCase(bodyField.Name, meth.RequestType.Fields, msgs)
+ } else {
+ bodyFieldName = bodyField.Name
+ }
+ // Align pathParams with body field path.
+ pathParams := subPathParams(bodyField.Name, b.PathParams)
+ var err error
+ schema, err = renderFieldAsDefinition(bodyField.Target, reg, customRefs, pathParams)
+ if err != nil {
+ return err
+ }
+ if schema.Title != "" {
+ desc = mergeDescription(schema)
+ } else {
+ desc = fieldProtoComments(reg, bodyField.Target.Message, bodyField.Target)
+ }
+ }
+
+ if meth.GetClientStreaming() {
+ desc += " (streaming inputs)"
+ }
+ parameters = append(parameters, openapiParameterObject{
+ Name: bodyFieldName,
+ Description: desc,
+ In: "body",
+ Required: true,
+ Schema: &schema,
+ })
+ }
+
+ // add the parameters to the query string
+ queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams, b.Body, b.HTTPMethod)
+ if err != nil {
+ return err
+ }
+ parameters = append(parameters, queryParams...)
+
+ path := partsToOpenAPIPath(parts, pathParamNames)
+
+ pathItemObject, ok := getPathItemObject(*paths, path)
+
+ if !ok {
+ pathItemObject = openapiPathItemObject{}
+ } else {
+ // handle case where we have an existing mapping for the same path and method
+ existingOperationObject := operationFunc(&pathItemObject)
+ if existingOperationObject != nil {
+ var firstPathParameter *openapiParameterObject
+ var firstParamIndex int
+ for index, param := range parameters {
+ param := param
+ if param.In == "path" {
+ firstPathParameter = ¶m
+ firstParamIndex = index
+ break
+ }
+ }
+ if firstPathParameter == nil {
+ // Without a path parameter, there is nothing to vary to support multiple mappings of the same path/method.
+ // Previously this did not log an error and only overwrote the mapping, we now log the error but
+ // still overwrite the mapping
+ grpclog.Errorf("Duplicate mapping for path %s %s", b.HTTPMethod, path)
+ } else {
+ newPathCount := 0
+ var newPath string
+ var newPathElement string
+ // Iterate until there is not an existing operation that matches the same escaped path.
+ // Most of the time this will only be a single iteration, but a large API could technically have
+ // a pretty large amount of these if it used similar patterns for all its functions.
+ for existingOperationObject != nil {
+ newPathCount += 1
+ newPathElement = firstPathParameter.Name + pathParamUniqueSuffixDeliminator + strconv.Itoa(newPathCount)
+ newPath = strings.ReplaceAll(path, "{"+firstPathParameter.Name+"}", "{"+newPathElement+"}")
+
+ if newPathItemObject, ok := getPathItemObject(*paths, newPath); ok {
+ existingOperationObject = operationFunc(&newPathItemObject)
+ } else {
+ existingOperationObject = nil
+ }
+ }
+ // update the pathItemObject we are adding to with the new path
+ pathItemObject, _ = getPathItemObject(*paths, newPath)
+ firstPathParameter.Name = newPathElement
+ path = newPath
+ parameters[firstParamIndex] = *firstPathParameter
+ }
+ }
+ }
+
+ methProtoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.ServiceDescriptorProto)(nil)), "Method")
+ desc := "A successful response."
+ var responseSchema openapiSchemaObject
+
+ if b.ResponseBody == nil || len(b.ResponseBody.FieldPath) == 0 {
+ responseSchema = openapiSchemaObject{
+ schemaCore: schemaCore{},
+ }
+
+ // Don't link to a full definition for
+ // empty; it's overly verbose.
+ // schema.Properties{} renders it as
+ // well, without a definition
+ wknSchemaCore, isWkn := wktSchemas[meth.ResponseType.FQMN()]
+ if !isWkn {
+ if err := responseSchema.setRefFromFQN(meth.ResponseType.FQMN(), reg); err != nil {
+ return err
+ }
+ } else {
+ responseSchema.schemaCore = wknSchemaCore
+
+ // Special workaround for Empty: it's well-known type but wknSchemas only returns schema.schemaCore; but we need to set schema.Properties which is a level higher.
+ if meth.ResponseType.FQMN() == ".google.protobuf.Empty" {
+ responseSchema.Properties = &openapiSchemaObjectProperties{}
+ }
+ }
+ } else {
+ // This is resolving the value of response_body in the google.api.HttpRule
+ lastField := b.ResponseBody.FieldPath[len(b.ResponseBody.FieldPath)-1]
+ responseSchema = schemaOfField(lastField.Target, reg, customRefs)
+ if responseSchema.Description != "" {
+ desc = responseSchema.Description
+ } else {
+ desc = fieldProtoComments(reg, lastField.Target.Message, lastField.Target)
+ }
+ }
+ if meth.GetServerStreaming() {
+ desc += "(streaming responses)"
+ responseSchema.Type = "object"
+ swgRef, _ := fullyQualifiedNameToOpenAPIName(meth.ResponseType.FQMN(), reg)
+ responseSchema.Title = "Stream result of " + swgRef
+
+ props := openapiSchemaObjectProperties{
+ keyVal{
+ Key: "result",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: responseSchema.Ref,
+ },
+ },
+ },
+ }
+ if !reg.GetDisableDefaultErrors() {
+ statusDef, hasStatus := fullyQualifiedNameToOpenAPIName(".google.rpc.Status", reg)
+ if hasStatus {
+ props = append(props, keyVal{
+ Key: "error",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: fmt.Sprintf("#/definitions/%s", statusDef),
+ },
+ },
+ })
+ }
+ }
+
+ // Special case HttpBody responses, they will be unformatted bytes
+ if meth.ResponseType.FQMN() == ".google.api.HttpBody" {
+ responseSchema.Type = "string"
+ responseSchema.Format = "binary"
+ responseSchema.Title = "Free form byte stream"
+ // The error response is still JSON, but technically the full response
+ // is still unformatted, so don't include the error response structure.
+ props = nil
+ }
+
+ responseSchema.Properties = &props
+ responseSchema.Ref = ""
+ }
+
+ operationObject := &openapiOperationObject{
+ Parameters: parameters,
+ Responses: openapiResponsesObject{},
+ Deprecated: deprecated,
+ }
+
+ if !reg.GetDisableDefaultResponses() {
+ operationObject.Responses["200"] = openapiResponseObject{
+ Description: desc,
+ Schema: responseSchema,
+ Headers: openapiHeadersObject{},
+ }
+ }
+
+ if !reg.GetDisableServiceTags() {
+ tag := svc.GetName()
+ if pkg := svc.File.GetPackage(); pkg != "" && reg.IsIncludePackageInTags() {
+ tag = pkg + "." + tag
+ }
+ operationObject.Tags = []string{tag}
+ }
+
+ if !reg.GetDisableDefaultErrors() {
+ errDef, hasErrDef := fullyQualifiedNameToOpenAPIName(".google.rpc.Status", reg)
+ if hasErrDef {
+ // https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responses-object
+ operationObject.Responses["default"] = openapiResponseObject{
+ Description: "An unexpected error response.",
+ Schema: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: fmt.Sprintf("#/definitions/%s", errDef),
+ },
+ },
+ }
+ }
+ }
+ operationObject.OperationID = fmt.Sprintf("%s_%s", svc.GetName(), meth.GetName())
+ if reg.GetSimpleOperationIDs() {
+ operationObject.OperationID = meth.GetName()
+ }
+ if bIdx != 0 {
+ // OperationID must be unique in an OpenAPI v2 definition.
+ operationObject.OperationID += strconv.Itoa(bIdx + 1)
+ }
+
+ // Fill reference map with referenced request messages
+ for _, param := range operationObject.Parameters {
+ if param.Schema != nil && param.Schema.Ref != "" {
+ requestResponseRefs[param.Schema.Ref] = struct{}{}
+ }
+ }
+
+ methComments := protoComments(reg, svc.File, nil, "Service", int32(svcIdx-svcBaseIdx), methProtoPath, int32(methIdx))
+ if err := updateOpenAPIDataFromComments(reg, operationObject, meth, methComments, false); err != nil {
+ panic(err)
+ }
+
+ svcOpts, err := getServiceOpenAPIOption(reg, svc)
+ if err != nil {
+ grpclog.Error(err)
+ return err
+ }
+
+ // Set Tag with the user-defined service name
+ if svcOpts.GetName() != "" {
+ operationObject.Tags = []string{svcOpts.GetName()}
+ }
+
+ opts, err := getMethodOpenAPIOption(reg, meth)
+ if opts != nil {
+ if err != nil {
+ panic(err)
+ }
+ operationObject.ExternalDocs = protoExternalDocumentationToOpenAPIExternalDocumentation(opts.ExternalDocs, reg, meth)
+
+ if opts.Deprecated {
+ operationObject.Deprecated = true
+ }
+
+ if opts.Summary != "" {
+ operationObject.Summary = opts.Summary
+ }
+ if opts.Description != "" {
+ operationObject.Description = opts.Description
+ }
+ if len(opts.Tags) > 0 {
+ operationObject.Tags = make([]string, len(opts.Tags))
+ copy(operationObject.Tags, opts.Tags)
+ }
+ if opts.OperationId != "" {
+ operationObject.OperationID = opts.OperationId
+ }
+ if opts.Security != nil {
+ newSecurity := []openapiSecurityRequirementObject{}
+ if operationObject.Security != nil {
+ newSecurity = *operationObject.Security
+ }
+ for _, secReq := range opts.Security {
+ newSecReq := openapiSecurityRequirementObject{}
+ for secReqKey, secReqValue := range secReq.SecurityRequirement {
+ if secReqValue == nil {
+ continue
+ }
+
+ newSecReqValue := make([]string, len(secReqValue.Scope))
+ copy(newSecReqValue, secReqValue.Scope)
+ newSecReq[secReqKey] = newSecReqValue
+ }
+
+ if len(newSecReq) > 0 {
+ newSecurity = append(newSecurity, newSecReq)
+ }
+ }
+ operationObject.Security = &newSecurity
+ }
+ if opts.Responses != nil {
+ for name, resp := range opts.Responses {
+ // Merge response data into default response if available.
+ respObj := operationObject.Responses[name]
+ if resp.Description != "" {
+ respObj.Description = resp.Description
+ }
+ if resp.Schema != nil {
+ respObj.Schema = openapiSchemaFromProtoSchema(resp.Schema, reg, customRefs, meth)
+ }
+ if resp.Examples != nil {
+ respObj.Examples = openapiExamplesFromProtoExamples(resp.Examples)
+ }
+ if resp.Headers != nil {
+ hdrs, err := processHeaders(resp.Headers)
+ if err != nil {
+ return err
+ }
+ respObj.Headers = hdrs
+ }
+ if resp.Extensions != nil {
+ exts, err := processExtensions(resp.Extensions)
+ if err != nil {
+ return err
+ }
+ respObj.extensions = exts
+ }
+ operationObject.Responses[name] = respObj
+ }
+ }
+
+ if opts.Extensions != nil {
+ exts, err := processExtensions(opts.Extensions)
+ if err != nil {
+ return err
+ }
+ operationObject.extensions = exts
+ }
+
+ if len(opts.Consumes) > 0 {
+ operationObject.Consumes = make([]string, len(opts.Consumes))
+ copy(operationObject.Consumes, opts.Consumes)
+ }
+
+ if len(opts.Produces) > 0 {
+ operationObject.Produces = make([]string, len(opts.Produces))
+ copy(operationObject.Produces, opts.Produces)
+ }
+
+ if params := opts.Parameters; params != nil && len(params.Headers) > 0 {
+ for _, header := range params.Headers {
+ param := openapiParameterObject{
+ In: "header",
+ Name: header.Name,
+ Description: header.Description,
+ Required: header.Required,
+ Format: header.Format,
+ }
+
+ switch header.Type {
+ case openapi_options.HeaderParameter_STRING:
+ param.Type = "string"
+ case openapi_options.HeaderParameter_NUMBER:
+ param.Type = "number"
+ case openapi_options.HeaderParameter_INTEGER:
+ param.Type = "integer"
+ case openapi_options.HeaderParameter_BOOLEAN:
+ param.Type = "boolean"
+ default:
+ return fmt.Errorf("invalid header parameter type: %+v", header.Type)
+ }
+
+ operationObject.Parameters = append(operationObject.Parameters, param)
+ }
+ }
+
+ // TODO(ivucica): add remaining fields of operation object
+ }
+
+ switch b.HTTPMethod {
+ case "DELETE":
+ pathItemObject.Delete = operationObject
+ case "GET":
+ pathItemObject.Get = operationObject
+ case "POST":
+ pathItemObject.Post = operationObject
+ case "PUT":
+ pathItemObject.Put = operationObject
+ case "PATCH":
+ pathItemObject.Patch = operationObject
+ case "HEAD":
+ pathItemObject.Head = operationObject
+ case "OPTIONS":
+ pathItemObject.Options = operationObject
+ }
+
+ updatePaths(paths, path, pathItemObject)
+ }
+ }
+ }
+
+ // Success! return nil on the error object
+ return nil
+}
+
+// Returns the openapiPathItemObject associated with a path. If path is not present, returns
+// empty openapiPathItemObject and false.
+func getPathItemObject(paths openapiPathsObject, path string) (openapiPathItemObject, bool) {
+ for _, pathData := range paths {
+ if pathData.Path == path {
+ return pathData.PathItemObject, true
+ }
+ }
+
+ return openapiPathItemObject{}, false
+}
+
+// If a path already exists in openapiPathsObject, updates that path's openapiPathItemObject. If not,
+// appends a new path and openapiPathItemObject to the openapiPathsObject.
+func updatePaths(paths *openapiPathsObject, path string, pathItemObject openapiPathItemObject) {
+ for i, p := range *paths {
+ if p.Path == path {
+ (*paths)[i].PathItemObject = pathItemObject
+ return
+ }
+ }
+ *paths = append(*paths, pathData{
+ Path: path,
+ PathItemObject: pathItemObject,
+ })
+}
+
+func mergeDescription(schema openapiSchemaObject) string {
+ desc := schema.Description
+ if schema.Title != "" { // join title because title of parameter object will be ignored
+ desc = strings.TrimSpace(schema.Title + paragraphDeliminator + schema.Description)
+ }
+ return desc
+}
+
+func operationForMethod(httpMethod string) func(*openapiPathItemObject) *openapiOperationObject {
+ switch httpMethod {
+ case "GET":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Get }
+ case "POST":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Post }
+ case "PUT":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Put }
+ case "DELETE":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Delete }
+ case "PATCH":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Patch }
+ case "HEAD":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Head }
+ case "OPTIONS":
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return obj.Options }
+ default:
+ return func(obj *openapiPathItemObject) *openapiOperationObject { return nil }
+ }
+}
+
+// This function is called with a param which contains the entire definition of a method.
+func applyTemplate(p param) (*openapiSwaggerObject, error) {
+ // Create the basic template object. This is the object that everything is
+ // defined off of.
+ s := openapiSwaggerObject{
+ // OpenAPI 2.0 is the version of this document
+ Swagger: "2.0",
+ Consumes: []string{"application/json"},
+ Produces: []string{"application/json"},
+ Paths: openapiPathsObject{},
+ Definitions: make(openapiDefinitionsObject),
+ Info: openapiInfoObject{
+ Title: *p.File.Name,
+ Version: "version not set",
+ },
+ }
+
+ // Loops through all the services and their exposed GET/POST/PUT/DELETE definitions
+ // and create entries for all of them.
+ // Also adds custom user specified references to second map.
+ requestResponseRefs, customRefs := refMap{}, refMap{}
+ if err := renderServices(p.Services, &s.Paths, p.reg, requestResponseRefs, customRefs, p.Messages, s.Definitions); err != nil {
+ panic(err)
+ }
+
+ messages := messageMap{}
+ streamingMessages := messageMap{}
+ enums := enumMap{}
+
+ if !p.reg.GetDisableDefaultErrors() {
+ // Add the error type to the message map
+ runtimeError, swgRef, err := lookupMsgAndOpenAPIName("google.rpc", "Status", p.reg)
+ if err == nil {
+ messages[swgRef] = runtimeError
+ } else {
+ // just in case there is an error looking up runtimeError
+ grpclog.Error(err)
+ }
+ }
+
+ // Find all the service's messages and enumerations that are defined (recursively)
+ // and write request, response and other custom (but referenced) types out as definition objects.
+ findServicesMessagesAndEnumerations(p.Services, p.reg, messages, streamingMessages, enums, requestResponseRefs)
+ if err := renderMessagesAsDefinition(messages, s.Definitions, p.reg, customRefs, nil); err != nil {
+ return nil, err
+ }
+ renderEnumerationsAsDefinition(enums, s.Definitions, p.reg, requestResponseRefs)
+
+ // File itself might have some comments and metadata.
+ packageProtoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Package")
+ packageComments := protoComments(p.reg, p.File, nil, "Package", packageProtoPath)
+ if err := updateOpenAPIDataFromComments(p.reg, &s, p, packageComments, true); err != nil {
+ return nil, err
+ }
+
+ // There may be additional options in the OpenAPI option in the proto.
+ spb, err := getFileOpenAPIOption(p.reg, p.File)
+ if err != nil {
+ return nil, err
+ }
+ if spb != nil {
+ if spb.Swagger != "" {
+ s.Swagger = spb.Swagger
+ }
+ if spb.Info != nil {
+ if spb.Info.Title != "" {
+ s.Info.Title = spb.Info.Title
+ }
+ if spb.Info.Description != "" {
+ s.Info.Description = spb.Info.Description
+ }
+ if spb.Info.TermsOfService != "" {
+ s.Info.TermsOfService = spb.Info.TermsOfService
+ }
+ if spb.Info.Version != "" {
+ s.Info.Version = spb.Info.Version
+ }
+ if spb.Info.Contact != nil {
+ if s.Info.Contact == nil {
+ s.Info.Contact = &openapiContactObject{}
+ }
+ if spb.Info.Contact.Name != "" {
+ s.Info.Contact.Name = spb.Info.Contact.Name
+ }
+ if spb.Info.Contact.Url != "" {
+ s.Info.Contact.URL = spb.Info.Contact.Url
+ }
+ if spb.Info.Contact.Email != "" {
+ s.Info.Contact.Email = spb.Info.Contact.Email
+ }
+ }
+ if spb.Info.License != nil {
+ if s.Info.License == nil {
+ s.Info.License = &openapiLicenseObject{}
+ }
+ if spb.Info.License.Name != "" {
+ s.Info.License.Name = spb.Info.License.Name
+ }
+ if spb.Info.License.Url != "" {
+ s.Info.License.URL = spb.Info.License.Url
+ }
+ }
+ if spb.Info.Extensions != nil {
+ exts, err := processExtensions(spb.Info.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ s.Info.extensions = exts
+ }
+ }
+ if spb.Host != "" {
+ s.Host = spb.Host
+ }
+ if spb.BasePath != "" {
+ s.BasePath = spb.BasePath
+ }
+ if len(spb.Schemes) > 0 {
+ s.Schemes = make([]string, len(spb.Schemes))
+ for i, scheme := range spb.Schemes {
+ s.Schemes[i] = strings.ToLower(scheme.String())
+ }
+ }
+ if len(spb.Consumes) > 0 {
+ s.Consumes = make([]string, len(spb.Consumes))
+ copy(s.Consumes, spb.Consumes)
+ }
+ if len(spb.Produces) > 0 {
+ s.Produces = make([]string, len(spb.Produces))
+ copy(s.Produces, spb.Produces)
+ }
+ if spb.SecurityDefinitions != nil && spb.SecurityDefinitions.Security != nil {
+ if s.SecurityDefinitions == nil {
+ s.SecurityDefinitions = openapiSecurityDefinitionsObject{}
+ }
+ for secDefKey, secDefValue := range spb.SecurityDefinitions.Security {
+ var newSecDefValue openapiSecuritySchemeObject
+ if oldSecDefValue, ok := s.SecurityDefinitions[secDefKey]; !ok {
+ newSecDefValue = openapiSecuritySchemeObject{}
+ } else {
+ newSecDefValue = oldSecDefValue
+ }
+ if secDefValue.Type != openapi_options.SecurityScheme_TYPE_INVALID {
+ switch secDefValue.Type {
+ case openapi_options.SecurityScheme_TYPE_BASIC:
+ newSecDefValue.Type = "basic"
+ case openapi_options.SecurityScheme_TYPE_API_KEY:
+ newSecDefValue.Type = "apiKey"
+ case openapi_options.SecurityScheme_TYPE_OAUTH2:
+ newSecDefValue.Type = "oauth2"
+ }
+ }
+ if secDefValue.Description != "" {
+ newSecDefValue.Description = secDefValue.Description
+ }
+ if secDefValue.Name != "" {
+ newSecDefValue.Name = secDefValue.Name
+ }
+ if secDefValue.In != openapi_options.SecurityScheme_IN_INVALID {
+ switch secDefValue.In {
+ case openapi_options.SecurityScheme_IN_QUERY:
+ newSecDefValue.In = "query"
+ case openapi_options.SecurityScheme_IN_HEADER:
+ newSecDefValue.In = "header"
+ }
+ }
+ if secDefValue.Flow != openapi_options.SecurityScheme_FLOW_INVALID {
+ switch secDefValue.Flow {
+ case openapi_options.SecurityScheme_FLOW_IMPLICIT:
+ newSecDefValue.Flow = "implicit"
+ case openapi_options.SecurityScheme_FLOW_PASSWORD:
+ newSecDefValue.Flow = "password"
+ case openapi_options.SecurityScheme_FLOW_APPLICATION:
+ newSecDefValue.Flow = "application"
+ case openapi_options.SecurityScheme_FLOW_ACCESS_CODE:
+ newSecDefValue.Flow = "accessCode"
+ }
+ }
+ if secDefValue.AuthorizationUrl != "" {
+ newSecDefValue.AuthorizationURL = secDefValue.AuthorizationUrl
+ }
+ if secDefValue.TokenUrl != "" {
+ newSecDefValue.TokenURL = secDefValue.TokenUrl
+ }
+ if secDefValue.Scopes != nil {
+ if newSecDefValue.Scopes == nil {
+ newSecDefValue.Scopes = openapiScopesObject{}
+ }
+ for scopeKey, scopeDesc := range secDefValue.Scopes.Scope {
+ newSecDefValue.Scopes[scopeKey] = scopeDesc
+ }
+ }
+ if secDefValue.Extensions != nil {
+ exts, err := processExtensions(secDefValue.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ newSecDefValue.extensions = exts
+ }
+ s.SecurityDefinitions[secDefKey] = newSecDefValue
+ }
+ }
+ if spb.Security != nil {
+ var newSecurity []openapiSecurityRequirementObject
+ if s.Security != nil {
+ newSecurity = s.Security
+ }
+ for _, secReq := range spb.Security {
+ newSecReq := openapiSecurityRequirementObject{}
+ for secReqKey, secReqValue := range secReq.SecurityRequirement {
+ if secReqValue == nil {
+ return nil, fmt.Errorf("malformed security requirement spec for key %q; value is required", secReqKey)
+ }
+ newSecReqValue := make([]string, len(secReqValue.Scope))
+ copy(newSecReqValue, secReqValue.Scope)
+ newSecReq[secReqKey] = newSecReqValue
+ }
+ newSecurity = append(newSecurity, newSecReq)
+ }
+ s.Security = newSecurity
+ }
+ s.ExternalDocs = protoExternalDocumentationToOpenAPIExternalDocumentation(spb.ExternalDocs, p.reg, spb)
+ // Populate all Paths with Responses set at top level,
+ // preferring Responses already set over those at the top level.
+ if spb.Responses != nil {
+ for _, verbs := range s.Paths {
+ var maps []openapiResponsesObject
+ if verbs.PathItemObject.Delete != nil {
+ maps = append(maps, verbs.PathItemObject.Delete.Responses)
+ }
+ if verbs.PathItemObject.Get != nil {
+ maps = append(maps, verbs.PathItemObject.Get.Responses)
+ }
+ if verbs.PathItemObject.Post != nil {
+ maps = append(maps, verbs.PathItemObject.Post.Responses)
+ }
+ if verbs.PathItemObject.Put != nil {
+ maps = append(maps, verbs.PathItemObject.Put.Responses)
+ }
+ if verbs.PathItemObject.Patch != nil {
+ maps = append(maps, verbs.PathItemObject.Patch.Responses)
+ }
+
+ for k, v := range spb.Responses {
+ for _, respMap := range maps {
+ if _, ok := respMap[k]; ok {
+ // Don't overwrite already existing Responses
+ continue
+ }
+ respMap[k] = openapiResponseObject{
+ Description: v.Description,
+ Schema: openapiSchemaFromProtoSchema(v.Schema, p.reg, customRefs, nil),
+ Examples: openapiExamplesFromProtoExamples(v.Examples),
+ }
+ }
+ }
+ }
+ }
+
+ if spb.Extensions != nil {
+ exts, err := processExtensions(spb.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ s.extensions = exts
+ }
+
+ if spb.Tags != nil {
+ for _, v := range spb.Tags {
+ newTag := openapiTagObject{}
+ newTag.Name = v.Name
+ newTag.Description = v.Description
+ if p.reg.GetUseGoTemplate() {
+ newTag.Description = goTemplateComments(newTag.Description, nil, p.reg)
+ }
+ if v.ExternalDocs != nil {
+ newTag.ExternalDocs = &openapiExternalDocumentationObject{
+ Description: v.ExternalDocs.Description,
+ URL: v.ExternalDocs.Url,
+ }
+ if p.reg.GetUseGoTemplate() {
+ newTag.ExternalDocs.Description = goTemplateComments(v.ExternalDocs.Description, nil, p.reg)
+ }
+ }
+ if v.Extensions != nil {
+ exts, err := processExtensions(v.Extensions)
+ if err != nil {
+ return nil, err
+ }
+ newTag.extensions = exts
+ }
+ s.Tags = append(s.Tags, newTag)
+ }
+ }
+
+ // Additional fields on the OpenAPI v2 spec's "OpenAPI" object
+ // should be added here, once supported in the proto.
+ }
+
+ if !p.reg.GetDisableServiceTags() {
+ s.Tags = mergeTags(s.Tags, renderServiceTags(p.Services, p.reg))
+ }
+
+ // Finally add any references added by users that aren't
+ // otherwise rendered.
+ if err := addCustomRefs(s.Definitions, p.reg, customRefs); err != nil {
+ return nil, err
+ }
+
+ return &s, nil
+}
+
+func mergeTags(existingTags []openapiTagObject, tags []openapiTagObject) []openapiTagObject {
+ for _, tag := range tags {
+ matched := false
+ for i, existingTag := range existingTags {
+ if existingTag.Name == tag.Name {
+ if existingTag.Description == "" {
+ existingTags[i].Description = tag.Description
+ }
+ if existingTag.ExternalDocs == nil {
+ existingTags[i].ExternalDocs = tag.ExternalDocs
+ } else if tag.ExternalDocs != nil {
+ if existingTag.ExternalDocs.Description == "" {
+ existingTags[i].ExternalDocs.Description = tag.ExternalDocs.Description
+ }
+ if existingTag.ExternalDocs.URL == "" {
+ existingTags[i].ExternalDocs.URL = tag.ExternalDocs.URL
+ }
+ }
+ if existingTag.extensions == nil {
+ existingTags[i].extensions = tag.extensions
+ } else if tag.extensions != nil {
+ for _, ext := range tag.extensions {
+ matchedExt := false
+ for _, existingExt := range existingTag.extensions {
+ if existingExt.key == ext.key {
+ matchedExt = true
+ break
+ }
+ }
+ if !matchedExt {
+ existingTags[i].extensions = append(existingTags[i].extensions, ext)
+ }
+ }
+ }
+ matched = true
+ break
+ }
+ }
+ if !matched {
+ existingTags = append(existingTags, tag)
+ }
+ }
+ return existingTags
+}
+
+func processExtensions(inputExts map[string]*structpb.Value) ([]extension, error) {
+ exts := make([]extension, 0, len(inputExts))
+ for k, v := range inputExts {
+ if !strings.HasPrefix(k, "x-") {
+ return nil, fmt.Errorf("extension keys need to start with \"x-\": %q", k)
+ }
+ ext, err := (&protojson.MarshalOptions{Indent: " "}).Marshal(v)
+ if err != nil {
+ return nil, err
+ }
+ exts = append(exts, extension{key: k, value: ext})
+ }
+ sort.Slice(exts, func(i, j int) bool { return exts[i].key < exts[j].key })
+ return exts, nil
+}
+
+func validateHeaderTypeAndFormat(headerType, format string) error {
+ // The type of the object. The value MUST be one of "string", "number", "integer", "boolean", or "array"
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject
+ // Note: currently not implementing array as we are only implementing this in the operation response context
+ switch headerType {
+ // the format property is an open string-valued property, and can have any value to support documentation needs
+ // primary check for format is to ensure that the number/integer formats are extensions of the specified type
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#dataTypeFormat
+ case "string":
+ return nil
+ case "number":
+ switch format {
+ case "uint",
+ "uint8",
+ "uint16",
+ "uint32",
+ "uint64",
+ "int",
+ "int8",
+ "int16",
+ "int32",
+ "int64",
+ "float",
+ "float32",
+ "float64",
+ "complex64",
+ "complex128",
+ "double",
+ "byte",
+ "rune",
+ "uintptr",
+ "":
+ return nil
+ default:
+ return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
+ }
+ case "integer":
+ switch format {
+ case "uint",
+ "uint8",
+ "uint16",
+ "uint32",
+ "uint64",
+ "int",
+ "int8",
+ "int16",
+ "int32",
+ "int64",
+ "":
+ return nil
+ default:
+ return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
+ }
+ case "boolean":
+ return nil
+ }
+ return fmt.Errorf("the provided header type %q is not supported", headerType)
+}
+
+func validateDefaultValueTypeAndFormat(headerType string, defaultValue string, format string) error {
+ switch headerType {
+ case "string":
+ if !isQuotedString(defaultValue) {
+ return fmt.Errorf("the provided default value %q does not match provider type %q, or is not properly quoted with escaped quotations", defaultValue, headerType)
+ }
+ switch format {
+ case "date-time":
+ unquoteTime := strings.Trim(defaultValue, `"`)
+ if _, err := time.Parse(time.RFC3339, unquoteTime); err != nil {
+ return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
+ }
+ case "date":
+ const layoutRFC3339Date = "2006-01-02"
+ unquoteDate := strings.Trim(defaultValue, `"`)
+ if _, err := time.Parse(layoutRFC3339Date, unquoteDate); err != nil {
+ return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
+ }
+ }
+ case "number":
+ if err := isJSONNumber(defaultValue, headerType); err != nil {
+ return err
+ }
+ case "integer":
+ switch format {
+ case "int32":
+ if _, err := strconv.ParseInt(defaultValue, 0, 32); err != nil {
+ return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
+ }
+ case "uint32":
+ if _, err := strconv.ParseUint(defaultValue, 0, 32); err != nil {
+ return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
+ }
+ case "int64":
+ if _, err := strconv.ParseInt(defaultValue, 0, 64); err != nil {
+ return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
+ }
+ case "uint64":
+ if _, err := strconv.ParseUint(defaultValue, 0, 64); err != nil {
+ return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
+ }
+ default:
+ if _, err := strconv.ParseInt(defaultValue, 0, 64); err != nil {
+ return fmt.Errorf("the provided default value %q does not match provided type %q", defaultValue, headerType)
+ }
+ }
+ case "boolean":
+ if !isBool(defaultValue) {
+ return fmt.Errorf("the provided default value %q does not match provider type %q", defaultValue, headerType)
+ }
+ }
+ return nil
+}
+
+func isQuotedString(s string) bool {
+ return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
+}
+
+func isJSONNumber(s string, t string) error {
+ val, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return fmt.Errorf("the provided default value %q does not match provider type %q", s, t)
+ }
+ // Floating point values that cannot be represented as sequences of digits (such as Infinity and NaN) are not permitted.
+ // See: https://tools.ietf.org/html/rfc4627#section-2.4
+ if math.IsInf(val, 0) || math.IsNaN(val) {
+ return fmt.Errorf("the provided number %q is not a valid JSON number", s)
+ }
+
+ return nil
+}
+
+func isBool(s string) bool {
+ // Unable to use strconv.ParseBool because it returns truthy values https://golang.org/pkg/strconv/#example_ParseBool
+ // per https://swagger.io/specification/v2/#data-types
+ // type: boolean represents two values: true and false. Note that truthy and falsy values such as "true", "", 0 or null are not considered boolean values.
+ return s == "true" || s == "false"
+}
+
+func processHeaders(inputHdrs map[string]*openapi_options.Header) (openapiHeadersObject, error) {
+ hdrs := make(map[string]openapiHeaderObject, len(inputHdrs))
+ for k, v := range inputHdrs {
+ header := textproto.CanonicalMIMEHeaderKey(k)
+ ret := openapiHeaderObject{
+ Description: v.Description,
+ Format: v.Format,
+ Pattern: v.Pattern,
+ }
+ if err := validateHeaderTypeAndFormat(v.Type, v.Format); err != nil {
+ return nil, err
+ }
+ ret.Type = v.Type
+ if v.Default != "" {
+ if err := validateDefaultValueTypeAndFormat(v.Type, v.Default, v.Format); err != nil {
+ return nil, err
+ }
+ ret.Default = RawExample(v.Default)
+ }
+ hdrs[header] = ret
+ }
+ return hdrs, nil
+}
+
+func removeInternalComments(comment string) string {
+ c := []string{}
+ for len(comment) > 0 {
+ open := strings.SplitN(comment, "(--", 2)
+ if len(open) == 1 {
+ c = append(c, open[0])
+ break
+ }
+ ex := strings.TrimRight(open[0], " \t")
+ // Trim only one line prior to all spaces
+ switch {
+ case strings.HasSuffix(ex, "\r\n"):
+ ex = strings.TrimSuffix(ex, "\r\n")
+ case strings.HasSuffix(ex, "\n"):
+ ex = strings.TrimSuffix(ex, "\n")
+ }
+ if ex != "" {
+ c = append(c, ex)
+ }
+ comment = open[1]
+
+ close := strings.SplitN(comment, "--)", 2)
+ if len(close) > 1 {
+ comment = close[1]
+ } else {
+ break
+ }
+ }
+ return strings.Join(c, "")
+}
+
+// updateOpenAPIDataFromComments updates a OpenAPI object based on a comment
+// from the proto file.
+//
+// First paragraph of a comment is used for summary. Remaining paragraphs of
+// a comment are used for description. If 'Summary' field is not present on
+// the passed swaggerObject, the summary and description are joined by \n\n.
+//
+// If there is a field named 'Info', its 'Summary' and 'Description' fields
+// will be updated instead.
+//
+// If there is no 'Summary', the same behavior will be attempted on 'Title',
+// but only if the last character is not a period.
+func updateOpenAPIDataFromComments(reg *descriptor.Registry, swaggerObject interface{}, data interface{}, comment string, isPackageObject bool) error {
+ if len(comment) == 0 {
+ return nil
+ }
+
+ // Checks whether the "ignore_comments" flag is set to true
+ if reg.GetIgnoreComments() {
+ return nil
+ }
+
+ // Checks whether the "remove_internal_comments" flag is set to true
+ if reg.GetRemoveInternalComments() {
+ comment = removeInternalComments(comment)
+ }
+
+ // Checks whether the "use_go_templates" flag is set to true
+ if reg.GetUseGoTemplate() {
+ comment = goTemplateComments(comment, data, reg)
+ }
+
+ // Figure out what to apply changes to.
+ swaggerObjectValue := reflect.ValueOf(swaggerObject)
+ infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info")
+ if !infoObjectValue.CanSet() {
+ // No such field? Apply summary and description directly to
+ // passed object.
+ infoObjectValue = swaggerObjectValue.Elem()
+ }
+
+ // Figure out which properties to update.
+ summaryValue := infoObjectValue.FieldByName("Summary")
+ descriptionValue := infoObjectValue.FieldByName("Description")
+ readOnlyValue := infoObjectValue.FieldByName("ReadOnly")
+
+ if readOnlyValue.Kind() == reflect.Bool && readOnlyValue.CanSet() && strings.Contains(comment, "Output only.") {
+ readOnlyValue.Set(reflect.ValueOf(true))
+ }
+
+ usingTitle := false
+ if !summaryValue.CanSet() {
+ summaryValue = infoObjectValue.FieldByName("Title")
+ usingTitle = true
+ }
+
+ paragraphs := strings.Split(comment, paragraphDeliminator)
+
+ // If there is a summary (or summary-equivalent) and it's empty, use the first
+ // paragraph as summary, and the rest as description.
+ if summaryValue.CanSet() {
+ summary := strings.TrimSpace(paragraphs[0])
+ description := strings.TrimSpace(strings.Join(paragraphs[1:], paragraphDeliminator))
+ if !usingTitle || (len(summary) > 0 && summary[len(summary)-1] != '.') {
+ // overrides the schema value only if it's empty
+ // keep the comment precedence when updating the package definition
+ if summaryValue.Len() == 0 || isPackageObject {
+ summaryValue.Set(reflect.ValueOf(summary))
+ }
+ if len(description) > 0 {
+ if !descriptionValue.CanSet() {
+ return errors.New("encountered object type with a summary, but no description")
+ }
+ // overrides the schema value only if it's empty
+ // keep the comment precedence when updating the package definition
+ if descriptionValue.Len() == 0 || isPackageObject {
+ descriptionValue.Set(reflect.ValueOf(description))
+ }
+ }
+ return nil
+ }
+ }
+
+ // There was no summary field on the swaggerObject. Try to apply the
+ // whole comment into description if the OpenAPI object description is empty.
+ if descriptionValue.CanSet() {
+ if descriptionValue.Len() == 0 || isPackageObject {
+ descriptionValue.Set(reflect.ValueOf(strings.Join(paragraphs, paragraphDeliminator)))
+ }
+ return nil
+ }
+
+ return errors.New("no description nor summary property")
+}
+
+func fieldProtoComments(reg *descriptor.Registry, msg *descriptor.Message, field *descriptor.Field) string {
+ protoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.DescriptorProto)(nil)), "Field")
+ for i, f := range msg.Fields {
+ if f == field {
+ return protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), protoPath, int32(i))
+ }
+ }
+ return ""
+}
+
+func enumValueProtoComments(reg *descriptor.Registry, enum *descriptor.Enum) string {
+ protoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.EnumDescriptorProto)(nil)), "Value")
+ var comments []string
+ for idx, value := range enum.GetValue() {
+ if reg.GetOmitEnumDefaultValue() && value.GetNumber() == 0 {
+ continue
+ }
+ if !isVisible(getEnumValueVisibilityOption(value), reg) {
+ continue
+ }
+ name := value.GetName()
+ if reg.GetEnumsAsInts() {
+ name = strconv.Itoa(int(value.GetNumber()))
+ }
+ if str := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), protoPath, int32(idx)); str != "" {
+ comments = append(comments, name+": "+str)
+ }
+ }
+ if len(comments) > 0 {
+ return "- " + strings.Join(comments, "\n - ")
+ }
+ return ""
+}
+
+func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string {
+ if file.SourceCodeInfo == nil {
+ fmt.Fprintln(os.Stderr, file.GetName(), "descriptor.File should not contain nil SourceCodeInfo")
+ return ""
+ }
+
+ outerPaths := make([]int32, len(outers))
+ for i := range outers {
+ location := ""
+ if file.Package != nil {
+ location = file.GetPackage()
+ }
+
+ msg, err := reg.LookupMsg(location, strings.Join(outers[:i+1], "."))
+ if err != nil {
+ panic(err)
+ }
+ outerPaths[i] = int32(msg.Index)
+ }
+
+ for _, loc := range file.SourceCodeInfo.Location {
+ if !isProtoPathMatches(loc.Path, outerPaths, typeName, typeIndex, fieldPaths) {
+ continue
+ }
+ comments := ""
+ if loc.LeadingComments != nil {
+ comments = strings.TrimRight(*loc.LeadingComments, "\n")
+ comments = strings.TrimSpace(comments)
+ // TODO(ivucica): this is a hack to fix "// " being interpreted as "//".
+ // perhaps we should:
+ // - split by \n
+ // - determine if every (but first and last) line begins with " "
+ // - trim every line only if that is the case
+ // - join by \n
+ comments = strings.ReplaceAll(comments, "\n ", "\n")
+ comments = removeInternalComments(comments)
+ }
+ if loc.TrailingComments != nil {
+ trailing := strings.TrimSpace(*loc.TrailingComments)
+ if comments == "" {
+ comments = trailing
+ } else {
+ comments += "\n\n" + trailing
+ }
+ }
+ return comments
+ }
+ return ""
+}
+
+func goTemplateComments(comment string, data interface{}, reg *descriptor.Registry) string {
+ var temp bytes.Buffer
+ tpl, err := template.New("").Funcs(template.FuncMap{
+ // Allows importing documentation from a file
+ "import": func(name string) string {
+ file, err := os.ReadFile(name)
+ if err != nil {
+ return err.Error()
+ }
+ // Runs template over imported file
+ return goTemplateComments(string(file), data, reg)
+ },
+ // Grabs title and description from a field
+ "fieldcomments": func(msg *descriptor.Message, field *descriptor.Field) string {
+ return strings.ReplaceAll(fieldProtoComments(reg, msg, field), "\n", "
")
+ },
+ "arg": func(name string) string {
+ if v, f := reg.GetGoTemplateArgs()[name]; f {
+ return v
+ }
+ return fmt.Sprintf("goTemplateArg %s not found", name)
+ },
+ }).Parse(comment)
+ if err != nil {
+ // If there is an error parsing the templating insert the error as string in the comment
+ // to make it easier to debug the template error
+ return err.Error()
+ }
+ if err := tpl.Execute(&temp, data); err != nil {
+ // If there is an error executing the templating insert the error as string in the comment
+ // to make it easier to debug the error
+ return err.Error()
+ }
+ return temp.String()
+}
+
+var (
+ messageProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "MessageType")
+ nestedProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.DescriptorProto)(nil)), "NestedType")
+ packageProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Package")
+ serviceProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Service")
+ methodProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.ServiceDescriptorProto)(nil)), "Method")
+)
+
+func isProtoPathMatches(paths []int32, outerPaths []int32, typeName string, typeIndex int32, fieldPaths []int32) bool {
+ if typeName == "Package" && typeIndex == packageProtoPath {
+ // path for package comments is just [2], and all the other processing
+ // is too complex for it.
+ if len(paths) == 0 || typeIndex != paths[0] {
+ return false
+ }
+ return true
+ }
+
+ if len(paths) != len(outerPaths)*2+2+len(fieldPaths) {
+ return false
+ }
+
+ if typeName == "Method" {
+ if paths[0] != serviceProtoPath || paths[2] != methodProtoPath {
+ return false
+ }
+ paths = paths[2:]
+ } else {
+ typeNameDescriptor := reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil))
+
+ if len(outerPaths) > 0 {
+ if paths[0] != messageProtoPath || paths[1] != outerPaths[0] {
+ return false
+ }
+ paths = paths[2:]
+ outerPaths = outerPaths[1:]
+
+ for i, v := range outerPaths {
+ if paths[i*2] != nestedProtoPath || paths[i*2+1] != v {
+ return false
+ }
+ }
+ paths = paths[len(outerPaths)*2:]
+
+ if typeName == "MessageType" {
+ typeName = "NestedType"
+ }
+ typeNameDescriptor = reflect.TypeOf((*descriptorpb.DescriptorProto)(nil))
+ }
+
+ if paths[0] != protoPathIndex(typeNameDescriptor, typeName) || paths[1] != typeIndex {
+ return false
+ }
+ paths = paths[2:]
+ }
+
+ for i, v := range fieldPaths {
+ if paths[i] != v {
+ return false
+ }
+ }
+ return true
+}
+
+// protoPathIndex returns a path component for google.protobuf.descriptor.SourceCode_Location.
+//
+// Specifically, it returns an id as generated from descriptor proto which
+// can be used to determine what type the id following it in the path is.
+// For example, if we are trying to locate comments related to a field named
+// `Address` in a message named `Person`, the path will be:
+//
+// [4, a, 2, b]
+//
+// While `a` gets determined by the order in which the messages appear in
+// the proto file, and `b` is the field index specified in the proto
+// file itself, the path actually needs to specify that `a` refers to a
+// message and not, say, a service; and that `b` refers to a field and not
+// an option.
+//
+// protoPathIndex figures out the values 4 and 2 in the above example. Because
+// messages are top level objects, the value of 4 comes from field id for
+// `MessageType` inside `google.protobuf.descriptor.FileDescriptor` message.
+// This field has a message type `google.protobuf.descriptor.DescriptorProto`.
+// And inside message `DescriptorProto`, there is a field named `Field` with id
+// 2.
+//
+// Some code generators seem to be hardcoding these values; this method instead
+// interprets them from `descriptor.proto`-derived Go source as necessary.
+func protoPathIndex(descriptorType reflect.Type, what string) int32 {
+ field, ok := descriptorType.Elem().FieldByName(what)
+ if !ok {
+ panic(fmt.Errorf("could not find protobuf descriptor type id for %s", what))
+ }
+ pbtag := field.Tag.Get("protobuf")
+ if pbtag == "" {
+ panic(fmt.Errorf("no Go tag 'protobuf' on protobuf descriptor for %s", what))
+ }
+ path, err := strconv.ParseInt(strings.Split(pbtag, ",")[1], 10, 32)
+ if err != nil {
+ panic(fmt.Errorf("protobuf descriptor id for %s cannot be converted to a number: %s", what, err.Error()))
+ }
+ return int32(path)
+}
+
+// extractOperationOptionFromMethodDescriptor extracts the message of type
+// openapi_options.Operation from a given proto method's descriptor.
+func extractOperationOptionFromMethodDescriptor(meth *descriptorpb.MethodDescriptorProto) (*openapi_options.Operation, error) {
+ if meth.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(meth.Options, openapi_options.E_Openapiv2Operation) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(meth.Options, openapi_options.E_Openapiv2Operation)
+ opts, ok := ext.(*openapi_options.Operation)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want an Operation", ext)
+ }
+ return opts, nil
+}
+
+// extractSchemaOptionFromMessageDescriptor extracts the message of type
+// openapi_options.Schema from a given proto message's descriptor.
+func extractSchemaOptionFromMessageDescriptor(msg *descriptorpb.DescriptorProto) (*openapi_options.Schema, error) {
+ if msg.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(msg.Options, openapi_options.E_Openapiv2Schema) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(msg.Options, openapi_options.E_Openapiv2Schema)
+ opts, ok := ext.(*openapi_options.Schema)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a Schema", ext)
+ }
+ return opts, nil
+}
+
+// extractEnumSchemaOptionFromEnumDescriptor extracts the message of type
+// openapi_options.EnumSchema from a given proto enum's descriptor.
+func extractEnumSchemaOptionFromEnumDescriptor(enum *descriptorpb.EnumDescriptorProto) (*openapi_options.EnumSchema, error) {
+ if enum.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(enum.Options, openapi_options.E_Openapiv2Enum) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(enum.Options, openapi_options.E_Openapiv2Enum)
+ opts, ok := ext.(*openapi_options.EnumSchema)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a EnumSchema", ext)
+ }
+ return opts, nil
+}
+
+// extractTagOptionFromServiceDescriptor extracts the tag of type
+// openapi_options.Tag from a given proto service's descriptor.
+func extractTagOptionFromServiceDescriptor(svc *descriptorpb.ServiceDescriptorProto) (*openapi_options.Tag, error) {
+ if svc.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(svc.Options, openapi_options.E_Openapiv2Tag) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(svc.Options, openapi_options.E_Openapiv2Tag)
+ opts, ok := ext.(*openapi_options.Tag)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a Tag", ext)
+ }
+ return opts, nil
+}
+
+// extractOpenAPIOptionFromFileDescriptor extracts the message of type
+// openapi_options.OpenAPI from a given proto method's descriptor.
+func extractOpenAPIOptionFromFileDescriptor(file *descriptorpb.FileDescriptorProto) (*openapi_options.Swagger, error) {
+ if file.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(file.Options, openapi_options.E_Openapiv2Swagger) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(file.Options, openapi_options.E_Openapiv2Swagger)
+ opts, ok := ext.(*openapi_options.Swagger)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a OpenAPI object", ext)
+ }
+ return opts, nil
+}
+
+func extractJSONSchemaFromFieldDescriptor(fd *descriptorpb.FieldDescriptorProto) (*openapi_options.JSONSchema, error) {
+ if fd.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(fd.Options, openapi_options.E_Openapiv2Field) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(fd.Options, openapi_options.E_Openapiv2Field)
+ opts, ok := ext.(*openapi_options.JSONSchema)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a JSONSchema object", ext)
+ }
+ return opts, nil
+}
+
+func extractFieldBehaviorFromFieldDescriptor(fd *descriptorpb.FieldDescriptorProto) ([]annotations.FieldBehavior, error) {
+ if fd.Options == nil {
+ return nil, nil
+ }
+ if !proto.HasExtension(fd.Options, annotations.E_FieldBehavior) {
+ return nil, nil
+ }
+ ext := proto.GetExtension(fd.Options, annotations.E_FieldBehavior)
+ opts, ok := ext.([]annotations.FieldBehavior)
+ if !ok {
+ return nil, fmt.Errorf("extension is %T; want a []FieldBehavior object", ext)
+ }
+ return opts, nil
+}
+
+func getFieldVisibilityOption(fd *descriptor.Field) *visibility.VisibilityRule {
+ if fd.Options == nil {
+ return nil
+ }
+ if !proto.HasExtension(fd.Options, visibility.E_FieldVisibility) {
+ return nil
+ }
+ ext := proto.GetExtension(fd.Options, visibility.E_FieldVisibility)
+ opts, ok := ext.(*visibility.VisibilityRule)
+ if !ok {
+ return nil
+ }
+ return opts
+}
+
+func getServiceVisibilityOption(fd *descriptor.Service) *visibility.VisibilityRule {
+ if fd.Options == nil {
+ return nil
+ }
+ if !proto.HasExtension(fd.Options, visibility.E_ApiVisibility) {
+ return nil
+ }
+ ext := proto.GetExtension(fd.Options, visibility.E_ApiVisibility)
+ opts, ok := ext.(*visibility.VisibilityRule)
+ if !ok {
+ return nil
+ }
+ return opts
+}
+
+func getMethodVisibilityOption(fd *descriptor.Method) *visibility.VisibilityRule {
+ if fd.Options == nil {
+ return nil
+ }
+ if !proto.HasExtension(fd.Options, visibility.E_MethodVisibility) {
+ return nil
+ }
+ ext := proto.GetExtension(fd.Options, visibility.E_MethodVisibility)
+ opts, ok := ext.(*visibility.VisibilityRule)
+ if !ok {
+ return nil
+ }
+ return opts
+}
+
+func getEnumValueVisibilityOption(fd *descriptorpb.EnumValueDescriptorProto) *visibility.VisibilityRule {
+ if fd.Options == nil {
+ return nil
+ }
+ if !proto.HasExtension(fd.Options, visibility.E_ValueVisibility) {
+ return nil
+ }
+ ext := proto.GetExtension(fd.Options, visibility.E_ValueVisibility)
+ opts, ok := ext.(*visibility.VisibilityRule)
+ if !ok {
+ return nil
+ }
+ return opts
+}
+
+func getMethodOpenAPIOption(reg *descriptor.Registry, meth *descriptor.Method) (*openapi_options.Operation, error) {
+ opts, err := extractOperationOptionFromMethodDescriptor(meth.MethodDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ if opts != nil {
+ return opts, nil
+ }
+ opts, ok := reg.GetOpenAPIMethodOptionv3(meth.FQMN())
+ if !ok {
+ return nil, nil
+ }
+ return opts, nil
+}
+
+func getMessageOpenAPIOption(reg *descriptor.Registry, msg *descriptor.Message) (*openapi_options.Schema, error) {
+ opts, err := extractSchemaOptionFromMessageDescriptor(msg.DescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ if opts != nil {
+ return opts, nil
+ }
+ opts, ok := reg.GetOpenAPIMessageOptionv3(msg.FQMN())
+ if !ok {
+ return nil, nil
+ }
+ return opts, nil
+}
+
+func getEnumOpenAPIOption(reg *descriptor.Registry, enum *descriptor.Enum) (*openapi_options.EnumSchema, error) {
+ opts, err := extractEnumSchemaOptionFromEnumDescriptor(enum.EnumDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ return opts, nil
+}
+
+func getServiceOpenAPIOption(reg *descriptor.Registry, svc *descriptor.Service) (*openapi_options.Tag, error) {
+ if opts, ok := reg.GetOpenAPIServiceOptionv3(svc.FQSN()); ok {
+ return opts, nil
+ }
+ opts, err := extractTagOptionFromServiceDescriptor(svc.ServiceDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ return opts, nil
+}
+
+func getFileOpenAPIOption(reg *descriptor.Registry, file *descriptor.File) (*openapi_options.Swagger, error) {
+ opts, err := extractOpenAPIOptionFromFileDescriptor(file.FileDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ if opts != nil {
+ return opts, nil
+ }
+ opts, ok := reg.GetOpenAPIFileOptionv3(*file.Name)
+ if !ok {
+ return nil, nil
+ }
+ return opts, nil
+}
+
+func getFieldOpenAPIOption(reg *descriptor.Registry, fd *descriptor.Field) (*openapi_options.JSONSchema, error) {
+ opts, err := extractJSONSchemaFromFieldDescriptor(fd.FieldDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ if opts != nil {
+ return opts, nil
+ }
+ opts, ok := reg.GetOpenAPIFieldOptionv3(fd.FQFN())
+ if !ok {
+ return nil, nil
+ }
+ return opts, nil
+}
+
+func getFieldBehaviorOption(reg *descriptor.Registry, fd *descriptor.Field) ([]annotations.FieldBehavior, error) {
+ opts, err := extractFieldBehaviorFromFieldDescriptor(fd.FieldDescriptorProto)
+ if err != nil {
+ return nil, err
+ }
+ if opts != nil {
+ return opts, nil
+ }
+ return opts, nil
+}
+
+func protoJSONSchemaToOpenAPISchemaCore(j *openapi_options.JSONSchema, reg *descriptor.Registry, refs refMap) schemaCore {
+ ret := schemaCore{}
+
+ if j.GetRef() != "" {
+ openapiName, ok := fullyQualifiedNameToOpenAPIName(j.GetRef(), reg)
+ if ok {
+ ret.Ref = "#/definitions/" + openapiName
+ if refs != nil {
+ refs[j.GetRef()] = struct{}{}
+ }
+ } else {
+ ret.Ref += j.GetRef()
+ }
+ } else {
+ f, t := protoJSONSchemaTypeToFormat(j.GetType())
+ ret.Format = f
+ ret.Type = t
+ }
+
+ return ret
+}
+
+func updateswaggerObjectFromJSONSchema(s *openapiSchemaObject, j *openapi_options.JSONSchema, reg *descriptor.Registry, data interface{}) {
+ s.Title = j.GetTitle()
+ s.Description = j.GetDescription()
+ if reg.GetUseGoTemplate() {
+ s.Title = goTemplateComments(s.Title, data, reg)
+ s.Description = goTemplateComments(s.Description, data, reg)
+ }
+ if s.Type == "array" {
+ s.Items.MaxLength = j.GetMaxLength()
+ s.Items.MinLength = j.GetMinLength()
+ s.Items.Pattern = j.GetPattern()
+ s.Items.Default = j.GetDefault()
+ s.Items.MaxProperties = j.GetMaxProperties()
+ s.Items.MinProperties = j.GetMinProperties()
+ s.Items.Required = j.GetRequired()
+ s.Items.Minimum = j.GetMinimum()
+ s.Items.Maximum = j.GetMaximum()
+ s.Items.ReadOnly = j.GetReadOnly()
+ s.Items.MultipleOf = j.GetMultipleOf()
+ s.Items.ExclusiveMaximum = j.GetExclusiveMaximum()
+ s.Items.ExclusiveMinimum = j.GetExclusiveMinimum()
+ s.Items.Enum = j.GetEnum()
+
+ if j.GetDefault() == "" {
+ s.Items.Default = nil
+ }
+ if len(j.GetEnum()) == 0 {
+ s.Items.Enum = nil
+ }
+ if j.GetFormat() != "" {
+ s.Items.Format = j.GetFormat()
+ }
+ } else {
+ s.MaxLength = j.GetMaxLength()
+ s.MinLength = j.GetMinLength()
+ s.Pattern = j.GetPattern()
+ s.Default = j.GetDefault()
+ s.MaxProperties = j.GetMaxProperties()
+ s.MinProperties = j.GetMinProperties()
+ s.Required = j.GetRequired()
+ s.Minimum = j.GetMinimum()
+ s.Maximum = j.GetMaximum()
+ s.ReadOnly = j.GetReadOnly()
+ s.MultipleOf = j.GetMultipleOf()
+ s.ExclusiveMaximum = j.GetExclusiveMaximum()
+ s.ExclusiveMinimum = j.GetExclusiveMinimum()
+ s.Enum = j.GetEnum()
+
+ if j.GetDefault() == "" {
+ s.Default = nil
+ }
+ if len(j.GetEnum()) == 0 {
+ s.Enum = nil
+ }
+ if j.GetFormat() != "" {
+ s.Format = j.GetFormat()
+ }
+ }
+ s.UniqueItems = j.GetUniqueItems()
+ s.MaxItems = j.GetMaxItems()
+ s.MinItems = j.GetMinItems()
+
+ if j.GetExtensions() != nil {
+ exts, err := processExtensions(j.GetExtensions())
+ if err != nil {
+ panic(err)
+ }
+ s.extensions = exts
+ }
+ if overrideType := j.GetType(); len(overrideType) > 0 {
+ s.Type = strings.ToLower(overrideType[0].String())
+ }
+ if j.GetExample() != "" {
+ s.Example = RawExample(j.GetExample())
+ }
+}
+
+func updateSwaggerObjectFromFieldBehavior(s *openapiSchemaObject, j []annotations.FieldBehavior, reg *descriptor.Registry, field *descriptor.Field) {
+ required := false
+ if reg.GetUseProto3FieldSemantics() {
+ required = !field.GetProto3Optional()
+ }
+ for _, fb := range j {
+ switch fb {
+ case annotations.FieldBehavior_REQUIRED:
+ required = true
+ case annotations.FieldBehavior_OUTPUT_ONLY:
+ s.ReadOnly = true
+ case annotations.FieldBehavior_FIELD_BEHAVIOR_UNSPECIFIED:
+ case annotations.FieldBehavior_OPTIONAL:
+ required = false
+ case annotations.FieldBehavior_INPUT_ONLY:
+ // OpenAPI v3 supports a writeOnly property, but this is not supported in Open API v2
+ case annotations.FieldBehavior_IMMUTABLE:
+ }
+ }
+ if required {
+ if reg.GetUseJSONNamesForFields() {
+ s.Required = append(s.Required, *field.JsonName)
+ } else {
+ s.Required = append(s.Required, *field.Name)
+ }
+ }
+}
+
+func openapiSchemaFromProtoEnumSchema(s *openapi_options.EnumSchema, reg *descriptor.Registry, refs refMap, data interface{}) openapiSchemaObject {
+ ret := openapiSchemaObject{
+ ExternalDocs: protoExternalDocumentationToOpenAPIExternalDocumentation(s.GetExternalDocs(), reg, data),
+ }
+ jsonSchema := &openapi_options.JSONSchema{
+ Ref: s.Ref,
+ Title: s.Title,
+ Extensions: s.Extensions,
+ Description: s.Description,
+ Default: s.Default,
+ ReadOnly: s.ReadOnly,
+ Example: s.Example,
+ }
+ ret.schemaCore = protoJSONSchemaToOpenAPISchemaCore(jsonSchema, reg, refs)
+ updateswaggerObjectFromJSONSchema(&ret, jsonSchema, reg, data)
+ return ret
+}
+
+func openapiSchemaFromProtoSchema(s *openapi_options.Schema, reg *descriptor.Registry, refs refMap, data interface{}) openapiSchemaObject {
+ ret := openapiSchemaObject{
+ ExternalDocs: protoExternalDocumentationToOpenAPIExternalDocumentation(s.GetExternalDocs(), reg, data),
+ }
+
+ ret.schemaCore = protoJSONSchemaToOpenAPISchemaCore(s.GetJsonSchema(), reg, refs)
+ updateswaggerObjectFromJSONSchema(&ret, s.GetJsonSchema(), reg, data)
+
+ if s != nil && s.Example != "" {
+ ret.Example = RawExample(s.Example)
+ }
+
+ return ret
+}
+
+func openapiExamplesFromProtoExamples(in map[string]string) map[string]interface{} {
+ if len(in) == 0 {
+ return nil
+ }
+ out := make(map[string]interface{}, len(in))
+ for mimeType, exampleStr := range in {
+ switch mimeType {
+ case "application/json":
+ // JSON example objects are rendered raw.
+ out[mimeType] = RawExample(exampleStr)
+ default:
+ // All other mimetype examples are rendered as strings.
+ out[mimeType] = exampleStr
+ }
+ }
+ return out
+}
+
+func protoJSONSchemaTypeToFormat(in []openapi_options.JSONSchema_JSONSchemaSimpleTypes) (string, string) {
+ if len(in) == 0 {
+ return "", ""
+ }
+
+ // Can't support more than 1 type, just return the first element.
+ // This is due to an inconsistency in the design of the openapiv2 proto
+ // and that used in schemaCore. schemaCore uses the v3 definition of types,
+ // which only allows a single string, while the openapiv2 proto uses the OpenAPI v2
+ // definition, which defers to the JSON schema definition, which allows a string or an array.
+ // Sources:
+ // https://swagger.io/specification/#itemsObject
+ // https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.2
+ switch in[0] {
+ case openapi_options.JSONSchema_UNKNOWN, openapi_options.JSONSchema_NULL:
+ return "", ""
+ case openapi_options.JSONSchema_OBJECT:
+ return "object", ""
+ case openapi_options.JSONSchema_ARRAY:
+ return "array", ""
+ case openapi_options.JSONSchema_BOOLEAN:
+ // NOTE: in OpenAPI specification, format should be empty on boolean type
+ return "boolean", ""
+ case openapi_options.JSONSchema_INTEGER:
+ return "integer", "int32"
+ case openapi_options.JSONSchema_NUMBER:
+ return "number", "double"
+ case openapi_options.JSONSchema_STRING:
+ // NOTE: in OpenAPI specification, format should be empty on string type
+ return "string", ""
+ default:
+ // Maybe panic?
+ return "", ""
+ }
+}
+
+func protoExternalDocumentationToOpenAPIExternalDocumentation(in *openapi_options.ExternalDocumentation, reg *descriptor.Registry, data interface{}) *openapiExternalDocumentationObject {
+ if in == nil {
+ return nil
+ }
+
+ if reg.GetUseGoTemplate() {
+ in.Description = goTemplateComments(in.Description, data, reg)
+ }
+
+ return &openapiExternalDocumentationObject{
+ Description: in.Description,
+ URL: in.Url,
+ }
+}
+
+func addCustomRefs(d openapiDefinitionsObject, reg *descriptor.Registry, refs refMap) error {
+ if len(refs) == 0 {
+ return nil
+ }
+ msgMap := make(messageMap)
+ enumMap := make(enumMap)
+ for ref := range refs {
+ swgName, swgOk := fullyQualifiedNameToOpenAPIName(ref, reg)
+ if !swgOk {
+ grpclog.Errorf("can't resolve OpenAPI name from CustomRef %q", ref)
+ continue
+ }
+ if _, ok := d[swgName]; ok {
+ // Skip already existing definitions
+ delete(refs, ref)
+ continue
+ }
+ msg, err := reg.LookupMsg("", ref)
+ if err == nil {
+ msgMap[swgName] = msg
+ continue
+ }
+ enum, err := reg.LookupEnum("", ref)
+ if err == nil {
+ enumMap[swgName] = enum
+ continue
+ }
+
+ // ?? Should be either enum or msg
+ }
+ if err := renderMessagesAsDefinition(msgMap, d, reg, refs, nil); err != nil {
+ return err
+ }
+ renderEnumerationsAsDefinition(enumMap, d, reg, refs)
+
+ // Run again in case any new refs were added
+ return addCustomRefs(d, reg, refs)
+}
+
+func lowerCamelCase(fieldName string, fields []*descriptor.Field, msgs []*descriptor.Message) string {
+ for _, oneField := range fields {
+ if oneField.GetName() == fieldName {
+ return oneField.GetJsonName()
+ }
+ }
+ messageNameToFieldsToJSONName := make(map[string]map[string]string, len(msgs))
+ fieldNameToType := make(map[string]string)
+ for _, msg := range msgs {
+ fieldNameToJSONName := make(map[string]string)
+ for _, oneField := range msg.GetField() {
+ fieldNameToJSONName[oneField.GetName()] = oneField.GetJsonName()
+ fieldNameToType[oneField.GetName()] = oneField.GetTypeName()
+ }
+ messageNameToFieldsToJSONName[msg.GetName()] = fieldNameToJSONName
+ }
+ if strings.Contains(fieldName, ".") {
+ fieldNames := strings.Split(fieldName, ".")
+ fieldNamesWithCamelCase := make([]string, 0)
+ for i := 0; i < len(fieldNames)-1; i++ {
+ fieldNamesWithCamelCase = append(fieldNamesWithCamelCase, casing.JSONCamelCase(fieldNames[i]))
+ }
+ prefix := strings.Join(fieldNamesWithCamelCase, ".")
+ reservedJSONName := getReservedJSONName(fieldName, messageNameToFieldsToJSONName, fieldNameToType)
+ if reservedJSONName != "" {
+ return prefix + "." + reservedJSONName
+ }
+ }
+ return casing.JSONCamelCase(fieldName)
+}
+
+func getReservedJSONName(fieldName string, messageNameToFieldsToJSONName map[string]map[string]string, fieldNameToType map[string]string) string {
+ if len(strings.Split(fieldName, ".")) == 2 {
+ fieldNames := strings.Split(fieldName, ".")
+ firstVariable := fieldNames[0]
+ firstType := fieldNameToType[firstVariable]
+ firstTypeShortNames := strings.Split(firstType, ".")
+ firstTypeShortName := firstTypeShortNames[len(firstTypeShortNames)-1]
+ return messageNameToFieldsToJSONName[firstTypeShortName][fieldNames[1]]
+ }
+ fieldNames := strings.Split(fieldName, ".")
+ return getReservedJSONName(strings.Join(fieldNames[1:], "."), messageNameToFieldsToJSONName, fieldNameToType)
+}
+
+func find(a []string, x string) int {
+ // This is a linear search but we are dealing with a small number of fields
+ for i, n := range a {
+ if x == n {
+ return i
+ }
+ }
+ return -1
+}
+
+// Make a deep copy of the outer parameters that has paramName as the first component,
+// but remove the first component of the field path.
+func subPathParams(paramName string, outerParams []descriptor.Parameter) []descriptor.Parameter {
+ var innerParams []descriptor.Parameter
+ for _, p := range outerParams {
+ if len(p.FieldPath) > 1 && p.FieldPath[0].Name == paramName {
+ subParam := descriptor.Parameter{
+ FieldPath: p.FieldPath[1:],
+ Target: p.Target,
+ Method: p.Method,
+ }
+ innerParams = append(innerParams, subParam)
+ }
+ }
+ return innerParams
+}
+
+func getFieldConfiguration(reg *descriptor.Registry, fd *descriptor.Field) *openapi_options.JSONSchema_FieldConfiguration {
+ if j, err := getFieldOpenAPIOption(reg, fd); err == nil {
+ return j.GetFieldConfiguration()
+ }
+ return nil
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/template_fuzz_test.go b/protoc-gen-openapiv3/internal/genopenapi/template_fuzz_test.go
new file mode 100644
index 00000000000..c8a164988f0
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/template_fuzz_test.go
@@ -0,0 +1,26 @@
+//go:build go1.18
+// +build go1.18
+
+package genopenapi
+
+import (
+ "regexp"
+ "testing"
+)
+
+var replaceInternalCommentsRegex = regexp.MustCompile(`(?s)(\r?\n)?[ \t]*(\(--)((.*?--\))|.*$)?`)
+
+func FuzzRemoveInternalComments(f *testing.F) {
+ f.Add("Text\n\n(-- Comment --)\n\nMore Text\n")
+ f.Add("Text\n\n(-- Multi\nLine\n\nComment --)\n\nMore Text\n")
+ f.Add("(-- Starting with comment --)\n\nMore Text\n")
+ f.Add("\n\n(-- Starting with new line and comment --)\n\nMore Text\n")
+ f.Add("Ending with\n\n(-- Comment --)")
+ f.Fuzz(func(t *testing.T, s string) {
+ s1 := removeInternalComments(s)
+ s2 := replaceInternalCommentsRegex.ReplaceAllString(s, "")
+ if s1 != s2 {
+ t.Errorf("Unexpected comment removal difference: our function produced %q but regex produced %q on %q", s1, s2, s)
+ }
+ })
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/template_test.go b/protoc-gen-openapiv3/internal/genopenapi/template_test.go
new file mode 100644
index 00000000000..7b0ae4efdb7
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/template_test.go
@@ -0,0 +1,11281 @@
+package genopenapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfigv3"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
+ openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
+ "google.golang.org/genproto/googleapis/api/annotations"
+ "google.golang.org/genproto/googleapis/api/visibility"
+ "google.golang.org/protobuf/encoding/protojson"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/reflect/protodesc"
+ "google.golang.org/protobuf/reflect/protoreflect"
+ "google.golang.org/protobuf/reflect/protoregistry"
+ "google.golang.org/protobuf/types/descriptorpb"
+ "google.golang.org/protobuf/types/known/anypb"
+ "google.golang.org/protobuf/types/known/durationpb"
+ "google.golang.org/protobuf/types/known/emptypb"
+ field_mask "google.golang.org/protobuf/types/known/fieldmaskpb"
+ "google.golang.org/protobuf/types/known/structpb"
+ "google.golang.org/protobuf/types/known/timestamppb"
+ "google.golang.org/protobuf/types/known/wrapperspb"
+ "google.golang.org/protobuf/types/pluginpb"
+)
+
+var marshaler = &runtime.JSONPb{}
+
+func TestOpenapiExamplesFromProtoExamples(t *testing.T) {
+ examples := openapiExamplesFromProtoExamples(map[string]string{
+ "application/json": `{"Hello": "Worldr!"}`,
+ "plain/text": "Hello, World!",
+ })
+
+ testCases := map[Format]string{
+ FormatJSON: `
+ {
+ "application/json": {
+ "Hello": "Worldr!"
+ },
+ "plain/text": "Hello, World!"
+ }
+ `,
+ FormatYAML: `
+ application/json:
+ Hello: Worldr!
+ plain/text: Hello, World!
+ `,
+ }
+
+ spaceRemover := strings.NewReplacer(" ", "", "\t", "", "\n", "")
+
+ for format, expected := range testCases {
+ t.Run(string(format), func(t *testing.T) {
+ var buf bytes.Buffer
+
+ encoder, err := format.NewEncoder(&buf)
+ if err != nil {
+ t.Fatalf("creating encoder: %s", err)
+ }
+
+ err = encoder.Encode(examples)
+ if err != nil {
+ t.Fatalf("encoding: %s", err)
+ }
+
+ actual := spaceRemover.Replace(buf.String())
+ expected = spaceRemover.Replace(expected)
+
+ if expected != actual {
+ t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual)
+ }
+ })
+ }
+}
+
+func crossLinkFixture(f *descriptor.File) *descriptor.File {
+ for _, m := range f.Messages {
+ m.File = f
+ }
+ for _, svc := range f.Services {
+ svc.File = f
+ for _, m := range svc.Methods {
+ m.Service = svc
+ for _, b := range m.Bindings {
+ b.Method = m
+ for _, param := range b.PathParams {
+ param.Method = m
+ }
+ }
+ }
+ }
+ return f
+}
+
+func reqFromFile(f *descriptor.File) *pluginpb.CodeGeneratorRequest {
+ return &pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ f.FileDescriptorProto,
+ },
+ FileToGenerate: []string{f.GetName()},
+ }
+}
+
+func TestMessageToQueryParametersWithEnumAsInt(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "b",
+ In: "query",
+ Required: false,
+ Type: "number",
+ Format: "double",
+ },
+ {
+ Name: "c",
+ In: "query",
+ Required: false,
+ Type: "array",
+ CollectionFormat: "multi",
+ },
+ },
+ },
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("Nested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("deep"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested"),
+ Number: proto.Int32(2),
+ },
+ },
+ NestedType: []*descriptorpb.DescriptorProto{{
+ Name: proto.String("DeepNested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
+ Number: proto.Int32(2),
+ },
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ {
+ Name: proto.String("DeepEnum"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {Name: proto.String("FALSE"), Number: proto.Int32(0)},
+ {Name: proto.String("TRUE"), Number: proto.Int32(1)},
+ },
+ },
+ },
+ }},
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "nested.a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.b",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.c",
+ In: "query",
+ Required: false,
+ Type: "integer",
+ Enum: []int{0, 1},
+ Default: 0,
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ reg.SetEnumsAsInts(true)
+ var msgs []*descriptor.Message
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ // avoid checking Items for array types
+ for i := range params {
+ params[i].Items = nil
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+func TestMessageToQueryParametersWithOmitEnumDefaultValue(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "b",
+ In: "query",
+ Required: false,
+ Type: "number",
+ Format: "double",
+ },
+ {
+ Name: "c",
+ In: "query",
+ Required: false,
+ Type: "array",
+ CollectionFormat: "multi",
+ },
+ },
+ },
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("Nested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("deep"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested"),
+ Number: proto.Int32(2),
+ },
+ },
+ NestedType: []*descriptorpb.DescriptorProto{{
+ Name: proto.String("DeepNested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
+ Number: proto.Int32(2),
+ },
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ {
+ Name: proto.String("DeepEnum"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {Name: proto.String("FALSE"), Number: proto.Int32(0)},
+ {Name: proto.String("TRUE"), Number: proto.Int32(1)},
+ },
+ },
+ },
+ }},
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "nested.a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.b",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.c",
+ In: "query",
+ Required: false,
+ Type: "string",
+ Enum: []string{"TRUE"},
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ reg.SetOmitEnumDefaultValue(true)
+ var msgs []*descriptor.Message
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ // avoid checking Items for array types
+ for i := range params {
+ params[i].Items = nil
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+func TestMessageToQueryParameters(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "b",
+ In: "query",
+ Required: false,
+ Type: "number",
+ Format: "double",
+ },
+ {
+ Name: "c",
+ In: "query",
+ Required: false,
+ Type: "array",
+ CollectionFormat: "multi",
+ },
+ },
+ },
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("Nested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("deep"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested"),
+ Number: proto.Int32(2),
+ },
+ },
+ NestedType: []*descriptorpb.DescriptorProto{{
+ Name: proto.String("DeepNested"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
+ Number: proto.Int32(2),
+ },
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ {
+ Name: proto.String("DeepEnum"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {Name: proto.String("FALSE"), Number: proto.Int32(0)},
+ {Name: proto.String("TRUE"), Number: proto.Int32(1)},
+ },
+ },
+ },
+ }},
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "nested.a",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.b",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "nested.deep.c",
+ In: "query",
+ Required: false,
+ Type: "string",
+ Enum: []string{"FALSE", "TRUE"},
+ Default: "FALSE",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ // avoid checking Items for array types
+ for i := range params {
+ params[i].Items = nil
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+// TestMessageToQueryParametersNoRecursive, is a check that cyclical references between messages
+// are not falsely detected given previous known edge-cases.
+func TestMessageToQueryParametersNoRecursive(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ }
+
+ tests := []test{
+ // First test:
+ // Here is a message that has two of another message adjacent to one another in a nested message.
+ // There is no loop but this was previously falsely flagged as a cycle.
+ // Example proto:
+ // message NonRecursiveMessage {
+ // string field = 1;
+ // }
+ // message BaseMessage {
+ // NonRecursiveMessage first = 1;
+ // NonRecursiveMessage second = 2;
+ // }
+ // message QueryMessage {
+ // BaseMessage first = 1;
+ // string second = 2;
+ // }
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("QueryMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("first"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.BaseMessage"),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("second"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ {
+ Name: proto.String("BaseMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("first"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.NonRecursiveMessage"),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("second"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.NonRecursiveMessage"),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ // Note there is no recursive nature to this message
+ {
+ Name: proto.String("NonRecursiveMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ // Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ },
+ Message: "QueryMessage",
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+
+ _, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("No recursion error should be thrown: %s", err)
+ }
+ }
+}
+
+// TestMessageToQueryParametersRecursive, is a check that cyclical references between messages
+// are handled gracefully. The goal is to ensure that attempts to add messages with cyclical
+// references to query-parameters returns an error message.
+func TestMessageToQueryParametersRecursive(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ }
+
+ tests := []test{
+ // First test:
+ // Here we test that a message that references itself through a field will return an error.
+ // Example proto:
+ // message DirectRecursiveMessage {
+ // DirectRecursiveMessage nested = 1;
+ // }
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("DirectRecursiveMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.DirectRecursiveMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ },
+ Message: "DirectRecursiveMessage",
+ },
+ // Second test:
+ // Here we test that a cycle through multiple messages is detected and that an error is returned.
+ // Sample:
+ // message Root { NodeMessage nested = 1; }
+ // message NodeMessage { CycleMessage nested = 1; }
+ // message CycleMessage { Root nested = 1; }
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("RootMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.NodeMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("NodeMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.CycleMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("CycleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.RootMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ },
+ Message: "RootMessage",
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ _, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err == nil {
+ t.Fatalf("It should not be allowed to have recursive query parameters")
+ }
+ }
+}
+
+func TestMessageToQueryParametersWithJsonName(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ requiredField := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+ requiredFieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, requiredField)
+
+ messageSchema := &openapi_options.Schema{
+ JsonSchema: &openapi_options.JSONSchema{
+ Required: []string{"test_field_b"},
+ },
+ }
+ messageOption := &descriptorpb.MessageOptions{}
+ proto.SetExtension(messageOption, openapi_options.E_Openapiv2Schema, messageSchema)
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("test_field_a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ JsonName: proto.String("testFieldA"),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "testFieldA",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ },
+ },
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("SubMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("test_field_a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ JsonName: proto.String("testFieldA"),
+ },
+ },
+ },
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("sub_message"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.SubMessage"),
+ Number: proto.Int32(1),
+ JsonName: proto.String("subMessage"),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "subMessage.testFieldA",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ },
+ },
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("test_field_a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ JsonName: proto.String("testFieldACustom"),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("test_field_b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ JsonName: proto.String("testFieldBCustom"),
+ },
+ },
+ Options: messageOption,
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "testFieldACustom",
+ In: "query",
+ Required: true,
+ Type: "string",
+ },
+ {
+ Name: "testFieldBCustom",
+ In: "query",
+ Required: true,
+ Type: "string",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %#v, got %#v", test.Params, params)
+ }
+ }
+}
+
+func TestMessageToQueryParametersWellKnownTypes(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ WellKnownMsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a_field_mask"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".google.protobuf.FieldMask"),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("a_timestamp"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".google.protobuf.Timestamp"),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ },
+ WellKnownMsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("FieldMask"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("paths"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ },
+ {
+ Name: proto.String("Timestamp"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("seconds"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("nanos"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a_field_mask",
+ In: "query",
+ Required: false,
+ Type: "string",
+ },
+ {
+ Name: "a_timestamp",
+ In: "query",
+ Required: false,
+ Type: "string",
+ Format: "date-time",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ reg.SetEnumsAsInts(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ {
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("google/well_known.proto"),
+ Package: proto.String("google.protobuf"),
+ Dependency: []string{},
+ MessageType: test.WellKnownMsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("google/well_known"),
+ },
+ },
+ {
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("acme/example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{"google/well_known.proto"},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("acme/example"),
+ },
+ },
+ },
+ })
+ if err != nil {
+ t.Fatalf("failed to load CodeGeneratorRequest: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+func TestMessageToQueryParametersWithRequiredField(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ messageSchema := &openapi_options.Schema{
+ JsonSchema: &openapi_options.JSONSchema{
+ Required: []string{"a"},
+ },
+ }
+ messageOption := &descriptorpb.MessageOptions{}
+ proto.SetExtension(messageOption, openapi_options.E_Openapiv2Schema, messageSchema)
+
+ fieldSchema := &openapi_options.JSONSchema{Required: []string{"b"}}
+ fieldOption := &descriptorpb.FieldOptions{}
+ proto.SetExtension(fieldOption, openapi_options.E_Openapiv2Field, fieldSchema)
+
+ // TODO(makdon): is nested field's test case necessary here?
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
+ Number: proto.Int32(2),
+ Options: fieldOption,
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ Options: messageOption,
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a",
+ In: "query",
+ Required: true,
+ Type: "string",
+ },
+ {
+ Name: "b",
+ In: "query",
+ Required: true,
+ Type: "number",
+ Format: "double",
+ },
+ {
+ Name: "c",
+ In: "query",
+ Required: false,
+ Type: "array",
+ CollectionFormat: "multi",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ // avoid checking Items for array types
+ for i := range params {
+ params[i].Items = nil
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+func TestMessageToQueryParametersWithEnumFieldOption(t *testing.T) {
+ type test struct {
+ MsgDescs []*descriptorpb.DescriptorProto
+ Message string
+ Params []openapiParameterObject
+ }
+
+ fieldSchema := &openapi_options.JSONSchema{Enum: []string{"enum1", "enum2"}}
+ fieldOption := &descriptorpb.FieldOptions{}
+ proto.SetExtension(fieldOption, openapi_options.E_Openapiv2Field, fieldSchema)
+
+ tests := []test{
+ {
+ MsgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("a"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Options: fieldOption,
+ },
+ {
+ Name: proto.String("b"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("c"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ TypeName: proto.String(".example.ExampleMessage.EnabledEnum"),
+ Number: proto.Int32(3),
+ },
+ {
+ Name: proto.String("d"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ TypeName: proto.String(".example.ExampleMessage.EnabledEnum"),
+ Number: proto.Int32(4),
+ Options: fieldOption,
+ },
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ {
+ Name: proto.String("EnabledEnum"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {Name: proto.String("FALSE"), Number: proto.Int32(0)},
+ {Name: proto.String("TRUE"), Number: proto.Int32(1)},
+ },
+ },
+ },
+ },
+ },
+ Message: "ExampleMessage",
+ Params: []openapiParameterObject{
+ {
+ Name: "a",
+ In: "query",
+ Type: "string",
+ Enum: []string{"enum1", "enum2"},
+ },
+ {
+ Name: "b",
+ In: "query",
+ Type: "string",
+ },
+ {
+ Name: "c",
+ In: "query",
+ Type: "string",
+ Enum: []string{"FALSE", "TRUE"},
+ Default: "FALSE",
+ },
+ {
+ Name: "d",
+ In: "query",
+ Type: "string",
+ Enum: []string{"FALSE", "TRUE"},
+ Default: "FALSE",
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.MsgDescs {
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.MsgDescs,
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ message, err := reg.LookupMsg("", ".example."+test.Message)
+ if err != nil {
+ t.Fatalf("failed to lookup message: %s", err)
+ }
+ params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
+ if err != nil {
+ t.Fatalf("failed to convert message to query parameters: %s", err)
+ }
+ // avoid checking Items for array types
+ for i := range params {
+ params[i].Items = nil
+ }
+ if !reflect.DeepEqual(params, test.Params) {
+ t.Errorf("expected %v, got %v", test.Params, params)
+ }
+ }
+}
+
+func TestApplyTemplateSimple(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateMultiService(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+
+ // Create two services that have the same method name. We will test that the
+ // operation IDs are different
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ svc2 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("OtherService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ ServiceDescriptorProto: svc2,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/ping",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ // Check that the two services have unique operation IDs even though they
+ // have the same method name.
+ if want, is := "ExampleService_Example", result.getPathItemObject("/v1/echo").Get.OperationID; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
+ }
+ if want, is := "OtherService_Example", result.getPathItemObject("/v1/ping").Get.OperationID; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateOpenAPIConfigFromYAML(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ openapiOptions := &openapiconfigv3.OpenAPIOptions{
+ Service: []*openapiconfigv3.OpenAPIServiceOption{
+ {
+ Service: "example.ExampleService",
+ Option: &openapi_options.Tag{
+ Description: "ExampleService description",
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Find out more about ExampleService",
+ },
+ },
+ },
+ },
+ }
+ if err := reg.RegisterOpenAPIOptionsv3(openapiOptions); err != nil {
+ t.Errorf("reg.RegisterOpenAPIOptions for Service %#v failed with %v; want success", openapiOptions.Service, err)
+ return
+ }
+
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := "ExampleService description", result.Tags[0].Description, "Tags[0].Description"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := "Find out more about ExampleService", result.Tags[0].ExternalDocs.Description, "Tags[0].ExternalDocs.Description"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ reg.SetDisableServiceTags(true)
+
+ res, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ if got, want := len(res.Tags), 0; got != want {
+ t.Fatalf("len(applyTemplate(%#v).Tags) = %d want to be %d", file, got, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateOverrideWithOperation(t *testing.T) {
+ newFile := func() *descriptor.File {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ Options: &descriptorpb.MethodOptions{},
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ return &descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ }
+
+ verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *openapiconfigv3.OpenAPIOptions) {
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", *file, err)
+ return
+ }
+ if opts != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(opts); err != nil {
+ t.Fatalf("failed to register OpenAPI options: %s", err)
+ }
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", *file, err)
+ return
+ }
+
+ if want, is := "MyExample", result.getPathItemObject("/v1/echo").Get.OperationID; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", *file, is, want)
+ }
+ if want, is := []string{"application/xml"}, result.getPathItemObject("/v1/echo").Get.Consumes; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).Paths[0].Get.Consumes = %s want to be %s", *file, is, want)
+ }
+ if want, is := []string{"application/json", "application/xml"}, result.getPathItemObject("/v1/echo").Get.Produces; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).Paths[0].Get.Produces = %s want to be %s", *file, is, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", *file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+ }
+
+ openapiOperation := openapi_options.Operation{
+ OperationId: "MyExample",
+ Consumes: []string{"application/xml"},
+ Produces: []string{"application/json", "application/xml"},
+ }
+
+ t.Run("verify override via method option", func(t *testing.T) {
+ file := newFile()
+ proto.SetExtension(proto.Message(file.Services[0].Methods[0].MethodDescriptorProto.Options),
+ openapi_options.E_Openapiv2Operation, &openapiOperation)
+
+ reg := descriptor.NewRegistry()
+ verifyTemplateFromReq(t, reg, file, nil)
+ })
+
+ t.Run("verify override options annotations", func(t *testing.T) {
+ file := newFile()
+ reg := descriptor.NewRegistry()
+ opts := &openapiconfigv3.OpenAPIOptions{
+ Method: []*openapiconfigv3.OpenAPIMethodOption{
+ {
+ Method: "example.ExampleService.Example",
+ Option: &openapiOperation,
+ },
+ },
+ }
+ verifyTemplateFromReq(t, reg, file, opts)
+ })
+}
+
+func TestApplyTemplateExtensions(t *testing.T) {
+ newFile := func() *descriptor.File {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ Options: &descriptorpb.MethodOptions{},
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ return &descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ }
+ swagger := openapi_options.Swagger{
+ Info: &openapi_options.Info{
+ Title: "test",
+ Extensions: map[string]*structpb.Value{
+ "x-info-extension": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
+ },
+ },
+ Extensions: map[string]*structpb.Value{
+ "x-foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
+ "x-bar": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{
+ Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "baz"}}},
+ }}},
+ },
+ SecurityDefinitions: &openapi_options.SecurityDefinitions{
+ Security: map[string]*openapi_options.SecurityScheme{
+ "somescheme": {
+ Extensions: map[string]*structpb.Value{
+ "x-security-baz": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
+ },
+ },
+ },
+ },
+ Tags: []*openapi_options.Tag{
+ {
+ Name: "test tag",
+ Description: "test tag description",
+ Extensions: map[string]*structpb.Value{
+ "x-traitTag": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
+ },
+ },
+ },
+ }
+ openapiOperation := openapi_options.Operation{
+ Responses: map[string]*openapi_options.Response{
+ "200": {
+ Extensions: map[string]*structpb.Value{
+ "x-resp-id": {Kind: &structpb.Value_StringValue{StringValue: "resp1000"}},
+ },
+ },
+ },
+ Extensions: map[string]*structpb.Value{
+ "x-op-foo": {Kind: &structpb.Value_StringValue{StringValue: "baz"}},
+ },
+ }
+ verifyTemplateExtensions := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
+ opts *openapiconfigv3.OpenAPIOptions,
+ ) {
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if opts != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(opts); err != nil {
+ t.Fatalf("failed to register OpenAPI annotations: %s", err)
+ }
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if got, want := len(result.extensions), 2; got != want {
+ t.Fatalf("len(applyTemplate(%#v).Extensions) = %d want to be %d", file, got, want)
+ }
+ if got, want := result.extensions[0].key, "x-bar"; got != want {
+ t.Errorf("applyTemplate(%#v).Extensions[0].key = %s want to be %s", file, got, want)
+ }
+ if got, want := result.extensions[1].key, "x-foo"; got != want {
+ t.Errorf("applyTemplate(%#v).Extensions[1].key = %s want to be %s", file, got, want)
+ }
+ {
+ var got []string
+ err = marshaler.Unmarshal(result.extensions[0].value, &got)
+ if err != nil {
+ t.Fatalf("marshaler.Unmarshal failed: %v", err)
+ }
+ want := []string{"baz"}
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Error(diff)
+ }
+ }
+ {
+ var got string
+ err = marshaler.Unmarshal(result.extensions[1].value, &got)
+ if err != nil {
+ t.Fatalf("marshaler.Unmarshal failed: %v", err)
+ }
+ want := "bar"
+ if diff := cmp.Diff(got, want); diff != "" {
+ t.Error(diff)
+ }
+ }
+
+ var scheme openapiSecuritySchemeObject
+ for _, v := range result.SecurityDefinitions {
+ scheme = v
+ }
+ if want, is, name := []extension{
+ {key: "x-security-baz", value: json.RawMessage("true")},
+ }, scheme.extensions, "SecurityScheme.Extensions"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ if want, is, name := []extension{
+ {key: "x-info-extension", value: json.RawMessage("\"bar\"")},
+ }, result.Info.extensions, "Info.Extensions"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ var operation *openapiOperationObject
+ var response openapiResponseObject
+ for _, v := range result.Paths {
+ operation = v.PathItemObject.Get
+ response = v.PathItemObject.Get.Responses["200"]
+ }
+ if want, is, name := []extension{
+ {key: "x-op-foo", value: json.RawMessage("\"baz\"")},
+ }, operation.extensions, "operation.Extensions"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := []extension{
+ {key: "x-resp-id", value: json.RawMessage("\"resp1000\"")},
+ }, response.extensions, "response.Extensions"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ if len(result.Tags) == 0 {
+ t.Errorf("No tags found in result")
+ return
+ }
+ tag := result.Tags[0]
+ if want, is, name := []extension{
+ {key: "x-traitTag", value: json.RawMessage("true")},
+ }, tag.extensions, "Tags[0].Extensions"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ }
+ t.Run("verify template options set via proto options", func(t *testing.T) {
+ file := newFile()
+ proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
+ proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
+ reg := descriptor.NewRegistry()
+ verifyTemplateExtensions(t, reg, file, nil)
+ })
+ t.Run("verify template options set via annotations", func(t *testing.T) {
+ file := newFile()
+ opts := &openapiconfigv3.OpenAPIOptions{
+ File: []*openapiconfigv3.OpenAPIFileOption{
+ {
+ File: "example.proto",
+ Option: &swagger,
+ },
+ },
+ Method: []*openapiconfigv3.OpenAPIMethodOption{
+ {
+ Method: "example.ExampleService.Example",
+ Option: &openapiOperation,
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ verifyTemplateExtensions(t, reg, file, opts)
+ })
+}
+
+func TestApplyTemplateHeaders(t *testing.T) {
+ newFile := func() *descriptor.File {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ Options: &descriptorpb.MethodOptions{},
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ return &descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ }
+ openapiOperation := openapi_options.Operation{
+ Responses: map[string]*openapi_options.Response{
+ "200": {
+ Description: "Testing Headers",
+ Headers: map[string]*openapi_options.Header{
+ "string": {
+ Description: "string header description",
+ Type: "string",
+ Format: "uuid",
+ Pattern: "",
+ },
+ "boolean": {
+ Description: "boolean header description",
+ Type: "boolean",
+ Default: "true",
+ Pattern: "^true|false$",
+ },
+ "integer": {
+ Description: "integer header description",
+ Type: "integer",
+ Default: "0",
+ Pattern: "^[0-9]$",
+ },
+ "number": {
+ Description: "number header description",
+ Type: "number",
+ Default: "1.2",
+ Pattern: "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
+ },
+ },
+ },
+ },
+ }
+ verifyTemplateHeaders := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
+ opts *openapiconfigv3.OpenAPIOptions,
+ ) {
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if opts != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(opts); err != nil {
+ t.Fatalf("failed to register OpenAPI annotations: %s", err)
+ }
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ var response openapiResponseObject
+ for _, v := range result.Paths {
+ response = v.PathItemObject.Get.Responses["200"]
+ }
+ if want, is, name := []openapiHeadersObject{
+ {
+ "String": openapiHeaderObject{
+ Description: "string header description",
+ Type: "string",
+ Format: "uuid",
+ Pattern: "",
+ },
+ "Boolean": openapiHeaderObject{
+ Description: "boolean header description",
+ Type: "boolean",
+ Default: RawExample("true"),
+ Pattern: "^true|false$",
+ },
+ "Integer": openapiHeaderObject{
+ Description: "integer header description",
+ Type: "integer",
+ Default: RawExample("0"),
+ Pattern: "^[0-9]$",
+ },
+ "Number": openapiHeaderObject{
+ Description: "number header description",
+ Type: "number",
+ Default: RawExample("1.2"),
+ Pattern: "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
+ },
+ },
+ }[0], response.Headers, "response.Headers"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ }
+ t.Run("verify template options set via proto options", func(t *testing.T) {
+ file := newFile()
+ proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
+ reg := descriptor.NewRegistry()
+ verifyTemplateHeaders(t, reg, file, nil)
+ })
+}
+
+func TestValidateHeaderType(t *testing.T) {
+ type test struct {
+ Type string
+ Format string
+ expectedError error
+ }
+ tests := []test{
+ {
+ "string",
+ "date-time",
+ nil,
+ },
+ {
+ "boolean",
+ "",
+ nil,
+ },
+ {
+ "integer",
+ "uint",
+ nil,
+ },
+ {
+ "integer",
+ "uint8",
+ nil,
+ },
+ {
+ "integer",
+ "uint16",
+ nil,
+ },
+ {
+ "integer",
+ "uint32",
+ nil,
+ },
+ {
+ "integer",
+ "uint64",
+ nil,
+ },
+ {
+ "integer",
+ "int",
+ nil,
+ },
+ {
+ "integer",
+ "int8",
+ nil,
+ },
+ {
+ "integer",
+ "int16",
+ nil,
+ },
+ {
+ "integer",
+ "int32",
+ nil,
+ },
+ {
+ "integer",
+ "int64",
+ nil,
+ },
+ {
+ "integer",
+ "float64",
+ errors.New("the provided format \"float64\" is not a valid extension of the type \"integer\""),
+ },
+ {
+ "integer",
+ "uuid",
+ errors.New("the provided format \"uuid\" is not a valid extension of the type \"integer\""),
+ },
+ {
+ "number",
+ "uint",
+ nil,
+ },
+ {
+ "number",
+ "uint8",
+ nil,
+ },
+ {
+ "number",
+ "uint16",
+ nil,
+ },
+ {
+ "number",
+ "uint32",
+ nil,
+ },
+ {
+ "number",
+ "uint64",
+ nil,
+ },
+ {
+ "number",
+ "int",
+ nil,
+ },
+ {
+ "number",
+ "int8",
+ nil,
+ },
+ {
+ "number",
+ "int16",
+ nil,
+ },
+ {
+ "number",
+ "int32",
+ nil,
+ },
+ {
+ "number",
+ "int64",
+ nil,
+ },
+ {
+ "number",
+ "float",
+ nil,
+ },
+ {
+ "number",
+ "float32",
+ nil,
+ },
+ {
+ "number",
+ "float64",
+ nil,
+ },
+ {
+ "number",
+ "complex64",
+ nil,
+ },
+ {
+ "number",
+ "complex128",
+ nil,
+ },
+ {
+ "number",
+ "double",
+ nil,
+ },
+ {
+ "number",
+ "byte",
+ nil,
+ },
+ {
+ "number",
+ "rune",
+ nil,
+ },
+ {
+ "number",
+ "uintptr",
+ nil,
+ },
+ {
+ "number",
+ "date",
+ errors.New("the provided format \"date\" is not a valid extension of the type \"number\""),
+ },
+ {
+ "array",
+ "",
+ errors.New("the provided header type \"array\" is not supported"),
+ },
+ {
+ "foo",
+ "",
+ errors.New("the provided header type \"foo\" is not supported"),
+ },
+ }
+ for _, v := range tests {
+ err := validateHeaderTypeAndFormat(v.Type, v.Format)
+
+ if v.expectedError == nil {
+ if err != nil {
+ t.Errorf("unexpected error %v", err)
+ }
+ } else {
+ if err == nil {
+ t.Fatal("expected header error not returned")
+ }
+ if err.Error() != v.expectedError.Error() {
+ t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
+ }
+ }
+ }
+}
+
+func TestValidateDefaultValueType(t *testing.T) {
+ type test struct {
+ Type string
+ Value string
+ Format string
+ expectedError error
+ }
+ tests := []test{
+ {
+ "string",
+ `"string"`,
+ "",
+ nil,
+ },
+ {
+ "string",
+ "\"2012-11-01T22:08:41+00:00\"",
+ "date-time",
+ nil,
+ },
+ {
+ "string",
+ "\"2012-11-01\"",
+ "date",
+ nil,
+ },
+ {
+ "string",
+ "0",
+ "",
+ errors.New("the provided default value \"0\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
+ },
+ {
+ "string",
+ "false",
+ "",
+ errors.New("the provided default value \"false\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
+ },
+ {
+ "boolean",
+ "true",
+ "",
+ nil,
+ },
+ {
+ "boolean",
+ "0",
+ "",
+ errors.New("the provided default value \"0\" does not match provider type \"boolean\""),
+ },
+ {
+ "boolean",
+ `"string"`,
+ "",
+ errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"boolean\""),
+ },
+ {
+ "number",
+ "1.2",
+ "",
+ nil,
+ },
+ {
+ "number",
+ "123",
+ "",
+ nil,
+ },
+ {
+ "number",
+ "nan",
+ "",
+ errors.New("the provided number \"nan\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "NaN",
+ "",
+ errors.New("the provided number \"NaN\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "-459.67",
+ "",
+ nil,
+ },
+ {
+ "number",
+ "inf",
+ "",
+ errors.New("the provided number \"inf\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "infinity",
+ "",
+ errors.New("the provided number \"infinity\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "Inf",
+ "",
+ errors.New("the provided number \"Inf\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "Infinity",
+ "",
+ errors.New("the provided number \"Infinity\" is not a valid JSON number"),
+ },
+ {
+ "number",
+ "false",
+ "",
+ errors.New("the provided default value \"false\" does not match provider type \"number\""),
+ },
+ {
+ "number",
+ `"string"`,
+ "",
+ errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"number\""),
+ },
+ {
+ "integer",
+ "2",
+ "",
+ nil,
+ },
+ {
+ "integer",
+ fmt.Sprint(math.MaxInt32),
+ "int32",
+ nil,
+ },
+ {
+ "integer",
+ fmt.Sprint(int64(math.MaxInt32) + 1),
+ "int32",
+ errors.New("the provided default value \"2147483648\" does not match provided format \"int32\""),
+ },
+ {
+ "integer",
+ fmt.Sprint(int64(math.MaxInt64)),
+ "int64",
+ nil,
+ },
+ {
+ "integer",
+ "9223372036854775808",
+ "int64",
+ errors.New("the provided default value \"9223372036854775808\" does not match provided format \"int64\""),
+ },
+ {
+ "integer",
+ "18446744073709551615",
+ "uint64",
+ nil,
+ },
+ {
+ "integer",
+ "false",
+ "",
+ errors.New("the provided default value \"false\" does not match provided type \"integer\""),
+ },
+ {
+ "integer",
+ "1.2",
+ "",
+ errors.New("the provided default value \"1.2\" does not match provided type \"integer\""),
+ },
+ {
+ "integer",
+ `"string"`,
+ "",
+ errors.New("the provided default value \"\\\"string\\\"\" does not match provided type \"integer\""),
+ },
+ }
+ for _, v := range tests {
+ err := validateDefaultValueTypeAndFormat(v.Type, v.Value, v.Format)
+
+ if v.expectedError == nil {
+ if err != nil {
+ t.Errorf("unexpected error '%v'", err)
+ }
+ } else {
+ if err == nil {
+ t.Error("expected update error not returned")
+ }
+ if err.Error() != v.expectedError.Error() {
+ t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
+ }
+ }
+ }
+}
+
+func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String("NestedMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ nesteddesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("NestedMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("int32"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("bool"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Echo"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ ClientStreaming: proto.Bool(false),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ meth.ServerStreaming = proto.Bool(false)
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ nested := &descriptor.Message{
+ DescriptorProto: nesteddesc,
+ }
+
+ nestedField := &descriptor.Field{
+ Message: msg,
+ FieldDescriptorProto: msg.GetField()[0],
+ }
+ intField := &descriptor.Field{
+ Message: nested,
+ FieldDescriptorProto: nested.GetField()[0],
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg, nested},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew): Figure out what this should really be
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ {
+ Name: "int32",
+ Target: intField,
+ },
+ }),
+ Target: intField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ }),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fmt.Fprintln(os.Stderr, "fd", file.FileDescriptorProto)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+ fmt.Fprintln(os.Stderr, "AllFQMNs", reg.GetAllFQMNs())
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, got := "2.0", result.Swagger; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
+ }
+ if want, got := "", result.BasePath; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
+ }
+ if want, got := ([]string)(nil), result.Schemes; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
+ }
+ if want, got := []string{"application/json"}, result.Consumes; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
+ }
+ if want, got := []string{"application/json"}, result.Produces; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String("NestedMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ nesteddesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("NestedMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("int32"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("bool"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Echo"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ ClientStreaming: proto.Bool(true),
+ ServerStreaming: proto.Bool(true),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ nested := &descriptor.Message{
+ DescriptorProto: nesteddesc,
+ }
+
+ nestedField := &descriptor.Field{
+ Message: msg,
+ FieldDescriptorProto: msg.GetField()[0],
+ }
+ intField := &descriptor.Field{
+ Message: nested,
+ FieldDescriptorProto: nested.GetField()[0],
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg, nested},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew): Figure out what this should really be
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ {
+ Name: "int32",
+ Target: intField,
+ },
+ }),
+ Target: intField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ }),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ // Only ExampleMessage must be present, not NestedMessage
+ if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ }
+ if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
+ t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
+ } else {
+ if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
+ if want, got, name := "object", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
+ if want, got, name := 2, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ } else {
+ resultProperty := streamExampleExampleMessageProperties[0]
+ if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ result := resultProperty.Value.(openapiSchemaObject)
+ if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ errorProperty := streamExampleExampleMessageProperties[1]
+ if want, got, name := "error", errorProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ err := errorProperty.Value.(openapiSchemaObject)
+ if want, got, name := "#/definitions/rpcStatus", err.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ }
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateRequestWithServerStreamingAndNoStandardErrors(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String("NestedMessage"),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ nesteddesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("NestedMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("int32"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("bool"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Echo"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ ClientStreaming: proto.Bool(false),
+ ServerStreaming: proto.Bool(true),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+ nested := &descriptor.Message{
+ DescriptorProto: nesteddesc,
+ }
+
+ nestedField := &descriptor.Field{
+ Message: msg,
+ FieldDescriptorProto: msg.GetField()[0],
+ }
+ intField := &descriptor.Field{
+ Message: nested,
+ FieldDescriptorProto: nested.GetField()[0],
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg, nested},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ {
+ Name: "int32",
+ Target: intField,
+ },
+ }),
+ Target: intField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "nested",
+ Target: nestedField,
+ },
+ }),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+ reg.SetDisableDefaultErrors(true)
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ // Should only include the message, no status or any type
+ if want, got, name := 1, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ }
+ if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
+ t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
+ } else {
+ if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
+ if want, got, name := "object", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
+ if want, got, name := 1, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ } else {
+ resultProperty := streamExampleExampleMessageProperties[0]
+ if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ result := resultProperty.Value.(openapiSchemaObject)
+ if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ }
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateRequestWithUnusedReferences(t *testing.T) {
+ reqdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("string"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ respdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("EmptyMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("EmptyMessage"),
+ ClientStreaming: proto.Bool(false),
+ ServerStreaming: proto.Bool(false),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ req := &descriptor.Message{
+ DescriptorProto: reqdesc,
+ }
+ resp := &descriptor.Message{
+ DescriptorProto: respdesc,
+ }
+ stringField := &descriptor.Field{
+ Message: req,
+ FieldDescriptorProto: req.GetField()[0],
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{reqdesc, respdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{req, resp},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: req,
+ ResponseType: resp,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/example",
+ },
+ },
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/example/{string}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "string",
+ Target: stringField,
+ },
+ }),
+ Target: stringField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "string",
+ Target: stringField,
+ },
+ }),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ // Only EmptyMessage must be present, not ExampleMessage (plus error status)
+ if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateRequestWithBodyQueryParameters(t *testing.T) {
+ bookDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("Book"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("id"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ createDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("CreateBookRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("parent"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("book"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("book_id"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("CreateBook"),
+ InputType: proto.String("CreateBookRequest"),
+ OutputType: proto.String("Book"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("BookService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ bookMsg := &descriptor.Message{
+ DescriptorProto: bookDesc,
+ }
+ createMsg := &descriptor.Message{
+ DescriptorProto: createDesc,
+ }
+
+ parentField := &descriptor.Field{
+ Message: createMsg,
+ FieldDescriptorProto: createMsg.GetField()[0],
+ }
+ bookField := &descriptor.Field{
+ Message: createMsg,
+ FieldMessage: bookMsg,
+ FieldDescriptorProto: createMsg.GetField()[1],
+ }
+ bookIDField := &descriptor.Field{
+ Message: createMsg,
+ FieldDescriptorProto: createMsg.GetField()[2],
+ }
+
+ createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
+
+ newFile := func() descriptor.File {
+ return descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("book.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{bookDesc, createDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/book.pb",
+ Name: "book_pb",
+ },
+ Messages: []*descriptor.Message{bookMsg, createMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: createMsg,
+ ResponseType: bookMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{parent=publishers/*}/books",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "parent",
+ Target: parentField,
+ },
+ }),
+ Target: parentField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "book",
+ Target: bookField,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ }
+ type args struct {
+ file descriptor.File
+ }
+ type paramOut struct {
+ Name string
+ In string
+ Required bool
+ }
+ tests := []struct {
+ name string
+ args args
+ want []paramOut
+ }{
+ {
+ name: "book_in_body",
+ args: args{file: newFile()},
+ want: []paramOut{
+ {"parent", "path", true},
+ {"book", "body", true},
+ {"book_id", "query", false},
+ },
+ },
+ {
+ name: "book_in_query",
+ args: args{file: func() descriptor.File {
+ f := newFile()
+ f.Services[0].Methods[0].Bindings[0].Body = nil
+ return f
+ }()},
+ want: []paramOut{
+ {"parent", "path", true},
+ {"book", "query", false},
+ {"book_id", "query", false},
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{tt.args.file.FileDescriptorProto}})
+ if err != nil {
+ t.Errorf("Registry.Load() failed with %v; want success", err)
+ return
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&tt.args.file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", tt.args.file, err)
+ return
+ }
+
+ if _, ok := result.getPathItemObject("/v1/{parent}/books").Post.Responses["200"]; !ok {
+ t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", tt.args.file, `result.getPathItemObject("/v1/{parent}/books").Post.Responses["200"]`)
+ } else {
+
+ if want, got, name := 3, len(result.getPathItemObject("/v1/{parent}/books").Post.Parameters), `len(result.getPathItemObject("/v1/{parent}/books").Post.Parameters)`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", tt.args.file, name, got, want)
+ }
+
+ for i, want := range tt.want {
+ p := result.getPathItemObject("/v1/{parent}/books").Post.Parameters[i]
+ if got, name := (paramOut{p.Name, p.In, p.Required}), `result.getPathItemObject("/v1/{parent}/books").Post.Parameters[0]`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %v want to be %v", tt.args.file, name, got, want)
+ }
+ }
+
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", tt.args.file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+ })
+ }
+}
+
+func TestApplyTemplateWithRequestAndBodyParameters(t *testing.T) {
+ bookDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("Book"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("id"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ createDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("CreateBookRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("parent"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("book"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ {
+ Name: proto.String("book_id"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("CreateBook"),
+ InputType: proto.String("CreateBookRequest"),
+ OutputType: proto.String("Book"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("BookService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ bookMsg := &descriptor.Message{
+ DescriptorProto: bookDesc,
+ }
+ createMsg := &descriptor.Message{
+ DescriptorProto: createDesc,
+ }
+
+ parentField := &descriptor.Field{
+ Message: createMsg,
+ FieldDescriptorProto: createMsg.GetField()[0],
+ }
+ bookField := &descriptor.Field{
+ Message: createMsg,
+ FieldMessage: bookMsg,
+ FieldDescriptorProto: createMsg.GetField()[1],
+ }
+ bookIDField := &descriptor.Field{
+ Message: createMsg,
+ FieldDescriptorProto: createMsg.GetField()[2],
+ }
+
+ createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("book.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{bookDesc, createDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/book.pb",
+ Name: "book_pb",
+ },
+ Messages: []*descriptor.Message{bookMsg, createMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: createMsg,
+ ResponseType: bookMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{parent=publishers/*}/books",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "parent",
+ Target: parentField,
+ },
+ }),
+ Target: parentField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: []descriptor.FieldPathComponent{},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ if want, is, name := 1, len(result.Paths), "len(result.Paths)"; !reflect.DeepEqual(is, want) {
+ t.Errorf("%s = %d want to be %d", name, want, is)
+ }
+ if want, is, name := 4, len(result.Paths[0].PathItemObject.Post.Parameters), "len(result.Paths[0].PathItemObject.Post.Parameters)"; !reflect.DeepEqual(is, want) {
+ t.Errorf("%s = %d want to be %d", name, want, is)
+ }
+ if want, is, name := "#/definitions/BookServiceCreateBookBody", result.Paths[0].PathItemObject.Post.Parameters[1].Schema.schemaCore.Ref, "result.Paths[0].PathItemObject.Post.Parameters[1].Schema.schemaCore.Ref"; !reflect.DeepEqual(is, want) {
+ t.Errorf("%s = %s want to be %s", name, want, is)
+ }
+
+ _, found := result.Definitions["BookServiceCreateBookBody"]
+ if !found {
+ t.Error("expecting definition to contain BookServiceCreateBookBody")
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+// TestApplyTemplateProtobufAny tests that the protobufAny definition is correctly rendered with the @type field and
+// allowing additional properties.
+func TestApplyTemplateProtobufAny(t *testing.T) {
+ // checkProtobufAnyFormat verifies the only property should be @type and additional properties are allowed
+ checkProtobufAnyFormat := func(t *testing.T, protobufAny openapiSchemaObject) {
+ anyPropsJSON, err := protobufAny.Properties.MarshalJSON()
+ if err != nil {
+ t.Errorf("protobufAny.Properties.MarshalJSON(), got error = %v", err)
+ }
+ var anyPropsMap map[string]interface{}
+ if err := json.Unmarshal(anyPropsJSON, &anyPropsMap); err != nil {
+ t.Errorf("json.Unmarshal(), got error = %v", err)
+ }
+
+ // @type should exist
+ if _, ok := anyPropsMap["@type"]; !ok {
+ t.Errorf("protobufAny.Properties missing key, \"@type\". got = %#v", anyPropsMap)
+ }
+
+ // and @type should be the only property
+ if len(anyPropsMap) > 1 {
+ t.Errorf("len(protobufAny.Properties) = %v, want = %v", len(anyPropsMap), 1)
+ }
+
+ // protobufAny should have additionalProperties allowed
+ if protobufAny.AdditionalProperties == nil {
+ t.Errorf("protobufAny.AdditionalProperties = nil, want not-nil")
+ }
+ }
+
+ type args struct {
+ regConfig func(registry *descriptor.Registry)
+ msgContainsAny bool
+ }
+ tests := []struct {
+ name string
+ args args
+ wantNumDefinitions int
+ }{
+ {
+ // our proto schema doesn't directly use protobufAny, but it is implicitly used by rpcStatus being
+ // automatically rendered
+ name: "default_protobufAny_from_rpcStatus",
+ args: args{
+ msgContainsAny: false,
+ },
+ wantNumDefinitions: 4,
+ },
+ {
+ // we have a protobufAny in a message, it should contain a ref inside the custom message
+ name: "protobufAny_referenced_in_message",
+ args: args{
+ msgContainsAny: true,
+ },
+ wantNumDefinitions: 4,
+ },
+ {
+ // we have a protobufAny in a message but with automatic rendering of rpcStatus disabled
+ name: "protobufAny_referenced_in_message_with_default_errors_disabled",
+ args: args{
+ msgContainsAny: true,
+ regConfig: func(reg *descriptor.Registry) {
+ reg.SetDisableDefaultErrors(true)
+ },
+ },
+ wantNumDefinitions: 3,
+ },
+ {
+ // we have a protobufAny in a message but with automatic rendering of responses disabled
+ name: "protobufAny_referenced_in_message_with_default_responses_disabled",
+ args: args{
+ msgContainsAny: true,
+ regConfig: func(reg *descriptor.Registry) {
+ reg.SetDisableDefaultResponses(true)
+ },
+ },
+ wantNumDefinitions: 4,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ reqdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ respdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("EmptyMessage"),
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("EmptyMessage"),
+ ClientStreaming: proto.Bool(false),
+ ServerStreaming: proto.Bool(false),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ req := &descriptor.Message{
+ DescriptorProto: reqdesc,
+ }
+ resp := &descriptor.Message{
+ DescriptorProto: respdesc,
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{reqdesc, respdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{req, resp},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: req,
+ ResponseType: resp,
+ },
+ },
+ },
+ },
+ }
+
+ reg := descriptor.NewRegistry()
+ reg.SetGenerateUnboundMethods(true)
+
+ if tt.args.regConfig != nil {
+ tt.args.regConfig(reg)
+ }
+
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+
+ protoFiles := []*descriptorpb.FileDescriptorProto{
+ file.FileDescriptorProto,
+ }
+
+ if tt.args.msgContainsAny {
+ // add an Any field to the request message
+ reqdesc.Field = append(reqdesc.Field, &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("any_value"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".google.protobuf.Any"),
+ Number: proto.Int32(2),
+ })
+
+ // update the dependencies to import it
+ file.Dependency = append(file.Dependency, "google/protobuf/any.proto")
+
+ anyDescriptorProto := protodesc.ToFileDescriptorProto((&anypb.Any{}).ProtoReflect().Descriptor().ParentFile())
+ anyDescriptorProto.SourceCodeInfo = &descriptorpb.SourceCodeInfo{}
+
+ // prepend the anyDescriptorProto to the protoFiles slice so that the dependency can be resolved
+ protoFiles = append(append(make([]*descriptorpb.FileDescriptorProto, 0, len(protoFiles)+1), anyDescriptorProto), protoFiles[0:]...)
+ }
+
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: protoFiles,
+ FileToGenerate: []string{file.GetName()},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ target, err := reg.LookupFile(file.GetName())
+ if err != nil {
+ t.Fatalf("failed to lookup file from reg: %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(target), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ if want, got, name := tt.wantNumDefinitions, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ }
+
+ protobufAny, ok := result.Definitions["protobufAny"]
+ if !ok {
+ t.Error("expecting Definitions to contain protobufAny")
+ }
+
+ checkProtobufAnyFormat(t, protobufAny)
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ resultJSON, _ := json.Marshal(result)
+ t.Errorf("got: %s", resultJSON)
+ }
+ })
+ }
+}
+
+func generateFieldsForJSONReservedName() []*descriptor.Field {
+ fields := make([]*descriptor.Field, 0)
+ fieldName := "json_name"
+ fieldJSONName := "jsonNAME"
+ fieldDescriptor := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName}
+ field := &descriptor.Field{FieldDescriptorProto: &fieldDescriptor}
+ return append(fields, field)
+}
+
+func generateMsgsForJSONReservedName() []*descriptor.Message {
+ result := make([]*descriptor.Message, 0)
+ // The first message, its field is field_abc and its type is NewType
+ // NewType field_abc
+ fieldName := "field_abc"
+ fieldJSONName := "fieldAbc"
+ messageName1 := "message1"
+ messageType := "pkg.a.NewType"
+ pfd := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName, TypeName: &messageType}
+ result = append(result,
+ &descriptor.Message{
+ DescriptorProto: &descriptorpb.DescriptorProto{
+ Name: &messageName1, Field: []*descriptorpb.FieldDescriptorProto{&pfd},
+ },
+ })
+ // The second message, its name is NewName, its type is string
+ // message NewType {
+ // string field_newName [json_name = RESERVEDJSONNAME]
+ // }
+ messageName := "NewType"
+ field := "field_newName"
+ fieldJSONName2 := "RESERVEDJSONNAME"
+ pfd2 := descriptorpb.FieldDescriptorProto{Name: &field, JsonName: &fieldJSONName2}
+ result = append(result, &descriptor.Message{
+ DescriptorProto: &descriptorpb.DescriptorProto{
+ Name: &messageName, Field: []*descriptorpb.FieldDescriptorProto{&pfd2},
+ },
+ })
+ return result
+}
+
+func TestTemplateWithJsonCamelCase(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"/test/{test_id}", "/test/{testId}"},
+ {"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1Id}/test2/{test2Id}"},
+ {"/test1/{test1_id}/{test2_id}", "/test1/{test1Id}/{test2Id}"},
+ {"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1Id}/{test2Id}"},
+ {"/test1/{test1_id1_id2}", "/test1/{test1Id1Id2}"},
+ {"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1Id1Id2}/test2/{test2Id3Id4}"},
+ {"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1Id1Id2}/{test2Id3Id4}"},
+ {"test/{a}", "test/{a}"},
+ {"test/{ab}", "test/{ab}"},
+ {"test/{a_a}", "test/{aA}"},
+ {"test/{ab_c}", "test/{abC}"},
+ {"test/{json_name}", "test/{jsonNAME}"},
+ {"test/{field_abc.field_newName}", "test/{fieldAbc.RESERVEDJSONNAME}"},
+ {"/item/search:item/{item_no_query}", "/item/search:item/{itemNoQuery}"},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+}
+
+func TestTemplateWithoutJsonCamelCase(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"/test/{test_id}", "/test/{test_id}"},
+ {"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1_id}/test2/{test2_id}"},
+ {"/test1/{test1_id}/{test2_id}", "/test1/{test1_id}/{test2_id}"},
+ {"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1_id}/{test2_id}"},
+ {"/test1/{test1_id1_id2}", "/test1/{test1_id1_id2}"},
+ {"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1_id1_id2}/test2/{test2_id3_id4}"},
+ {"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1_id1_id2}/{test2_id3_id4}"},
+ {"test/{a}", "test/{a}"},
+ {"test/{ab}", "test/{ab}"},
+ {"test/{a_a}", "test/{a_a}"},
+ {"test/{json_name}", "test/{json_name}"},
+ {"test/{field_abc.field_newName}", "test/{field_abc.field_newName}"},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(false)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+}
+
+func TestTemplateToOpenAPIPath(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"/test", "/test"},
+ {"/{test}", "/{test}"},
+ {"/{test=prefix/*}", "/{test}"},
+ {"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
+ {"/{test1}/{test2}", "/{test1}/{test2}"},
+ {"/{test1}/{test2}/", "/{test1}/{test2}/"},
+ {"/{name=prefix/*}", "/{name}"},
+ {"/{name=prefix1/*/prefix2/*}", "/{name}"},
+ {"/{user.name=prefix/*}", "/{user.name}"},
+ {"/{user.name=prefix1/*/prefix2/*}", "/{user.name}"},
+ {"/{parent=prefix/*}/children", "/{parent}/children"},
+ {"/{name=prefix/*}:customMethod", "/{name}:customMethod"},
+ {"/{name=prefix1/*/prefix2/*}:customMethod", "/{name}:customMethod"},
+ {"/{user.name=prefix/*}:customMethod", "/{user.name}:customMethod"},
+ {"/{user.name=prefix1/*/prefix2/*}:customMethod", "/{user.name}:customMethod"},
+ {"/{parent=prefix/*}/children:customMethod", "/{parent}/children:customMethod"},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(false)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+ reg.SetUseJSONNamesForFields(true)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+}
+
+func getParameters(names []string) []descriptor.Parameter {
+ params := make([]descriptor.Parameter, 0)
+ for _, name := range names {
+ params = append(params, descriptor.Parameter{
+ Target: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String(name),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ Message: &descriptor.Message{
+ File: &descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{},
+ },
+ DescriptorProto: &descriptorpb.DescriptorProto{
+ Name: proto.String(""),
+ },
+ },
+ FieldMessage: nil,
+ ForcePrefixedName: false,
+ },
+ FieldPath: []descriptor.FieldPathComponent{{
+ Name: name,
+ Target: nil,
+ }},
+ Method: nil,
+ })
+ }
+ return params
+}
+
+func TestTemplateToOpenAPIPathExpandSlashed(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ pathParams []descriptor.Parameter
+ expectedPathParams []string
+ useJSONNames bool
+ }{
+ {"/v1/{name=projects/*/documents/*}:exportResults", "/v1/projects/{project}/documents/{document}:exportResults", getParameters([]string{"name"}), []string{"project", "document"}, true},
+ {"/test/{name=*}", "/test/{name}", getParameters([]string{"name"}), []string{"name"}, true},
+ {"/test/{name=*}/", "/test/{name}/", getParameters([]string{"name"}), []string{"name"}, true},
+ {"/test/{name=test_cases/*}/", "/test/test_cases/{testCase}/", getParameters([]string{"name"}), []string{"testCase"}, true},
+ {"/test/{name=test_cases/*}/", "/test/test_cases/{test_case}/", getParameters([]string{"name"}), []string{"test_case"}, false},
+ {"/test/{test_type.name=test_cases/*}/", "/test/test_cases/{testCase}/", getParameters([]string{"test_type.name"}), []string{"testCase"}, true},
+ {"/test/{test_type.name=test_cases/*}/", "/test/test_cases/{test_case}/", getParameters([]string{"test_type.name"}), []string{"test_case"}, false},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetExpandSlashedPathPatterns(true)
+ for _, data := range tests {
+ reg.SetUseJSONNamesForFields(data.useJSONNames)
+ actualParts, actualParams := templateToExpandedPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), data.pathParams)
+ if data.expected != actualParts {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actualParts)
+ }
+ pathParamsNames := make([]string, 0)
+ for _, param := range actualParams {
+ pathParamsNames = append(pathParamsNames, param.FieldPath[0].Name)
+ }
+ if !reflect.DeepEqual(data.expectedPathParams, pathParamsNames) {
+ t.Errorf("Expected mutated path params in templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expectedPathParams, data.pathParams)
+ }
+ }
+}
+
+func TestExpandedPathParametersStringType(t *testing.T) {
+ tests := []struct {
+ input string
+ }{
+ {"/test/{name=test_cases/*}/"}, {"/v1/{name=projects/*/documents/*}:exportResults"},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetExpandSlashedPathPatterns(true)
+ expectedParamType := openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ }
+ for _, data := range tests {
+ _, actualParams := templateToExpandedPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), getParameters([]string{"name"}))
+ for _, param := range actualParams {
+ refs := make(refMap)
+ actualParamType := schemaOfField(param.Target, reg, refs)
+ if !reflect.DeepEqual(actualParamType, expectedParamType) {
+ t.Errorf("Expected all path parameters to be type of 'string', actual: %#+v", actualParamType)
+ }
+ }
+ }
+}
+
+func BenchmarkTemplateToOpenAPIPath(b *testing.B) {
+ const input = "/{user.name=prefix1/*/prefix2/*}:customMethod"
+
+ b.Run("with JSON names", func(b *testing.B) {
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(false)
+
+ for i := 0; i < b.N; i++ {
+ _ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ }
+ })
+
+ b.Run("without JSON names", func(b *testing.B) {
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+
+ for i := 0; i < b.N; i++ {
+ _ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ }
+ })
+}
+
+func TestResolveFullyQualifiedNameToOpenAPIName(t *testing.T) {
+ tests := []struct {
+ input string
+ output string
+ listOfFQMNs []string
+ namingStrategy string
+ }{
+ {
+ ".a.b.C",
+ "C",
+ []string{
+ ".a.b.C",
+ },
+ "legacy",
+ },
+ {
+ ".a.b.C",
+ "C",
+ []string{
+ ".a.b.C",
+ },
+ "simple",
+ },
+ {
+ ".a.b.C",
+ "abC",
+ []string{
+ ".a.C",
+ ".a.b.C",
+ },
+ "legacy",
+ },
+ {
+ ".a.b.C",
+ "b.C",
+ []string{
+ ".a.C",
+ ".a.b.C",
+ },
+ "simple",
+ },
+ {
+ ".a.b.C",
+ "abC",
+ []string{
+ ".C",
+ ".a.C",
+ ".a.b.C",
+ },
+ "legacy",
+ },
+ {
+ ".a.b.C",
+ "b.C",
+ []string{
+ ".C",
+ ".a.C",
+ ".a.b.C",
+ },
+ "simple",
+ },
+ {
+ ".a.b.C",
+ "a.b.C",
+ []string{
+ ".C",
+ ".a.C",
+ ".a.b.C",
+ },
+ "fqn",
+ },
+ }
+
+ for _, data := range tests {
+ names := resolveFullyQualifiedNameToOpenAPINames(data.listOfFQMNs, data.namingStrategy)
+ output := names[data.input]
+ if output != data.output {
+ t.Errorf("Expected fullyQualifiedNameToOpenAPIName(%v, %s) to be %s but got %s",
+ data.input, data.namingStrategy, data.output, output)
+ }
+ }
+}
+
+func templateToOpenAPIPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message, pathParamNames map[string]string) string {
+ return partsToOpenAPIPath(templateToParts(path, reg, fields, msgs), pathParamNames)
+}
+
+func templateToRegexpMap(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) map[string]string {
+ return partsToRegexpMap(templateToParts(path, reg, fields, msgs))
+}
+
+func templateToExpandedPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message, pathParams []descriptor.Parameter) (string, []descriptor.Parameter) {
+ pathParts, pathParams := expandPathPatterns(templateToParts(path, reg, fields, msgs), pathParams, reg)
+ return partsToOpenAPIPath(pathParts, make(map[string]string)), pathParams
+}
+
+func TestFQMNToRegexpMap(t *testing.T) {
+ tests := []struct {
+ input string
+ expected map[string]string
+ }{
+ {"/test", map[string]string{}},
+ {"/{test}", map[string]string{}},
+ {"/{test" + pathParamUniqueSuffixDeliminator + "1=prefix/*}", map[string]string{"test" + pathParamUniqueSuffixDeliminator + "1": "prefix/[^/]+"}},
+ {"/{test=prefix/that/has/multiple/parts/to/it/**}", map[string]string{"test": "prefix/that/has/multiple/parts/to/it/.+"}},
+ {"/{test1=organizations/*}/{test2=divisions/*}", map[string]string{
+ "test1": "organizations/[^/]+",
+ "test2": "divisions/[^/]+",
+ }},
+ {"/v1/{name=projects/*/topics/*}:delete", map[string]string{"name": "projects/[^/]+/topics/[^/]+"}},
+ }
+ reg := descriptor.NewRegistry()
+ for _, data := range tests {
+ actual := templateToRegexpMap(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
+ if !reflect.DeepEqual(data.expected, actual) {
+ t.Errorf("Expected partsToRegexpMap(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+}
+
+func TestFQMNtoOpenAPIName(t *testing.T) {
+ tests := []struct {
+ input string
+ expected string
+ }{
+ {"/test", "/test"},
+ {"/{test}", "/{test}"},
+ {"/{test=prefix/*}", "/{test}"},
+ {"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
+ {"/{test1}/{test2}", "/{test1}/{test2}"},
+ {"/{test1}/{test2}/", "/{test1}/{test2}/"},
+ {"/v1/{name=tests/*}/tests", "/v1/{name}/tests"},
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(false)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+ reg.SetUseJSONNamesForFields(true)
+ for _, data := range tests {
+ actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
+ if data.expected != actual {
+ t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
+ }
+ }
+}
+
+func TestSchemaOfField(t *testing.T) {
+ type test struct {
+ field *descriptor.Field
+ refs refMap
+ expected openapiSchemaObject
+ openAPIOptions *openapiconfigv3.OpenAPIOptions
+ useJSONNamesForFields bool
+ }
+
+ jsonSchema := &openapi_options.JSONSchema{
+ Title: "field title",
+ Description: "field description",
+ }
+ jsonSchemaWithOptions := &openapi_options.JSONSchema{
+ Title: "field title",
+ Description: "field description",
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxItems: 20,
+ MinItems: 2,
+ UniqueItems: true,
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ }
+ jsonSchemaRequired := &openapi_options.JSONSchema{
+ Required: []string{"required_via_json_schema"},
+ }
+ jsonSchemaWithFormat := &openapi_options.JSONSchema{
+ Format: "uuid",
+ }
+
+ fieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
+
+ requiredField := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+ requiredFieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, requiredField)
+
+ outputOnlyField := []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
+ outputOnlyOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(outputOnlyOptions, annotations.E_FieldBehavior, outputOnlyField)
+
+ tests := []test{
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("primitive_field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("repeated_primitive_field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("empty_field"),
+ TypeName: proto.String(".google.protobuf.Empty"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Properties: &openapiSchemaObjectProperties{},
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.FieldMask"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Timestamp"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "date-time",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Duration"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.StringValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("repeated_wrapped_field"),
+ TypeName: proto.String(".google.protobuf.StringValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.BytesValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "byte",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Int32Value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "integer",
+ Format: "int32",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.UInt32Value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "integer",
+ Format: "int64",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Int64Value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "int64",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.UInt64Value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "uint64",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.FloatValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "number",
+ Format: "float",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.DoubleValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "number",
+ Format: "double",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.BoolValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "boolean",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Struct"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.Value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{},
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.ListValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{schemaCore: schemaCore{
+ Type: "object",
+ }},
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("wrapped_field"),
+ TypeName: proto.String(".google.protobuf.NullValue"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("message_field"),
+ TypeName: proto.String(".example.Message"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ },
+ },
+ refs: refMap{".example.Message": struct{}{}},
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: "#/definitions/exampleMessage",
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("map_field"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Message.MapFieldEntry"),
+ Options: fieldOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ AdditionalProperties: &openapiSchemaObject{
+ schemaCore: schemaCore{Type: "string"},
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("array_field"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Options: fieldOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{schemaCore: schemaCore{
+ Type: "string",
+ }},
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("primitive_field"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ Options: fieldOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "integer",
+ Format: "int32",
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("message_field"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Empty"),
+ Options: fieldOptions,
+ },
+ },
+ refs: refMap{".example.Empty": struct{}{}},
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: "#/definitions/exampleEmpty",
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("map_field"), // should be called map_field_option but it's not valid map field name
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Message.MapFieldEntry"),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.map_field",
+ Option: jsonSchema,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ AdditionalProperties: &openapiSchemaObject{
+ schemaCore: schemaCore{Type: "string"},
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("array_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.array_field_option",
+ Option: jsonSchema,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{schemaCore: schemaCore{
+ Type: "string",
+ }},
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("primitive_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.primitive_field_option",
+ Option: jsonSchema,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "integer",
+ Format: "int32",
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("primitive_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum().Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.primitive_field_option",
+ Option: &openapi_options.JSONSchema{
+ Title: "field title",
+ Description: "field description",
+ Format: "uuid",
+ },
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "uuid",
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("message_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Empty"),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.message_field_option",
+ Option: jsonSchema,
+ },
+ },
+ },
+ refs: refMap{".example.Empty": struct{}{}},
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: "#/definitions/exampleEmpty",
+ },
+ Title: "field title",
+ Description: "field description",
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("required_via_field_behavior_field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Options: requiredFieldOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ Required: []string{"required_via_field_behavior_field"},
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("readonly_via_field_behavior_field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Options: outputOnlyOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ ReadOnly: true,
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("required_message_field"),
+ TypeName: proto.String(".example.Message"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ Options: requiredFieldOptions,
+ },
+ },
+ refs: refMap{".example.Message": struct{}{}},
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Ref: "#/definitions/exampleMessage",
+ },
+ Required: []string{"required_message_field"},
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("array_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.array_field_option",
+ Option: jsonSchemaWithOptions,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ Title: "field title",
+ Description: "field description",
+ UniqueItems: true,
+ MaxItems: 20,
+ MinItems: 2,
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("array_field_option"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.array_field_option",
+ Option: jsonSchemaWithOptions,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "int64",
+ },
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ Title: "field title",
+ Description: "field description",
+ UniqueItems: true,
+ MaxItems: 20,
+ MinItems: 2,
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("array_field_format"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.array_field_format",
+ Option: jsonSchemaWithFormat,
+ },
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ Format: "uuid",
+ },
+ },
+ },
+ },
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("required_via_field_behavior_field_json_name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ JsonName: proto.String("required_field_custom_name"),
+ Options: requiredFieldOptions,
+ },
+ },
+ refs: make(refMap),
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ Required: []string{"required_field_custom_name"},
+ },
+ useJSONNamesForFields: true,
+ },
+ {
+ field: &descriptor.Field{
+ FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("required_via_json_schema"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ JsonName: proto.String("required_via_json_schema_json_name"),
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.required_via_json_schema",
+ Option: jsonSchemaRequired,
+ },
+ },
+ },
+ refs: make(refMap),
+ useJSONNamesForFields: true,
+ expected: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ Required: []string{"required_via_json_schema_json_name"},
+ },
+ },
+ }
+ for _, test := range tests {
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(test.useJSONNamesForFields)
+
+ req := &pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ {
+ Name: proto.String("third_party/google.proto"),
+ Package: proto.String("google.protobuf"),
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("third_party/google"),
+ },
+ MessageType: []*descriptorpb.DescriptorProto{
+ protodesc.ToDescriptorProto((&emptypb.Empty{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&structpb.Struct{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&structpb.Value{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&structpb.ListValue{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&field_mask.FieldMask{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((×tamppb.Timestamp{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&durationpb.Duration{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.StringValue{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.BytesValue{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.Int32Value{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.UInt32Value{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.Int64Value{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.UInt64Value{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.FloatValue{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.DoubleValue{}).ProtoReflect().Descriptor()),
+ protodesc.ToDescriptorProto((&wrapperspb.BoolValue{}).ProtoReflect().Descriptor()),
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ protodesc.ToEnumDescriptorProto(structpb.NullValue(0).Descriptor()),
+ },
+ },
+ {
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{"third_party/google.proto"},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ MessageType: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("value"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ func() *descriptorpb.FieldDescriptorProto {
+ fd := test.field.FieldDescriptorProto
+ fd.Number = proto.Int32(2)
+ return fd
+ }(),
+ },
+ NestedType: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("MapFieldEntry"),
+ Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)},
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("key"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("value"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ },
+ },
+ {
+ Name: proto.String("Empty"),
+ },
+ },
+ EnumType: []*descriptorpb.EnumDescriptorProto{
+ {
+ Name: proto.String("MessageType"),
+ Value: []*descriptorpb.EnumValueDescriptorProto{
+ {
+ Name: proto.String("MESSAGE_TYPE_1"),
+ Number: proto.Int32(0),
+ },
+ },
+ },
+ },
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ },
+ },
+ }
+ err := reg.Load(req)
+ if err != nil {
+ t.Errorf("failed to reg.Load(req): %v", err)
+ }
+
+ // set field's parent message pointer to message so field can resolve its FQFN
+ test.field.Message = &descriptor.Message{
+ DescriptorProto: req.ProtoFile[1].MessageType[0],
+ File: &descriptor.File{
+ FileDescriptorProto: req.ProtoFile[1],
+ },
+ }
+
+ if test.openAPIOptions != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(test.openAPIOptions); err != nil {
+ t.Fatalf("failed to register OpenAPI options: %s", err)
+ }
+ }
+
+ refs := make(refMap)
+ actual := schemaOfField(test.field, reg, refs)
+ expectedSchemaObject := test.expected
+ if e, a := expectedSchemaObject, actual; !reflect.DeepEqual(a, e) {
+ t.Errorf("Expected schemaOfField(%v) = \n%#+v, actual: \n%#+v", test.field, e, a)
+ }
+ if !reflect.DeepEqual(refs, test.refs) {
+ t.Errorf("Expected schemaOfField(%v) to add refs %v, not %v", test.field, test.refs, refs)
+ }
+ }
+}
+
+func TestRenderMessagesAsDefinition(t *testing.T) {
+ jsonSchema := &openapi_options.JSONSchema{
+ Title: "field title",
+ Description: "field description",
+ Required: []string{"aRequiredField"},
+ }
+
+ requiredField := new(descriptorpb.FieldOptions)
+ proto.SetExtension(requiredField, openapi_options.E_Openapiv2Field, jsonSchema)
+
+ fieldBehaviorRequired := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+ requiredFieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
+
+ fieldBehaviorOutputOnlyField := []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
+ fieldBehaviorOutputOnlyOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(fieldBehaviorOutputOnlyOptions, annotations.E_FieldBehavior, fieldBehaviorOutputOnlyField)
+
+ fieldVisibilityFieldInternal := &visibility.VisibilityRule{Restriction: "INTERNAL"}
+ fieldVisibilityInternalOption := new(descriptorpb.FieldOptions)
+ proto.SetExtension(fieldVisibilityInternalOption, visibility.E_FieldVisibility, fieldVisibilityFieldInternal)
+
+ fieldVisibilityFieldPreview := &visibility.VisibilityRule{Restriction: "INTERNAL,PREVIEW"}
+ fieldVisibilityPreviewOption := new(descriptorpb.FieldOptions)
+ proto.SetExtension(fieldVisibilityPreviewOption, visibility.E_FieldVisibility, fieldVisibilityFieldPreview)
+
+ tests := []struct {
+ descr string
+ msgDescs []*descriptorpb.DescriptorProto
+ schema map[string]*openapi_options.Schema // per-message schema to add
+ defs openapiDefinitionsObject
+ openAPIOptions *openapiconfigv3.OpenAPIOptions
+ pathParams []descriptor.Parameter
+ UseJSONNamesForFields bool
+ UseAllOfForRefs bool
+ }{
+ {
+ descr: "no OpenAPI options",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{},
+ defs: map[string]openapiSchemaObject{
+ "Message": {schemaCore: schemaCore{Type: "object"}},
+ },
+ },
+ {
+ descr: "example option",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ Example: `{"foo":"bar"}`,
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {schemaCore: schemaCore{
+ Type: "object",
+ Example: RawExample(`{"foo":"bar"}`),
+ }},
+ },
+ },
+ {
+ descr: "example option with something non-json",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ Example: `XXXX anything goes XXXX`,
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {schemaCore: schemaCore{
+ Type: "object",
+ Example: RawExample(`XXXX anything goes XXXX`),
+ }},
+ },
+ },
+ {
+ descr: "external docs option",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "glorious docs",
+ Url: "https://nada",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "glorious docs",
+ URL: "https://nada",
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema options",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxItems: 20,
+ MinItems: 2,
+ UniqueItems: true,
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxItems: 20,
+ MinItems: 2,
+ UniqueItems: true,
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ },
+ {
+ descr: "JSONSchema options from registry",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Message: []*openapiconfigv3.OpenAPIMessageOption{
+ {
+ Message: "example.Message",
+ Option: &openapi_options.Schema{
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxItems: 20,
+ MinItems: 2,
+ UniqueItems: true,
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ MultipleOf: 100,
+ Maximum: 101,
+ ExclusiveMaximum: true,
+ Minimum: 1,
+ ExclusiveMinimum: true,
+ MaxLength: 10,
+ MinLength: 3,
+ Pattern: "[a-z]+",
+ MaxItems: 20,
+ MinItems: 2,
+ UniqueItems: true,
+ MaxProperties: 33,
+ MinProperties: 22,
+ Required: []string{"req"},
+ ReadOnly: true,
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with required properties",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("FieldOne"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("FieldTwo"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("FieldThree"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ Options: requiredFieldOptions,
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{"FieldOne", "FieldTwo"},
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"FieldOne", "FieldTwo", "FieldThree"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "FieldOne",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "FieldTwo",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "FieldThree",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with required properties",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("FieldOne"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ Options: requiredFieldOptions,
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"FieldOne"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "FieldOne",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with required properties by using annotations",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("FieldOne"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ Options: requiredFieldOptions,
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"FieldOne"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "FieldOne",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with hidden properties",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("aInternalField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Options: fieldVisibilityInternalOption,
+ },
+ {
+ Name: proto.String("aPreviewField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ Options: fieldVisibilityPreviewOption,
+ },
+ {
+ Name: proto.String("aVisibleField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req"},
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "aPreviewField",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "aVisibleField",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with path parameters",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("aRequiredField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Options: requiredField,
+ },
+ {
+ Name: proto.String("aPathParameter"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req"},
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req", "aRequiredField"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "aRequiredField",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ Description: "field description",
+ Title: "field title",
+ },
+ },
+ },
+ },
+ },
+ pathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath{
+ descriptor.FieldPathComponent{
+ Name: ("aPathParameter"),
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with required properties via field_behavior",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("aRequiredField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("aOutputOnlyField"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ Options: fieldBehaviorOutputOnlyOptions,
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req"},
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"req", "aRequiredField"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "aRequiredField",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "aOutputOnlyField",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ ReadOnly: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ descr: "JSONSchema with required properties and fields with json_name",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("FieldOne"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ JsonName: proto.String("custom_json_1"),
+ },
+ {
+ Name: proto.String("FieldTwo"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ JsonName: proto.String("custom_json_2"),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("FieldThree"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(3),
+ JsonName: proto.String("custom_json_3"),
+ Options: requiredFieldOptions,
+ },
+ },
+ },
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{"FieldOne", "FieldTwo"},
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: []string{"custom_json_1", "custom_json_2", "custom_json_3"},
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "custom_json_1",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "custom_json_2",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ {
+ Key: "custom_json_3",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ },
+ },
+ UseJSONNamesForFields: true,
+ },
+ {
+ descr: "JSONSchema with a read_only nested field",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {
+ Name: proto.String("Message"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("nested"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.Message.Nested"),
+ Number: proto.Int32(1),
+ Options: fieldBehaviorOutputOnlyOptions,
+ },
+ },
+ NestedType: []*descriptorpb.DescriptorProto{{
+ Name: proto.String("Nested"),
+ }},
+ },
+ },
+ UseAllOfForRefs: true,
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "title",
+ Description: "desc",
+ Required: []string{},
+ },
+ },
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Field: []*openapiconfigv3.OpenAPIFieldOption{
+ {
+ Field: "example.Message.nested",
+ Option: &openapi_options.JSONSchema{
+ Title: "nested field title",
+ Description: "nested field desc",
+ Example: `"ok":"TRUE"`,
+ },
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "exampleMessage": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "title",
+ Description: "desc",
+ Required: nil,
+ Properties: &openapiSchemaObjectProperties{
+ {
+ Key: "nested",
+ Value: openapiSchemaObject{
+ AllOf: []allOfEntry{{Ref: "#/definitions/MessageNested"}},
+ ReadOnly: true,
+ schemaCore: schemaCore{
+ Example: RawExample(`"ok":"TRUE"`),
+ },
+ Title: "nested field title",
+ Description: "nested field desc",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.descr, func(t *testing.T) {
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.msgDescs {
+ msgdesc.Options = &descriptorpb.MessageOptions{}
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+
+ reg := descriptor.NewRegistry()
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.msgDescs,
+ EnumType: []*descriptorpb.EnumDescriptorProto{},
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ reg.SetVisibilityRestrictionSelectors([]string{"PREVIEW"})
+
+ if test.UseJSONNamesForFields {
+ reg.SetUseJSONNamesForFields(true)
+ }
+
+ if test.UseAllOfForRefs {
+ reg.SetUseAllOfForRefs(true)
+ }
+
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ msgMap := map[string]*descriptor.Message{}
+ for _, d := range test.msgDescs {
+ name := d.GetName()
+ msg, err := reg.LookupMsg("example", name)
+ if err != nil {
+ t.Fatalf("lookup message %v: %v", name, err)
+ }
+ msgMap[msg.FQMN()] = msg
+
+ if schema, ok := test.schema[name]; ok {
+ proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, schema)
+ }
+ }
+
+ if test.openAPIOptions != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(test.openAPIOptions); err != nil {
+ t.Fatalf("failed to register OpenAPI options: %s", err)
+ }
+ }
+
+ refs := make(refMap)
+ actual := make(openapiDefinitionsObject)
+ if err := renderMessagesAsDefinition(msgMap, actual, reg, refs, test.pathParams); err != nil {
+ t.Errorf("renderMessagesAsDefinition failed with: %s", err)
+ }
+
+ if !reflect.DeepEqual(actual, test.defs) {
+ t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
+ }
+ })
+ }
+}
+
+func TestUpdateOpenAPIDataFromComments(t *testing.T) {
+ tests := []struct {
+ descr string
+ openapiSwaggerObject interface{}
+ comments string
+ expectedError error
+ expectedOpenAPIObject interface{}
+ useGoTemplate bool
+ goTemplateArgs []string
+ }{
+ {
+ descr: "empty comments",
+ openapiSwaggerObject: nil,
+ expectedOpenAPIObject: nil,
+ comments: "",
+ expectedError: nil,
+ },
+ {
+ descr: "set field to read only",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ ReadOnly: true,
+ Description: "... Output only. ...",
+ },
+ comments: "... Output only. ...",
+ expectedError: nil,
+ },
+ {
+ descr: "set title",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "Comment with no trailing dot",
+ },
+ comments: "Comment with no trailing dot",
+ expectedError: nil,
+ },
+ {
+ descr: "set description",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Description: "Comment with trailing dot.",
+ },
+ comments: "Comment with trailing dot.",
+ expectedError: nil,
+ },
+ {
+ descr: "use info object",
+ openapiSwaggerObject: &openapiSwaggerObject{
+ Info: openapiInfoObject{},
+ },
+ expectedOpenAPIObject: &openapiSwaggerObject{
+ Info: openapiInfoObject{
+ Description: "Comment with trailing dot.",
+ },
+ },
+ comments: "Comment with trailing dot.",
+ expectedError: nil,
+ },
+ {
+ descr: "multi line comment with title",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "First line",
+ Description: "Second line",
+ },
+ comments: "First line\n\nSecond line",
+ expectedError: nil,
+ },
+ {
+ descr: "multi line comment no title",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Description: "First line.\n\nSecond line",
+ },
+ comments: "First line.\n\nSecond line",
+ expectedError: nil,
+ },
+ {
+ descr: "multi line comment with summary with dot",
+ openapiSwaggerObject: &openapiOperationObject{},
+ expectedOpenAPIObject: &openapiOperationObject{
+ Summary: "First line.",
+ Description: "Second line",
+ },
+ comments: "First line.\n\nSecond line",
+ expectedError: nil,
+ },
+ {
+ descr: "multi line comment with summary no dot",
+ openapiSwaggerObject: &openapiOperationObject{},
+ expectedOpenAPIObject: &openapiOperationObject{
+ Summary: "First line",
+ Description: "Second line",
+ },
+ comments: "First line\n\nSecond line",
+ expectedError: nil,
+ },
+ {
+ descr: "multi line comment with summary no dot",
+ openapiSwaggerObject: &schemaCore{},
+ expectedOpenAPIObject: &schemaCore{},
+ comments: "Any comment",
+ expectedError: errors.New("no description nor summary property"),
+ },
+ {
+ descr: "without use_go_template",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "First line",
+ Description: "{{import \"documentation.md\"}}",
+ },
+ comments: "First line\n\n{{import \"documentation.md\"}}",
+ expectedError: nil,
+ },
+ {
+ descr: "error with use_go_template",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "First line",
+ Description: "open noneexistingfile.txt: no such file or directory",
+ },
+ comments: "First line\n\n{{import \"noneexistingfile.txt\"}}",
+ expectedError: nil,
+ useGoTemplate: true,
+ },
+ {
+ descr: "template with use_go_template",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "Template",
+ Description: `Description "which means nothing"`,
+ },
+ comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ expectedError: nil,
+ useGoTemplate: true,
+ },
+ {
+ descr: "template with use_go_template and go_template_args",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "Template",
+ Description: `Description "which means nothing" for environment test with value my_value`,
+ },
+ comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} for " +
+ "environment {{arg \"environment\"}} with value {{arg \"my_key\"}}",
+ expectedError: nil,
+ useGoTemplate: true,
+ goTemplateArgs: []string{"my_key=my_value", "environment=test"},
+ },
+ {
+ descr: "template with use_go_template and undefined go_template_args",
+ openapiSwaggerObject: &openapiSchemaObject{},
+ expectedOpenAPIObject: &openapiSchemaObject{
+ Title: "Template",
+ Description: `Description "which means nothing" for environment test with value ` +
+ `goTemplateArg something_undefined not found`,
+ },
+ comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} for " +
+ "environment {{arg \"environment\"}} with value {{arg \"something_undefined\"}}",
+ expectedError: nil,
+ useGoTemplate: true,
+ goTemplateArgs: []string{"environment=test"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.descr, func(t *testing.T) {
+ reg := descriptor.NewRegistry()
+ if test.useGoTemplate {
+ reg.SetUseGoTemplate(true)
+ }
+ if len(test.goTemplateArgs) > 0 {
+ reg.SetGoTemplateArgs(test.goTemplateArgs)
+ }
+ err := updateOpenAPIDataFromComments(reg, test.openapiSwaggerObject, nil, test.comments, false)
+ if test.expectedError == nil {
+ if err != nil {
+ t.Errorf("unexpected error '%v'", err)
+ }
+ if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
+ t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
+ }
+ } else {
+ if err == nil {
+ t.Error("expected update error not returned")
+ }
+ if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
+ t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
+ }
+ if err.Error() != test.expectedError.Error() {
+ t.Errorf("expected error malformed, expected %q, got %q", test.expectedError.Error(), err.Error())
+ }
+ }
+ })
+ }
+}
+
+func TestMessageOptionsWithGoTemplate(t *testing.T) {
+ tests := []struct {
+ descr string
+ msgDescs []*descriptorpb.DescriptorProto
+ schema map[string]*openapi_options.Schema // per-message schema to add
+ defs openapiDefinitionsObject
+ openAPIOptions *openapiconfigv3.OpenAPIOptions
+ useGoTemplate bool
+ goTemplateArgs []string
+ }{
+ {
+ descr: "external docs option",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "Message",
+ Description: `Description "which means nothing"`,
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: `Description "which means nothing"`,
+ },
+ },
+ },
+ useGoTemplate: true,
+ },
+ {
+ descr: "external docs option",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ },
+ },
+ useGoTemplate: false,
+ },
+ {
+ descr: "external docs option with go template args",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ schema: map[string]*openapi_options.Schema{
+ "Message": {
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
+ "{{arg \"my_key\"}}",
+ },
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
+ "{{arg \"my_key\"}}",
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "Message",
+ Description: `Description "which means nothing" too`,
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: `Description "which means nothing" too`,
+ },
+ },
+ },
+ useGoTemplate: true,
+ goTemplateArgs: []string{"my_key=too"},
+ },
+ {
+ descr: "registered OpenAPIOption",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Message: []*openapiconfigv3.OpenAPIMessageOption{
+ {
+ Message: "example.Message",
+ Option: &openapi_options.Schema{
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
+ },
+ },
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "Message",
+ Description: `Description "which means nothing"`,
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: `Description "which means nothing"`,
+ },
+ },
+ },
+ useGoTemplate: true,
+ },
+ {
+ descr: "registered OpenAPIOption with go template args",
+ msgDescs: []*descriptorpb.DescriptorProto{
+ {Name: proto.String("Message")},
+ },
+ openAPIOptions: &openapiconfigv3.OpenAPIOptions{
+ Message: []*openapiconfigv3.OpenAPIMessageOption{
+ {
+ Message: "example.Message",
+ Option: &openapi_options.Schema{
+ JsonSchema: &openapi_options.JSONSchema{
+ Title: "{{.Name}}",
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
+ "{{arg \"my_key\"}}",
+ },
+ ExternalDocs: &openapi_options.ExternalDocumentation{
+ Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
+ "{{arg \"my_key\"}}",
+ },
+ },
+ },
+ },
+ },
+ defs: map[string]openapiSchemaObject{
+ "Message": {
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Title: "Message",
+ Description: `Description "which means nothing" too`,
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: `Description "which means nothing" too`,
+ },
+ },
+ },
+ useGoTemplate: true,
+ goTemplateArgs: []string{"my_key=too"},
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.descr, func(t *testing.T) {
+ msgs := []*descriptor.Message{}
+ for _, msgdesc := range test.msgDescs {
+ msgdesc.Options = &descriptorpb.MessageOptions{}
+ msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
+ }
+
+ reg := descriptor.NewRegistry()
+ reg.SetUseGoTemplate(test.useGoTemplate)
+ reg.SetGoTemplateArgs(test.goTemplateArgs)
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: test.msgDescs,
+ EnumType: []*descriptorpb.EnumDescriptorProto{},
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ Messages: msgs,
+ }
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+
+ msgMap := map[string]*descriptor.Message{}
+ for _, d := range test.msgDescs {
+ name := d.GetName()
+ msg, err := reg.LookupMsg("example", name)
+ if err != nil {
+ t.Fatalf("lookup message %v: %v", name, err)
+ }
+ msgMap[msg.FQMN()] = msg
+
+ if schema, ok := test.schema[name]; ok {
+ proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, schema)
+ }
+ }
+
+ if test.openAPIOptions != nil {
+ if err := reg.RegisterOpenAPIOptionsv3(test.openAPIOptions); err != nil {
+ t.Fatalf("failed to register OpenAPI options: %s", err)
+ }
+ }
+
+ refs := make(refMap)
+ actual := make(openapiDefinitionsObject)
+ if err := renderMessagesAsDefinition(msgMap, actual, reg, refs, nil); err != nil {
+ t.Errorf("renderMessagesAsDefinition failed with: %s", err)
+ }
+
+ if !reflect.DeepEqual(actual, test.defs) {
+ t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
+ }
+ })
+ }
+}
+
+func TestTagsWithGoTemplate(t *testing.T) {
+ reg := descriptor.NewRegistry()
+ reg.SetUseGoTemplate(true)
+ reg.SetGoTemplateArgs([]string{"my_key=my_value"})
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Options: &descriptorpb.ServiceOptions{},
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{},
+ MessageType: []*descriptorpb.DescriptorProto{},
+ EnumType: []*descriptorpb.EnumDescriptorProto{},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ Messages: []*descriptor.Message{},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ },
+ },
+ }
+
+ // Set tag through service extension
+ proto.SetExtension(file.GetService()[0].Options, openapi_options.E_Openapiv2Tag, &openapi_options.Tag{
+ Name: "service tag",
+ Description: "{{ .Name }}!",
+ })
+
+ // Set tags through file extension
+ swagger := openapi_options.Swagger{
+ Tags: []*openapi_options.Tag{
+ {
+ Name: "not a service tag",
+ Description: "{{ import \"file\" }}",
+ },
+ {
+ Name: "ExampleService",
+ Description: "ExampleService!",
+ },
+ {
+ Name: "not a service tag 2",
+ Description: "{{ import \"file\" }}",
+ },
+ {
+ Name: "Service with my_key",
+ Description: "the {{arg \"my_key\"}}",
+ },
+ },
+ }
+ proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
+
+ actual, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+ expectedTags := []openapiTagObject{
+ {
+ Name: "not a service tag",
+ Description: "open file: no such file or directory",
+ },
+ {
+ Name: "ExampleService",
+ Description: "ExampleService!",
+ },
+ {
+ Name: "not a service tag 2",
+ Description: "open file: no such file or directory",
+ },
+ {
+ Name: "Service with my_key",
+ Description: "the my_value",
+ },
+ {
+ Name: "service tag",
+ Description: "ExampleService!",
+ },
+ }
+ if !reflect.DeepEqual(actual.Tags, expectedTags) {
+ t.Errorf("Expected tags %+v, not %+v", expectedTags, actual.Tags)
+ }
+}
+
+func TestTemplateWithoutErrorDefinition(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{},
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Echo"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Errorf("failed to reg.Load(): %v", err)
+ return
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ defRsp, ok := result.getPathItemObject("/v1/echo").Post.Responses["default"]
+ if !ok {
+ return
+ }
+
+ ref := defRsp.Schema.schemaCore.Ref
+ refName := strings.TrimPrefix(ref, "#/definitions/")
+ if refName == "" {
+ t.Fatal("created default Error response with empty reflink")
+ }
+
+ if _, ok := result.Definitions[refName]; !ok {
+ t.Errorf("default Error response with reflink '%v', but its definition was not found", refName)
+ }
+}
+
+func TestSingleServiceTemplateWithDuplicateHttp1Operations(t *testing.T) {
+ fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
+ field1 := &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Number: proto.Int32(1),
+ Type: &fieldType,
+ }
+
+ getFooMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("GetFooRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ getFooMsg := &descriptor.Message{
+ DescriptorProto: getFooMsgDesc,
+ }
+ deleteFooMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("DeleteFooRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ deleteFooMsg := &descriptor.Message{
+ DescriptorProto: deleteFooMsgDesc,
+ }
+ getFoo := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("GetFoo"),
+ InputType: proto.String("GetFooRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+ deleteFoo := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("DeleteFoo"),
+ InputType: proto.String("DeleteFooRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ getBarMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("GetBarRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ getBarMsg := &descriptor.Message{
+ DescriptorProto: getBarMsgDesc,
+ }
+ deleteBarMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("DeleteBarRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ deleteBarMsg := &descriptor.Message{
+ DescriptorProto: deleteBarMsgDesc,
+ }
+ getBar := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("GetBar"),
+ InputType: proto.String("GetBarRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+ deleteBar := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("DeleteBar"),
+ InputType: proto.String("DeleteBarRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ svc1 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Service1"),
+ Method: []*descriptorpb.MethodDescriptorProto{getFoo, deleteFoo, getBar, deleteBar},
+ }
+
+ emptyMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("EmptyMessage"),
+ }
+ emptyMsg := &descriptor.Message{
+ DescriptorProto: emptyMsgDesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("service1.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{getBarMsgDesc, deleteBarMsgDesc, getFooMsgDesc, deleteFooMsgDesc, emptyMsgDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc1},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{getFooMsg, deleteFooMsg, getBarMsg, deleteBarMsg, emptyMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: getFoo,
+ RequestType: getFooMsg,
+ ResponseType: getFooMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=foos/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: getFooMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: deleteFoo,
+ RequestType: deleteFooMsg,
+ ResponseType: emptyMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "DELETE",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=foos/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: deleteFooMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: getBar,
+ RequestType: getBarMsg,
+ ResponseType: getBarMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=bars/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: getBarMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: deleteBar,
+ RequestType: deleteBarMsg,
+ ResponseType: emptyMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "DELETE",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=bars/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: deleteBarMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ if got, want := len(result.Paths), 2; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ firstOpGet := result.getPathItemObject("/v1/{name}").Get
+ if got, want := firstOpGet.OperationID, "Service1_GetFoo"; got != want {
+ t.Fatalf("First operation GET id differed, got %s want %s", got, want)
+ }
+ if got, want := len(firstOpGet.Parameters), 2; got != want {
+ t.Fatalf("First operation GET params length differed, got %d want %d", got, want)
+ }
+ if got, want := firstOpGet.Parameters[0].Name, "name"; got != want {
+ t.Fatalf("First operation GET first param name differed, got %s want %s", got, want)
+ }
+ if got, want := firstOpGet.Parameters[0].Pattern, "foos/[^/]+"; got != want {
+ t.Fatalf("First operation GET first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := firstOpGet.Parameters[1].In, "body"; got != want {
+ t.Fatalf("First operation GET second param 'in' differed, got %s want %s", got, want)
+ }
+
+ firstOpDelete := result.getPathItemObject("/v1/{name}").Delete
+ if got, want := firstOpDelete.OperationID, "Service1_DeleteFoo"; got != want {
+ t.Fatalf("First operation id DELETE differed, got %s want %s", got, want)
+ }
+ if got, want := len(firstOpDelete.Parameters), 2; got != want {
+ t.Fatalf("First operation DELETE params length differed, got %d want %d", got, want)
+ }
+ if got, want := firstOpDelete.Parameters[0].Name, "name"; got != want {
+ t.Fatalf("First operation DELETE first param name differed, got %s want %s", got, want)
+ }
+ if got, want := firstOpDelete.Parameters[0].Pattern, "foos/[^/]+"; got != want {
+ t.Fatalf("First operation DELETE first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := firstOpDelete.Parameters[1].In, "body"; got != want {
+ t.Fatalf("First operation DELETE second param 'in' differed, got %s want %s", got, want)
+ }
+
+ secondOpGet := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}").Get
+ if got, want := secondOpGet.OperationID, "Service1_GetBar"; got != want {
+ t.Fatalf("Second operation id GET differed, got %s want %s", got, want)
+ }
+ if got, want := len(secondOpGet.Parameters), 2; got != want {
+ t.Fatalf("Second operation GET params length differed, got %d want %d", got, want)
+ }
+ if got, want := secondOpGet.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
+ t.Fatalf("Second operation GET first param name differed, got %s want %s", got, want)
+ }
+ if got, want := secondOpGet.Parameters[0].Pattern, "bars/[^/]+"; got != want {
+ t.Fatalf("Second operation GET first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := secondOpGet.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Second operation GET second param 'in' differed, got %s want %s", got, want)
+ }
+
+ secondOpDelete := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}").Delete
+ if got, want := secondOpDelete.OperationID, "Service1_DeleteBar"; got != want {
+ t.Fatalf("Second operation id differed, got %s want %s", got, want)
+ }
+ if got, want := len(secondOpDelete.Parameters), 2; got != want {
+ t.Fatalf("Second operation params length differed, got %d want %d", got, want)
+ }
+ if got, want := secondOpDelete.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
+ t.Fatalf("Second operation first param name differed, got %s want %s", got, want)
+ }
+ if got, want := secondOpDelete.Parameters[0].Pattern, "bars/[^/]+"; got != want {
+ t.Fatalf("Second operation first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := secondOpDelete.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Second operation third param 'in' differed, got %s want %s", got, want)
+ }
+}
+
+func getOperation(pathItem openapiPathItemObject, httpMethod string) *openapiOperationObject {
+ switch httpMethod {
+ case "GET":
+ return pathItem.Get
+ case "POST":
+ return pathItem.Post
+ case "PUT":
+ return pathItem.Put
+ case "DELETE":
+ return pathItem.Delete
+ case "PATCH":
+ return pathItem.Patch
+ case "HEAD":
+ return pathItem.Head
+ case "OPTIONS":
+ return pathItem.Options
+ default:
+ return nil
+ }
+}
+
+func TestSingleServiceTemplateWithDuplicateInAllSupportedHttp1Operations(t *testing.T) {
+ supportedMethods := []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
+
+ for _, method := range supportedMethods {
+ fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
+ field1 := &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Number: proto.Int32(1),
+ Type: &fieldType,
+ }
+
+ methodFooMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String(method + "FooRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ methodFooMsg := &descriptor.Message{
+ DescriptorProto: methodFooMsgDesc,
+ }
+ methodFoo := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String(method + "Foo"),
+ InputType: proto.String(method + "FooRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ methodBarMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String(method + "BarRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ methodBarMsg := &descriptor.Message{
+ DescriptorProto: methodBarMsgDesc,
+ }
+ methodBar := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String(method + "Bar"),
+ InputType: proto.String(method + "BarRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ svc1 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Service1"),
+ Method: []*descriptorpb.MethodDescriptorProto{methodFoo, methodBar},
+ }
+
+ emptyMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("EmptyMessage"),
+ }
+ emptyMsg := &descriptor.Message{
+ DescriptorProto: emptyMsgDesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("service1.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{methodBarMsgDesc, methodFooMsgDesc, emptyMsgDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc1},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{methodFooMsg, methodBarMsg, emptyMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: methodFoo,
+ RequestType: methodFooMsg,
+ ResponseType: methodFooMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: method,
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=foos/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: methodFooMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: methodBar,
+ RequestType: methodBarMsg,
+ ResponseType: methodBarMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: method,
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=bars/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: methodBarMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ if got, want := len(result.Paths), 2; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ firstOpMethod := getOperation(result.getPathItemObject("/v1/{name}"), method)
+ if got, want := firstOpMethod.OperationID, "Service1_"+method+"Foo"; got != want {
+ t.Fatalf("First operation %s id differed, got %s want %s", method, got, want)
+ }
+ if got, want := len(firstOpMethod.Parameters), 2; got != want {
+ t.Fatalf("First operation %s params length differed, got %d want %d", method, got, want)
+ }
+ if got, want := firstOpMethod.Parameters[0].Name, "name"; got != want {
+ t.Fatalf("First operation %s first param name differed, got %s want %s", method, got, want)
+ }
+ if got, want := firstOpMethod.Parameters[0].Pattern, "foos/[^/]+"; got != want {
+ t.Fatalf("First operation %s first param pattern differed, got %s want %s", method, got, want)
+ }
+ if got, want := firstOpMethod.Parameters[1].In, "body"; got != want {
+ t.Fatalf("First operation %s second param 'in' differed, got %s want %s", method, got, want)
+ }
+
+ secondOpMethod := getOperation(result.getPathItemObject("/v1/{name"+pathParamUniqueSuffixDeliminator+"1}"), method)
+ if got, want := secondOpMethod.OperationID, "Service1_"+method+"Bar"; got != want {
+ t.Fatalf("Second operation id %s differed, got %s want %s", method, got, want)
+ }
+ if got, want := len(secondOpMethod.Parameters), 2; got != want {
+ t.Fatalf("Second operation %s params length differed, got %d want %d", method, got, want)
+ }
+ if got, want := secondOpMethod.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
+ t.Fatalf("Second operation %s first param name differed, got %s want %s", method, got, want)
+ }
+ if got, want := secondOpMethod.Parameters[0].Pattern, "bars/[^/]+"; got != want {
+ t.Fatalf("Second operation %s first param pattern differed, got %s want %s", method, got, want)
+ }
+ if got, want := secondOpMethod.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Second operation %s second param 'in' differed, got %s want %s", method, got, want)
+ }
+ }
+}
+
+func TestSingleServiceTemplateWithDuplicateHttp1UnsupportedOperations(t *testing.T) {
+ fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
+ field1 := &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Number: proto.Int32(1),
+ Type: &fieldType,
+ }
+
+ unsupportedFooMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("UnsupportedFooRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ unsupportedFooMsg := &descriptor.Message{
+ DescriptorProto: unsupportedFooMsgDesc,
+ }
+ unsupportedFoo := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("UnsupportedFoo"),
+ InputType: proto.String("UnsupportedFooRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ unsupportedBarMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("UnsupportedBarRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ },
+ }
+ unsupportedBarMsg := &descriptor.Message{
+ DescriptorProto: unsupportedBarMsgDesc,
+ }
+ unsupportedBar := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("UnsupportedBar"),
+ InputType: proto.String("UnsupportedBarRequest"),
+ OutputType: proto.String("EmptyMessage"),
+ }
+
+ svc1 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Service1"),
+ Method: []*descriptorpb.MethodDescriptorProto{unsupportedFoo, unsupportedBar},
+ }
+
+ emptyMsgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("EmptyMessage"),
+ }
+ emptyMsg := &descriptor.Message{
+ DescriptorProto: emptyMsgDesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("service1.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{unsupportedBarMsgDesc, unsupportedFooMsgDesc, emptyMsgDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc1},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{unsupportedFooMsg, unsupportedBarMsg, emptyMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: unsupportedFoo,
+ RequestType: unsupportedFooMsg,
+ ResponseType: unsupportedFooMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "UNSUPPORTED",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=foos/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: unsupportedFooMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: unsupportedBar,
+ RequestType: unsupportedBarMsg,
+ ResponseType: unsupportedBarMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "UNSUPPORTED",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=bars/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: unsupportedBarMsg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ // Just should not crash, no special handling of unsupported HTTP methods
+ if got, want := len(result.Paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+}
+
+func TestTemplateWithDuplicateHttp1Operations(t *testing.T) {
+ fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
+ field1 := &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Number: proto.Int32(1),
+ Type: &fieldType,
+ }
+ field2 := &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("role"),
+ Number: proto.Int32(2),
+ Type: &fieldType,
+ }
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ field1,
+ field2,
+ },
+ }
+ meth1 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Method1"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ meth2 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Method2"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ svc1 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Service1"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
+ }
+ meth3 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Method3"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ meth4 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Method4"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+ svc2 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Service2"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth3, meth4},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("service1.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc1, svc2},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth1,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=organizations/*}/{role=roles/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field2,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "role",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: meth2,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=users/*}/{role=roles/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field2,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "role",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ {
+ ServiceDescriptorProto: svc2,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth3,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=users/*}/roles",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ {
+ MethodDescriptorProto: meth4,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/{name=groups/*}/{role=roles/*}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field1,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "name",
+ },
+ },
+ },
+ {
+ Target: &descriptor.Field{
+ FieldDescriptorProto: field2,
+ Message: msg,
+ },
+ FieldPath: descriptor.FieldPath{
+ {
+ Name: "role",
+ },
+ },
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ if got, want := len(result.Paths), 4; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ firstOp := result.getPathItemObject("/v1/{name}/{role}").Get
+ if got, want := firstOp.OperationID, "Service1_Method1"; got != want {
+ t.Fatalf("First operation id differed, got %s want %s", got, want)
+ }
+ if got, want := len(firstOp.Parameters), 3; got != want {
+ t.Fatalf("First operation params length differed, got %d want %d", got, want)
+ }
+ if got, want := firstOp.Parameters[0].Name, "name"; got != want {
+ t.Fatalf("First operation first param name differed, got %s want %s", got, want)
+ }
+ if got, want := firstOp.Parameters[0].Pattern, "organizations/[^/]+"; got != want {
+ t.Fatalf("First operation first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := firstOp.Parameters[1].Name, "role"; got != want {
+ t.Fatalf("First operation second param name differed, got %s want %s", got, want)
+ }
+ if got, want := firstOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
+ t.Fatalf("First operation second param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := firstOp.Parameters[2].In, "body"; got != want {
+ t.Fatalf("First operation third param 'in' differed, got %s want %s", got, want)
+ }
+
+ secondOp := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}/{role}").Get
+ if got, want := secondOp.OperationID, "Service1_Method2"; got != want {
+ t.Fatalf("Second operation id differed, got %s want %s", got, want)
+ }
+ if got, want := len(secondOp.Parameters), 3; got != want {
+ t.Fatalf("Second operation params length differed, got %d want %d", got, want)
+ }
+ if got, want := secondOp.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
+ t.Fatalf("Second operation first param name differed, got %s want %s", got, want)
+ }
+ if got, want := secondOp.Parameters[0].Pattern, "users/[^/]+"; got != want {
+ t.Fatalf("Second operation first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := secondOp.Parameters[1].Name, "role"; got != want {
+ t.Fatalf("Second operation second param name differed, got %s want %s", got, want)
+ }
+ if got, want := secondOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
+ t.Fatalf("Second operation second param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := secondOp.Parameters[2].In, "body"; got != want {
+ t.Fatalf("Second operation third param 'in' differed, got %s want %s", got, want)
+ }
+
+ thirdOp := result.getPathItemObject("/v1/{name}/roles").Get
+ if got, want := thirdOp.OperationID, "Service2_Method3"; got != want {
+ t.Fatalf("Third operation id differed, got %s want %s", got, want)
+ }
+ if got, want := len(thirdOp.Parameters), 2; got != want {
+ t.Fatalf("Third operation params length differed, got %d want %d", got, want)
+ }
+ if got, want := thirdOp.Parameters[0].Name, "name"; got != want {
+ t.Fatalf("Third operation first param name differed, got %s want %s", got, want)
+ }
+ if got, want := thirdOp.Parameters[0].Pattern, "users/[^/]+"; got != want {
+ t.Fatalf("Third operation first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := thirdOp.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Third operation second param 'in' differed, got %s want %s", got, want)
+ }
+
+ forthOp := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "2}/{role}").Get
+ if got, want := forthOp.OperationID, "Service2_Method4"; got != want {
+ t.Fatalf("Fourth operation id differed, got %s want %s", got, want)
+ }
+ if got, want := len(forthOp.Parameters), 3; got != want {
+ t.Fatalf("Fourth operation params length differed, got %d want %d", got, want)
+ }
+ if got, want := forthOp.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"2"; got != want {
+ t.Fatalf("Fourth operation first param name differed, got %s want %s", got, want)
+ }
+ if got, want := forthOp.Parameters[0].Pattern, "groups/[^/]+"; got != want {
+ t.Fatalf("Fourth operation first param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := forthOp.Parameters[1].Name, "role"; got != want {
+ t.Fatalf("Fourth operation second param name differed, got %s want %s", got, want)
+ }
+ if got, want := forthOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
+ t.Fatalf("Fourth operation second param pattern differed, got %s want %s", got, want)
+ }
+ if got, want := forthOp.Parameters[2].In, "body"; got != want {
+ t.Fatalf("Fourth operation second param 'in' differed, got %s want %s", got, want)
+ }
+}
+
+func Test_getReservedJsonName(t *testing.T) {
+ type args struct {
+ fieldName string
+ messageNameToFieldsToJSONName map[string]map[string]string
+ fieldNameToType map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ "test case 1: single dot use case",
+ args{
+ fieldName: "abc.a_1",
+ messageNameToFieldsToJSONName: map[string]map[string]string{
+ "Msg": {
+ "a_1": "a1JSONNAME",
+ "b_1": "b1JSONNAME",
+ },
+ },
+ fieldNameToType: map[string]string{
+ "abc": "pkg1.test.Msg",
+ "bcd": "pkg1.test.Msg",
+ },
+ },
+ "a1JSONNAME",
+ },
+ {
+ "test case 2: single dot use case with no existing field",
+ args{
+ fieldName: "abc.d_1",
+ messageNameToFieldsToJSONName: map[string]map[string]string{
+ "Msg": {
+ "a_1": "a1JSONNAME",
+ "b_1": "b1JSONNAME",
+ },
+ },
+ fieldNameToType: map[string]string{
+ "abc": "pkg1.test.Msg",
+ "bcd": "pkg1.test.Msg",
+ },
+ },
+ "",
+ },
+ {
+ "test case 3: double dot use case",
+ args{
+ fieldName: "pkg.abc.a_1",
+ messageNameToFieldsToJSONName: map[string]map[string]string{
+ "Msg": {
+ "a_1": "a1JSONNAME",
+ "b_1": "b1JSONNAME",
+ },
+ },
+ fieldNameToType: map[string]string{
+ "abc": "pkg1.test.Msg",
+ "bcd": "pkg1.test.Msg",
+ },
+ },
+ "a1JSONNAME",
+ },
+ {
+ "test case 4: double dot use case with a not existed field",
+ args{
+ fieldName: "pkg.abc.c_1",
+ messageNameToFieldsToJSONName: map[string]map[string]string{
+ "Msg": {
+ "a_1": "a1JSONNAME",
+ "b_1": "b1JSONNAME",
+ },
+ },
+ fieldNameToType: map[string]string{
+ "abc": "pkg1.test.Msg",
+ "bcd": "pkg1.test.Msg",
+ },
+ },
+ "",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := getReservedJSONName(tt.args.fieldName, tt.args.messageNameToFieldsToJSONName, tt.args.fieldNameToType); got != tt.want {
+ t.Errorf("getReservedJSONName() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestParseIncompleteSecurityRequirement(t *testing.T) {
+ swagger := openapi_options.Swagger{
+ Security: []*openapi_options.SecurityRequirement{
+ {
+ SecurityRequirement: map[string]*openapi_options.SecurityRequirement_SecurityRequirementValue{
+ "key": nil,
+ },
+ },
+ },
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ }
+ proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
+ reg := descriptor.NewRegistry()
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Errorf("failed to reg.Load(): %v", err)
+ return
+ }
+ _, err = applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err == nil {
+ t.Errorf("applyTemplate(%#v) did not error as expected", file)
+ return
+ }
+}
+
+func TestSubPathParams(t *testing.T) {
+ outerParams := []descriptor.Parameter{
+ {
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "prefix",
+ },
+ {
+ Name: "first",
+ },
+ },
+ },
+ {
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "prefix",
+ },
+ {
+ Name: "second",
+ },
+ {
+ Name: "deeper",
+ },
+ },
+ },
+ {
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "otherprefix",
+ },
+ {
+ Name: "third",
+ },
+ },
+ },
+ }
+ subParams := subPathParams("prefix", outerParams)
+
+ if got, want := len(subParams), 2; got != want {
+ t.Fatalf("Wrong number of path params, got %d want %d", got, want)
+ }
+ if got, want := len(subParams[0].FieldPath), 1; got != want {
+ t.Fatalf("Wrong length of path param 0, got %d want %d", got, want)
+ }
+ if got, want := subParams[0].FieldPath[0].Name, "first"; got != want {
+ t.Fatalf("Wrong path param 0, element 0, got %s want %s", got, want)
+ }
+ if got, want := len(subParams[1].FieldPath), 2; got != want {
+ t.Fatalf("Wrong length of path param 1 got %d want %d", got, want)
+ }
+ if got, want := subParams[1].FieldPath[0].Name, "second"; got != want {
+ t.Fatalf("Wrong path param 1, element 0, got %s want %s", got, want)
+ }
+ if got, want := subParams[1].FieldPath[1].Name, "deeper"; got != want {
+ t.Fatalf("Wrong path param 1, element 1, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesParameterDescriptionNoFieldBody(t *testing.T) {
+ optionsRaw := `{
+ "[grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema]": {
+ "jsonSchema": {
+ "title": "aMessage title",
+ "description": "aMessage description"
+ }
+ }
+ }`
+
+ options := &descriptorpb.MessageOptions{}
+ err := protojson.Unmarshal([]byte(optionsRaw), options)
+ if err != nil {
+ t.Fatalf("Error while unmarshalling options: %s", err.Error())
+ }
+
+ aMessageDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("AMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("project_id"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("other_field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ Options: options,
+ }
+ someResponseDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("SomeResponse"),
+ }
+ aMeth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("AMethod"),
+ InputType: proto.String("AMessage"),
+ OutputType: proto.String("SomeResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("Test"),
+ Method: []*descriptorpb.MethodDescriptorProto{aMeth},
+ }
+ aMessage := &descriptor.Message{
+ DescriptorProto: aMessageDesc,
+ }
+ someResponseMessage := &descriptor.Message{
+ DescriptorProto: someResponseDesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("api"),
+ Name: proto.String("test.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{aMessageDesc, someResponseDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{aMessage, someResponseMessage},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: aMeth,
+ RequestType: aMessage,
+ ResponseType: someResponseMessage,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/projects/someotherpath",
+ },
+ Body: &descriptor.Body{},
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err = reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ got := result.getPathItemObject("/v1/projects/someotherpath").Post.Parameters[0].Description
+ want := "aMessage description"
+
+ if got != want {
+ t.Fatalf("Wrong description for body parameter, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesWithBodyFieldNameInCamelCase(t *testing.T) {
+ userDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("User"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("role"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ updateDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("UpdateUserRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("user_object"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.User"),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("UpdateUser"),
+ InputType: proto.String("UpdateUserRequest"),
+ OutputType: proto.String("User"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("UserService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ userMsg := &descriptor.Message{
+ DescriptorProto: userDesc,
+ }
+ updateMsg := &descriptor.Message{
+ DescriptorProto: updateDesc,
+ }
+ nameField := &descriptor.Field{
+ Message: userMsg,
+ FieldDescriptorProto: userMsg.GetField()[0],
+ }
+ nameField.JsonName = proto.String("name")
+ roleField := &descriptor.Field{
+ Message: userMsg,
+ FieldDescriptorProto: userMsg.GetField()[1],
+ }
+ roleField.JsonName = proto.String("role")
+ userMsg.Fields = []*descriptor.Field{nameField, roleField}
+ userField := &descriptor.Field{
+ Message: updateMsg,
+ FieldMessage: userMsg,
+ FieldDescriptorProto: updateMsg.GetField()[0],
+ }
+ userField.JsonName = proto.String("userObject")
+ updateMsg.Fields = []*descriptor.Field{userField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String("user_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{userDesc, updateDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{userMsg, updateMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: updateMsg,
+ ResponseType: userMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/users/{user_object.name}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "user_object",
+ },
+ {
+ Name: "name",
+ },
+ }),
+ Target: nameField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "user_object",
+ Target: userField,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/v1/users/{userObject.name}"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/v1/users/{userObject.name}").Post
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "userObject.name"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "userObject"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ // The body parameter should be inlined and not contain 'name', as this is a path parameter.
+ schema := operation.Parameters[1].Schema
+ if got, want := schema.Ref, ""; got != want {
+ t.Fatalf("Wrong reference, got %s want %s", got, want)
+ }
+ props := schema.Properties
+ if props == nil {
+ t.Fatal("No properties on body parameter")
+ }
+ if got, want := len(*props), 1; got != want {
+ t.Fatalf("Properties length differed, got %d want %d", got, want)
+ }
+ for _, v := range *props {
+ if got, want := v.Key, "role"; got != want {
+ t.Fatalf("Wrong key for property, got %s want %s", got, want)
+ }
+ }
+}
+
+func TestRenderServicesWithBodyFieldHasFieldMask(t *testing.T) {
+ userDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("User"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("role"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ updateDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("UpdateUserRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("user_object"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.User"),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("update_mask"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".google.protobuf.FieldMask"),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("UpdateUser"),
+ InputType: proto.String("UpdateUserRequest"),
+ OutputType: proto.String("User"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("UserService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ userMsg := &descriptor.Message{
+ DescriptorProto: userDesc,
+ }
+ updateMsg := &descriptor.Message{
+ DescriptorProto: updateDesc,
+ }
+ nameField := &descriptor.Field{
+ Message: userMsg,
+ FieldDescriptorProto: userMsg.GetField()[0],
+ }
+ nameField.JsonName = proto.String("name")
+ roleField := &descriptor.Field{
+ Message: userMsg,
+ FieldDescriptorProto: userMsg.GetField()[1],
+ }
+ roleField.JsonName = proto.String("role")
+ userMsg.Fields = []*descriptor.Field{nameField, roleField}
+ userField := &descriptor.Field{
+ Message: updateMsg,
+ FieldMessage: userMsg,
+ FieldDescriptorProto: updateMsg.GetField()[0],
+ }
+ userField.JsonName = proto.String("userObject")
+ updateMaskField := &descriptor.Field{
+ Message: updateMsg,
+ FieldDescriptorProto: updateMsg.GetField()[1],
+ }
+ updateMaskField.JsonName = proto.String("updateMask")
+ updateMsg.Fields = []*descriptor.Field{userField, updateMaskField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String("user_service.proto"),
+ Dependency: []string{"google/well_known.proto"},
+ MessageType: []*descriptorpb.DescriptorProto{userDesc, updateDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{userMsg, updateMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: updateMsg,
+ ResponseType: userMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "PATCH",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/users/{user_object.name}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "user_object",
+ },
+ {
+ Name: "name",
+ },
+ }),
+ Target: nameField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: []descriptor.FieldPathComponent{
+ {
+ Name: "user_object",
+ Target: userField,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ reg.SetAllowPatchFeature(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{
+ {
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("google/well_known.proto"),
+ Package: proto.String("google.protobuf"),
+ Dependency: []string{},
+ MessageType: []*descriptorpb.DescriptorProto{
+ protodesc.ToDescriptorProto((&field_mask.FieldMask{}).ProtoReflect().Descriptor()),
+ },
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("google/well_known"),
+ },
+ },
+ file.FileDescriptorProto,
+ }})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/v1/users/{userObject.name}"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/v1/users/{userObject.name}").Patch
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "userObject.name"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "userObject"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesWithColonInPath(t *testing.T) {
+ jsonSchema := &openapi_options.JSONSchema{
+ FieldConfiguration: &openapi_options.JSONSchema_FieldConfiguration{
+ PathParamName: "overrideField",
+ },
+ }
+ fieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
+
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Options: fieldOptions,
+ },
+ },
+ }
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/my/{field}:foo",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "field",
+ },
+ }),
+ Target: reqField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/my/{overrideField}:foo"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/my/{overrideField}:foo").Post
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "overrideField"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Type, "string"; got != want {
+ t.Fatalf("Wrong parameter type, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "body"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesWithDoubleColonInPath(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/my/{field}:foo:bar",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "field",
+ },
+ }),
+ Target: reqField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/my/{field}:foo:bar"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/my/{field}:foo:bar").Post
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "field"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Type, "string"; got != want {
+ t.Fatalf("Wrong parameter type, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "body"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesWithColonLastInPath(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/my/{field}:",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "field",
+ },
+ }),
+ Target: reqField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/my/{field}:"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/my/{field}:").Post
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "field"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Type, "string"; got != want {
+ t.Fatalf("Wrong parameter type, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "body"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServicesWithColonInSegment(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/my/{field=segment/wi:th}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "field",
+ },
+ }),
+ Target: reqField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(reqFromFile(&file))
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/my/{field}"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/my/{field}").Post
+ if got, want := len(operation.Parameters), 2; got != want {
+ t.Fatalf("Parameters length differed, got %d want %d", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Name, "field"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].Type, "string"; got != want {
+ t.Fatalf("Wrong parameter type, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "body"; got != want {
+ t.Fatalf("Wrong parameter name, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location, got %s want %s", got, want)
+ }
+}
+
+func TestRenderServiceWithHeaderParameters(t *testing.T) {
+ file := func() descriptor.File {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ Options: &descriptorpb.MethodOptions{},
+ }
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+
+ return descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ }
+
+ type test struct {
+ file func() descriptor.File
+ openapiOperation *openapi_options.Operation
+ parameters openapiParametersObject
+ }
+
+ tests := map[string]*test{
+ "type string": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Type: openapi_options.HeaderParameter_STRING,
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Type: "string",
+ },
+ },
+ },
+ "type string with format": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Type: openapi_options.HeaderParameter_STRING,
+ Format: "uuid",
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Type: "string",
+ Format: "uuid",
+ },
+ },
+ },
+ "type integer": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Type: openapi_options.HeaderParameter_INTEGER,
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Type: "integer",
+ },
+ },
+ },
+ "type number": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Type: openapi_options.HeaderParameter_NUMBER,
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Type: "number",
+ },
+ },
+ },
+ "type boolean": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Type: openapi_options.HeaderParameter_BOOLEAN,
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Type: "boolean",
+ },
+ },
+ },
+ "header required": {
+ file: file,
+ openapiOperation: &openapi_options.Operation{
+ Parameters: &openapi_options.Parameters{
+ Headers: []*openapi_options.HeaderParameter{
+ {
+ Name: "X-Custom-Header",
+ Required: true,
+ Type: openapi_options.HeaderParameter_STRING,
+ },
+ },
+ },
+ },
+ parameters: openapiParametersObject{
+ {
+ Name: "X-Custom-Header",
+ In: "header",
+ Required: true,
+ Type: "string",
+ },
+ },
+ },
+ }
+
+ for name, test := range tests {
+ test := test
+
+ t.Run(name, func(t *testing.T) {
+ file := test.file()
+
+ proto.SetExtension(
+ proto.Message(file.Services[0].Methods[0].Options),
+ openapi_options.E_Openapiv2Operation,
+ test.openapiOperation)
+
+ reg := descriptor.NewRegistry()
+
+ fileCL := crossLinkFixture(&file)
+
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ }
+
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ params := result.getPathItemObject("/v1/echo").Get.Parameters
+
+ if !reflect.DeepEqual(params, test.parameters) {
+ t.Errorf("expected %+v, got %+v", test.parameters, params)
+ }
+ })
+ }
+}
+
+func GetPaths(req *openapiSwaggerObject) []string {
+ paths := make([]string, len(req.Paths))
+ i := 0
+ for _, k := range req.Paths {
+ paths[i] = k.Path
+ i++
+ }
+ return paths
+}
+
+func TestRenderServicesOpenapiPathsOrderPreserved(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth1 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod1"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ meth2 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod2"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth1,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/c/cpath",
+ },
+ },
+ },
+ }, {
+ MethodDescriptorProto: meth2,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/b/bpath",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetPreserveRPCOrder(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := result.Paths
+
+ firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
+ secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
+ for i, pathData := range paths {
+ switch i {
+ case 0:
+ if got, want := pathData.Path, firstRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 1:
+ if got, want := pathData.Path, secondRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ }
+ }
+}
+
+func TestRenderServicesOpenapiPathsOrderPreservedMultipleServices(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth1 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod1"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ meth2 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod2"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ meth3 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod3"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ meth4 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod4"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+
+ svc1 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyServiceOne"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
+ }
+ svc2 := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyServiceTwo"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth3, meth4},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc1, svc2},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth1,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/g/gpath",
+ },
+ },
+ },
+ }, {
+ MethodDescriptorProto: meth2,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/f/fpath",
+ },
+ },
+ },
+ },
+ },
+ }, {
+ ServiceDescriptorProto: svc1,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth3,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/c/cpath",
+ },
+ },
+ },
+ }, {
+ MethodDescriptorProto: meth4,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/b/bpath",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetPreserveRPCOrder(true)
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := result.Paths
+
+ firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
+ secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
+ thirdRPCPath := file.Services[1].Methods[0].Bindings[0].PathTmpl.Template
+ fourthRPCPath := file.Services[1].Methods[1].Bindings[0].PathTmpl.Template
+ for i, pathData := range paths {
+ switch i {
+ case 0:
+ if got, want := pathData.Path, firstRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 1:
+ if got, want := pathData.Path, secondRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 2:
+ if got, want := pathData.Path, thirdRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 3:
+ if got, want := pathData.Path, fourthRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ }
+ }
+}
+
+func TestRenderServicesOpenapiPathsOrderPreservedAdditionalBindings(t *testing.T) {
+ reqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyRequest"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+
+ resDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("MyResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("field"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ },
+ }
+ meth1 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod1"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+ meth2 := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("MyMethod2"),
+ InputType: proto.String("MyRequest"),
+ OutputType: proto.String("MyResponse"),
+ }
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("MyService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
+ }
+ reqMsg := &descriptor.Message{
+ DescriptorProto: reqDesc,
+ }
+ resMsg := &descriptor.Message{
+ DescriptorProto: resDesc,
+ }
+ reqField := &descriptor.Field{
+ Message: reqMsg,
+ FieldDescriptorProto: reqMsg.GetField()[0],
+ }
+ resField := &descriptor.Field{
+ Message: resMsg,
+ FieldDescriptorProto: resMsg.GetField()[0],
+ }
+ reqField.JsonName = proto.String("field")
+ resField.JsonName = proto.String("field")
+ reqMsg.Fields = []*descriptor.Field{reqField}
+ resMsg.Fields = []*descriptor.Field{resField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Package: proto.String("example"),
+ Name: proto.String(",my_service.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{reqDesc, resDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{reqMsg, resMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth1,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/c/cpath",
+ },
+ }, {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/additionalbinding",
+ },
+ },
+ },
+ }, {
+ MethodDescriptorProto: meth2,
+ RequestType: reqMsg,
+ ResponseType: resMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/b/bpath",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetPreserveRPCOrder(true)
+ reg.SetUseJSONNamesForFields(true)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
+ if err != nil {
+ t.Fatalf("failed to reg.Load(): %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := result.Paths
+ if err != nil {
+ t.Fatalf("failed to obtain extension paths: %v", err)
+ }
+
+ firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
+ firstRPCPathAdditionalBinding := file.Services[0].Methods[0].Bindings[1].PathTmpl.Template
+ secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
+ for i, pathData := range paths {
+ switch i {
+ case 0:
+ if got, want := pathData.Path, firstRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 1:
+ if got, want := pathData.Path, firstRPCPathAdditionalBinding; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ case 2:
+ if got, want := pathData.Path, secondRPCPath; got != want {
+ t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
+ }
+ }
+ }
+}
+
+func TestRenderServicesOpenapiRequiredBodyFieldContainingPathParam(t *testing.T) {
+ fieldBehaviorRequired := []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
+ requiredFieldOptions := new(descriptorpb.FieldOptions)
+ proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
+
+ bookDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("Book"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ },
+ {
+ Name: proto.String("type"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(2),
+ },
+ },
+ }
+ addBookReqDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("AddBookReq"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("book"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".Book"),
+ Number: proto.Int32(1),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("libraryId"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_UINT32.Enum(),
+ Number: proto.Int32(2),
+ Options: requiredFieldOptions,
+ },
+ {
+ Name: proto.String("isLatestEdition"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
+ Number: proto.Int32(3),
+ },
+ },
+ }
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("AddBook"),
+ InputType: proto.String("AddBookReq"),
+ OutputType: proto.String("Book"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("BookService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ bookMsg := &descriptor.Message{
+ DescriptorProto: bookDesc,
+ }
+ addBookReqMsg := &descriptor.Message{
+ DescriptorProto: addBookReqDesc,
+ }
+
+ nameField := &descriptor.Field{
+ Message: bookMsg,
+ FieldDescriptorProto: bookMsg.GetField()[0],
+ }
+ typeField := &descriptor.Field{
+ Message: bookMsg,
+ FieldDescriptorProto: bookMsg.GetField()[1],
+ }
+ bookMsg.Fields = []*descriptor.Field{nameField, typeField}
+
+ bookField := &descriptor.Field{
+ Message: addBookReqMsg,
+ FieldMessage: bookMsg,
+ FieldDescriptorProto: addBookReqMsg.GetField()[0],
+ }
+ libraryIdField := &descriptor.Field{
+ Message: addBookReqMsg,
+ FieldDescriptorProto: addBookReqMsg.GetField()[1],
+ }
+ isLatestEditionField := &descriptor.Field{
+ Message: addBookReqMsg,
+ FieldDescriptorProto: addBookReqMsg.GetField()[2],
+ }
+ addBookReqMsg.Fields = []*descriptor.Field{bookField, libraryIdField, isLatestEditionField}
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("book.proto"),
+ MessageType: []*descriptorpb.DescriptorProto{bookDesc, addBookReqDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{bookMsg, addBookReqMsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: addBookReqMsg,
+ ResponseType: bookMsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/books/{book.type}",
+ },
+ PathParams: []descriptor.Parameter{
+ {
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
+ {
+ Name: "book",
+ },
+ {
+ Name: "type",
+ },
+ }),
+ Target: typeField,
+ },
+ },
+ Body: &descriptor.Body{
+ FieldPath: []descriptor.FieldPathComponent{},
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(reqFromFile(fileCL))
+ if err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ return
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ paths := GetPaths(result)
+ if got, want := len(paths), 1; got != want {
+ t.Fatalf("Results path length differed, got %d want %d", got, want)
+ }
+
+ if got, want := paths[0], "/v1/books/{book.type}"; got != want {
+ t.Fatalf("Wrong results path, got %s want %s", got, want)
+ }
+
+ operation := *result.getPathItemObject("/v1/books/{book.type}").Post
+
+ if got, want := operation.Parameters[0].Name, "book.type"; got != want {
+ t.Fatalf("Wrong parameter name 0, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[0].In, "path"; got != want {
+ t.Fatalf("Wrong parameter location 0, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].Name, "body"; got != want {
+ t.Fatalf("Wrong parameter name 1, got %s want %s", got, want)
+ }
+
+ if got, want := operation.Parameters[1].In, "body"; got != want {
+ t.Fatalf("Wrong parameter location 1, got %s want %s", got, want)
+ }
+
+ if want, is, name := "#/definitions/BookServiceAddBookBody", operation.Parameters[1].Schema.schemaCore.Ref, "operation.Parameters[1].Schema.schemaCore.Ref"; !reflect.DeepEqual(is, want) {
+ t.Fatalf("%s = %s want to be %s", name, want, is)
+ }
+
+ definition, found := result.Definitions["BookServiceAddBookBody"]
+ if !found {
+ t.Fatalf("expecting definition to contain BookServiceAddBookBody")
+ }
+
+ if want, is, name := 3, len(*definition.Properties), "len(*definition.Properties)"; !reflect.DeepEqual(is, want) {
+ t.Fatalf("%s = %d want to be %d", name, want, is)
+ }
+
+ for index, keyValue := range []string{"book", "libraryId", "isLatestEdition"} {
+ if got, want := (*definition.Properties)[index].Key, keyValue; got != want {
+ t.Fatalf("Wrong definition property %d, got %s want %s", index, got, want)
+ }
+ }
+
+ correctRequiredFields := []string{"book", "libraryId"}
+ if got, want := definition.Required, correctRequiredFields; !reflect.DeepEqual(got, want) {
+ t.Fatalf("Wrong required fields in body definition, got = %s, want = %s", got, want)
+ }
+}
+
+func TestArrayMessageItemsType(t *testing.T) {
+ msgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("children"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.ExampleMessage"),
+ Number: proto.Int32(1),
+ JsonName: proto.String("children"),
+ },
+ },
+ }
+
+ nestDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("NestDescMessage"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("children"),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.ExampleMessage"),
+ Number: proto.Int32(1),
+ JsonName: proto.String("children"),
+ },
+ },
+ }
+
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("NestDescMessage"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgDesc,
+ }
+ nsg := &descriptor.Message{
+ DescriptorProto: nestDesc,
+ }
+ msg.Fields = []*descriptor.Field{
+ {
+ Message: msg,
+ FieldDescriptorProto: msg.GetField()[0],
+ },
+ }
+ nsg.Fields = []*descriptor.Field{
+ {
+ Message: nsg,
+ FieldDescriptorProto: nsg.GetField()[0],
+ },
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg, nsg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: nsg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ Body: &descriptor.Body{
+ FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
+ },
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(true)
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ if err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ {
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("acme/example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("acme/example"),
+ },
+ },
+ },
+ }); err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ expect := openapiDefinitionsObject{
+ "rpcStatus": openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Properties: &openapiSchemaObjectProperties{
+ keyVal{
+ Key: "code",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "integer",
+ Format: "int32",
+ },
+ },
+ },
+ keyVal{
+ Key: "message",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ keyVal{
+ Key: "details",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ Ref: "#/definitions/protobufAny",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "exampleExampleMessage": openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Properties: &openapiSchemaObjectProperties{
+ keyVal{
+ Key: "children",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ Ref: "#/definitions/exampleExampleMessage",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "exampleNestDescMessage": openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Properties: &openapiSchemaObjectProperties{
+ keyVal{
+ Key: "children",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "array",
+ Items: &openapiItemsObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ Ref: "#/definitions/exampleExampleMessage",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ "protobufAny": openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "object",
+ },
+ Properties: &openapiSchemaObjectProperties{
+ keyVal{
+ Key: "@type",
+ Value: openapiSchemaObject{
+ schemaCore: schemaCore{
+ Type: "string",
+ },
+ },
+ },
+ },
+ AdditionalProperties: &openapiSchemaObject{},
+ },
+ }
+
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+ if want, is, name := expect, result.Definitions, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
+ }
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestQueryParameterType(t *testing.T) {
+ ntDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("AddressEntry"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("key"),
+ Number: proto.Int32(1),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ JsonName: proto.String("key"),
+ },
+ {
+ Name: proto.String("value"),
+ Number: proto.Int32(2),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ JsonName: proto.String("value"),
+ },
+ },
+ Options: &descriptorpb.MessageOptions{
+ MapEntry: proto.Bool(true),
+ },
+ }
+
+ msgDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("Person"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("Address"),
+ Number: proto.Int32(1),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+ TypeName: proto.String(".example.com.Person.AddressEntry"),
+ JsonName: proto.String("Address"),
+ },
+ },
+ NestedType: []*descriptorpb.DescriptorProto{
+ ntDesc,
+ },
+ }
+
+ nesteDesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleResponse"),
+ Field: []*descriptorpb.FieldDescriptorProto{
+ {
+ Name: proto.String("Key"),
+ Number: proto.Int32(1),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ JsonName: proto.String("Key"),
+ },
+ {
+ Name: proto.String("Value"),
+ Number: proto.Int32(2),
+ Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
+ JsonName: proto.String("Value"),
+ },
+ },
+ }
+
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("Person"),
+ OutputType: proto.String("ExampleResponse"),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: msgDesc,
+ }
+ nt := &descriptor.Message{
+ DescriptorProto: ntDesc,
+ }
+ nest := &descriptor.Message{
+ DescriptorProto: nesteDesc,
+ }
+ msg.Fields = []*descriptor.Field{
+ {
+ Message: msg,
+ FieldDescriptorProto: msg.GetField()[0],
+ },
+ }
+ nt.Fields = []*descriptor.Field{
+ {
+ Message: nt,
+ FieldDescriptorProto: msg.GetField()[0],
+ },
+ }
+ nest.Fields = []*descriptor.Field{
+ {
+ Message: nest,
+ FieldDescriptorProto: nest.GetField()[0],
+ },
+ {
+ Message: nest,
+ FieldDescriptorProto: nest.GetField()[1],
+ },
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("person.proto"),
+ Package: proto.String("example.com"),
+ MessageType: []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg, nest},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: nest,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ expect := openapiPathsObject{{
+ Path: "/v1/echo",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Parameters: openapiParametersObject{
+ {
+ Name: "Address[string]",
+ In: "query",
+ Type: "integer",
+ },
+ },
+ },
+ },
+ }}
+
+ reg := descriptor.NewRegistry()
+ reg.SetUseJSONNamesForFields(false)
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ fileCL := crossLinkFixture(&file)
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ {
+ Name: proto.String("person.proto"),
+ Package: proto.String("example.com"),
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ MessageType: []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("person.proto"),
+ },
+ },
+ },
+ })
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+ if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
+ }
+
+ if want, is, name := expect[0].PathItemObject.Get.Parameters, result.getPathItemObject("/v1/echo").Get.Parameters, "Produces"; !reflect.DeepEqual(is, want) {
+ t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
+ }
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Echo"),
+ InputType: proto.String(".google.api.HttpBody"),
+ OutputType: proto.String(".google.api.HttpBody"),
+ ClientStreaming: proto.Bool(false),
+ ServerStreaming: proto.Bool(true),
+ }
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+ httpBodyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/api/httpbody.proto")
+ if err != nil {
+ t.Fatal(err)
+ }
+ httpBodyFile.SourceLocations()
+ desc, err := protoregistry.GlobalFiles.FindDescriptorByName("google.api.HttpBody")
+ if err != nil {
+ t.Fatal(err)
+ }
+ msg := &descriptor.Message{
+ DescriptorProto: protodesc.ToDescriptorProto(desc.(protoreflect.MessageDescriptor)),
+ File: &descriptor.File{
+ FileDescriptorProto: protodesc.ToFileDescriptorProto(httpBodyFile),
+ },
+ }
+ anyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/protobuf/any.proto")
+ if err != nil {
+ t.Fatal(err)
+ }
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ Dependency: []string{
+ "google/api/httpbody.proto",
+ },
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "POST",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ reg := descriptor.NewRegistry()
+ if err := AddErrorDefs(reg); err != nil {
+ t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
+ return
+ }
+ err = reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{
+ protodesc.ToFileDescriptorProto(anyFile),
+ protodesc.ToFileDescriptorProto(httpBodyFile),
+ file.FileDescriptorProto,
+ },
+ })
+ if err != nil {
+ t.Fatalf("failed to load code generator request: %v", err)
+ }
+ result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+ return
+ }
+
+ if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
+ }
+
+ if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
+ t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
+ } else {
+ if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
+ if want, got, name := "string", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ if want, got, name := "binary", streamExampleExampleMessage.Format, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Format`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ if want, got, name := "Free form byte stream", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
+ t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
+ }
+ if len(*streamExampleExampleMessage.Properties) != 0 {
+ t.Errorf("applyTemplate(%#v).Properties should be empty", file)
+ }
+ }
+
+ // If there was a failure, print out the input and the json result for debugging.
+ if t.Failed() {
+ t.Errorf("had: %s", file)
+ t.Errorf("got: %s", fmt.Sprint(result))
+ }
+}
+
+// Returns the openapiPathItemObject associated with a path.
+func (so openapiSwaggerObject) getPathItemObject(path string) openapiPathItemObject {
+ for _, pathData := range so.Paths {
+ if pathData.Path == path {
+ return pathData.PathItemObject
+ }
+ }
+
+ return openapiPathItemObject{}
+}
+
+func TestGetPathItemObjectSwaggerObjectMethod(t *testing.T) {
+ testCases := [...]struct {
+ testName string
+ swaggerObject openapiSwaggerObject
+ path string
+ expectedPathItemObject openapiPathItemObject
+ }{
+ {
+ testName: "Path present in swagger object",
+ swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }}},
+ path: "a/path",
+ expectedPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }, {
+ testName: "Path not present in swaggerObject",
+ swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }}},
+ path: "b/path",
+ expectedPathItemObject: openapiPathItemObject{},
+ }, {
+ testName: "Path present in swaggerPathsObject with multiple paths",
+ swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }, {
+ Path: "another/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "Another testful description",
+ },
+ },
+ }}},
+ path: "another/path",
+ expectedPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "Another testful description",
+ },
+ },
+ }, {
+ testName: "Path not present in swaggerObject with no paths",
+ swaggerObject: openapiSwaggerObject{},
+ path: "b/path",
+ expectedPathItemObject: openapiPathItemObject{},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.testName, func(t *testing.T) {
+ actualPathItemObject := tc.swaggerObject.getPathItemObject(tc.path)
+ if isEqual := reflect.DeepEqual(actualPathItemObject, tc.expectedPathItemObject); !isEqual {
+ t.Fatalf("Got pathItemObject: %#v, want pathItemObject: %#v", actualPathItemObject, tc.expectedPathItemObject)
+ }
+ })
+ }
+}
+
+func TestGetPathItemObjectFunction(t *testing.T) {
+ testCases := [...]struct {
+ testName string
+ paths openapiPathsObject
+ path string
+ expectedPathItemObject openapiPathItemObject
+ expectedIsPathPresent bool
+ }{
+ {
+ testName: "Path present in openapiPathsObject",
+ paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }},
+ path: "a/path",
+ expectedPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ expectedIsPathPresent: true,
+ }, {
+ testName: "Path not present in openapiPathsObject",
+ paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }},
+ path: "b/path",
+ expectedPathItemObject: openapiPathItemObject{},
+ expectedIsPathPresent: false,
+ }, {
+ testName: "Path present in openapiPathsObject with multiple paths",
+ paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }, {
+ Path: "another/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "Another testful description",
+ },
+ },
+ }},
+ path: "another/path",
+ expectedPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "Another testful description",
+ },
+ },
+ expectedIsPathPresent: true,
+ }, {
+ testName: "Path not present in empty openapiPathsObject",
+ paths: openapiPathsObject{},
+ path: "b/path",
+ expectedPathItemObject: openapiPathItemObject{},
+ expectedIsPathPresent: false,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.testName, func(t *testing.T) {
+ actualPathItemObject, actualIsPathPresent := getPathItemObject(tc.paths, tc.path)
+ if isEqual := reflect.DeepEqual(actualPathItemObject, tc.expectedPathItemObject); !isEqual {
+ t.Fatalf("Got pathItemObject: %#v, want pathItemObject: %#v", actualPathItemObject, tc.expectedPathItemObject)
+ }
+ if actualIsPathPresent != tc.expectedIsPathPresent {
+ t.Fatalf("Got isPathPresent bool: %t, want isPathPresent bool: %t", actualIsPathPresent, tc.expectedIsPathPresent)
+ }
+ })
+ }
+}
+
+func TestUpdatePaths(t *testing.T) {
+ testCases := [...]struct {
+ testName string
+ paths openapiPathsObject
+ pathToUpdate string
+ newPathItemObject openapiPathItemObject
+ expectedUpdatedPaths openapiPathsObject
+ }{
+ {
+ testName: "Path present in openapiPathsObject, pathItemObject updated.",
+ paths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }},
+ pathToUpdate: "a/path",
+ newPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A newly updated testful description",
+ },
+ },
+ expectedUpdatedPaths: openapiPathsObject{{
+ Path: "a/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A newly updated testful description",
+ },
+ },
+ }},
+ }, {
+ testName: "Path not present in openapiPathsObject, new path data appended.",
+ paths: openapiPathsObject{{
+ Path: "c/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }},
+ pathToUpdate: "b/path",
+ newPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A new testful description to add",
+ },
+ },
+ expectedUpdatedPaths: openapiPathsObject{{
+ Path: "c/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A testful description",
+ },
+ },
+ }, {
+ Path: "b/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A new testful description to add",
+ },
+ },
+ }},
+ }, {
+ testName: "No paths present in openapiPathsObject, new path data appended.",
+ paths: openapiPathsObject{},
+ pathToUpdate: "b/path",
+ newPathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A new testful description to add",
+ },
+ },
+ expectedUpdatedPaths: openapiPathsObject{{
+ Path: "b/path",
+ PathItemObject: openapiPathItemObject{
+ Get: &openapiOperationObject{
+ Description: "A new testful description to add",
+ },
+ },
+ }},
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.testName, func(t *testing.T) {
+ updatePaths(&tc.paths, tc.pathToUpdate, tc.newPathItemObject)
+ if pathsCorrectlyUpdated := reflect.DeepEqual(tc.paths, tc.expectedUpdatedPaths); !pathsCorrectlyUpdated {
+ t.Fatalf("Paths not correctly updated. Want %#v, got %#v", tc.expectedUpdatedPaths, tc.paths)
+ }
+ })
+ }
+}
+
+// Test that enum values have internal comments removed
+func TestEnumValueProtoComments(t *testing.T) {
+ reg := descriptor.NewRegistry()
+ name := "kind"
+ comments := "(-- this is a comment --)"
+
+ enum := &descriptor.Enum{
+ EnumDescriptorProto: &descriptorpb.EnumDescriptorProto{
+ Name: &name,
+ },
+ File: &descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ Name: new(string),
+ Package: new(string),
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{
+ Location: []*descriptorpb.SourceCodeInfo_Location{
+ {
+ LeadingComments: &comments,
+ },
+ },
+ },
+ },
+ },
+ }
+ comments = enumValueProtoComments(reg, enum)
+ if comments != "" {
+ t.Errorf("expected '', got '%v'", comments)
+ }
+}
+
+func MustMarshal(v interface{}) []byte {
+ b, err := json.Marshal(v)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+func TestMergeTags(t *testing.T) {
+ testCases := [...]struct {
+ testName string
+ existingTags []openapiTagObject
+ newTags []openapiTagObject
+ expectedMergedTags []openapiTagObject
+ }{
+ {
+ testName: "Simple merge.",
+ existingTags: []openapiTagObject{{
+ Name: "tag1",
+ Description: "tag1 description",
+ }},
+ newTags: []openapiTagObject{{
+ Name: "tag2",
+ Description: "tag2 description",
+ }},
+ expectedMergedTags: []openapiTagObject{{
+ Name: "tag1",
+ Description: "tag1 description",
+ }, {
+ Name: "tag2",
+ Description: "tag2 description",
+ }},
+ },
+ {
+ testName: "Merge description",
+ existingTags: []openapiTagObject{{
+ Name: "tag1",
+ Description: "tag1 description",
+ }, {
+ Name: "tag2",
+ }, {
+ Name: "tag3",
+ Description: "tag3 description",
+ }},
+ newTags: []openapiTagObject{{
+ Name: "tag2",
+ Description: "tag2 description",
+ }},
+ expectedMergedTags: []openapiTagObject{{
+ Name: "tag1",
+ Description: "tag1 description",
+ }, {
+ Name: "tag2",
+ Description: "tag2 description",
+ }, {
+ Name: "tag3",
+ Description: "tag3 description",
+ }},
+ },
+ {
+ testName: "Merge external docs",
+ existingTags: []openapiTagObject{{
+ Name: "tag1",
+ ExternalDocs: &openapiExternalDocumentationObject{},
+ }, {
+ Name: "tag2",
+ }, {
+ Name: "tag3",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag3 description",
+ },
+ }, {
+ Name: "tag4",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ URL: "tag4 url",
+ },
+ }},
+ newTags: []openapiTagObject{{
+ Name: "tag1",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag1 description",
+ },
+ }, {
+ Name: "tag2",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag2 description",
+ URL: "tag2 url",
+ },
+ }, {
+ Name: "tag3",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "ignored tag3 description",
+ URL: "tag3 url",
+ },
+ }, {
+ Name: "tag4",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag4 description",
+ },
+ }},
+ expectedMergedTags: []openapiTagObject{{
+ Name: "tag1",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag1 description",
+ },
+ }, {
+ Name: "tag2",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag2 description",
+ URL: "tag2 url",
+ },
+ }, {
+ Name: "tag3",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag3 description",
+ URL: "tag3 url",
+ },
+ }, {
+ Name: "tag4",
+ ExternalDocs: &openapiExternalDocumentationObject{
+ Description: "tag4 description",
+ URL: "tag4 url",
+ },
+ }},
+ },
+ {
+ testName: "Merge extensions",
+ existingTags: []openapiTagObject{{
+ Name: "tag1",
+ extensions: []extension{{key: "x-key1", value: MustMarshal("key1 extension")}},
+ }, {
+ Name: "tag2",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ {key: "x-key2", value: MustMarshal("key2 extension")},
+ },
+ }, {
+ Name: "tag3",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ },
+ }, {
+ Name: "tag4",
+ extensions: nil,
+ }},
+ newTags: []openapiTagObject{{
+ Name: "tag1",
+ extensions: []extension{{key: "x-key2", value: MustMarshal("key2 extension")}},
+ }, {
+ Name: "tag2",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ {key: "x-key2", value: MustMarshal("ignored key2 extension")},
+ {key: "x-key3", value: MustMarshal("key3 extension")},
+ },
+ }, {
+ Name: "tag3",
+ extensions: nil,
+ }, {
+ Name: "tag4",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ },
+ }},
+ expectedMergedTags: []openapiTagObject{{
+ Name: "tag1",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ {key: "x-key2", value: MustMarshal("key2 extension")},
+ },
+ }, {
+ Name: "tag2",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ {key: "x-key2", value: MustMarshal("key2 extension")},
+ {key: "x-key3", value: MustMarshal("key3 extension")},
+ },
+ }, {
+ Name: "tag3",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ },
+ }, {
+ Name: "tag4",
+ extensions: []extension{
+ {key: "x-key1", value: MustMarshal("key1 extension")},
+ },
+ }},
+ },
+ }
+ for _, tc := range testCases {
+ tc := tc
+ t.Run(tc.testName, func(t *testing.T) {
+ mergedTags := mergeTags(tc.existingTags, tc.newTags)
+ if !reflect.DeepEqual(tc.expectedMergedTags, mergedTags) {
+ t.Fatalf("%s: Tags not correctly merged. Want %#v, got %#v", tc.testName, tc.expectedMergedTags, mergedTags)
+ }
+ })
+ }
+}
+
+func TestApiVisibilityOption(t *testing.T) {
+ reg := descriptor.NewRegistry()
+
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+
+ methodExample := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ }
+
+ serviceOptions := &descriptorpb.ServiceOptions{}
+ proto.SetExtension(serviceOptions, visibility.E_ApiVisibility, &visibility.VisibilityRule{
+ Restriction: "INTERNAL",
+ })
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Options: serviceOptions,
+ Method: []*descriptorpb.MethodDescriptorProto{methodExample},
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: methodExample,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ Body: &descriptor.Body{FieldPath: nil},
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/example",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ err := reg.Load(&pluginpb.CodeGeneratorRequest{
+ ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
+ })
+ if err != nil {
+ t.Errorf("failed to reg.Load(req): %v", err)
+ }
+
+ actual, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ if len(actual.Definitions) != 0 {
+ t.Fatal("Definition should be excluded by api visibility option")
+ }
+}
+
+func TestRenderServicesOptionDeprecated(t *testing.T) {
+ testCases := [...]struct {
+ testName string
+ methodOptions descriptorpb.MethodOptions
+ openapiOperation *openapi_options.Operation
+ expectedDeprecated bool
+ }{
+ {
+ testName: "method option",
+ methodOptions: descriptorpb.MethodOptions{
+ Deprecated: proto.Bool(true),
+ },
+ expectedDeprecated: true,
+ },
+ {
+ testName: "openapi option",
+ openapiOperation: &openapi_options.Operation{
+ Deprecated: true,
+ },
+ expectedDeprecated: true,
+ },
+ {
+ testName: "empty openapi doesn't override method option",
+ methodOptions: descriptorpb.MethodOptions{
+ Deprecated: proto.Bool(true),
+ },
+ openapiOperation: &openapi_options.Operation{},
+ expectedDeprecated: true,
+ },
+ }
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(tc.testName, func(t *testing.T) {
+ msgdesc := &descriptorpb.DescriptorProto{
+ Name: proto.String("ExampleMessage"),
+ }
+
+ meth := &descriptorpb.MethodDescriptorProto{
+ Name: proto.String("Example"),
+ InputType: proto.String("ExampleMessage"),
+ OutputType: proto.String("ExampleMessage"),
+ Options: &tc.methodOptions,
+ }
+
+ svc := &descriptorpb.ServiceDescriptorProto{
+ Name: proto.String("ExampleService"),
+ Method: []*descriptorpb.MethodDescriptorProto{meth},
+ }
+
+ msg := &descriptor.Message{
+ DescriptorProto: msgdesc,
+ }
+
+ file := descriptor.File{
+ FileDescriptorProto: &descriptorpb.FileDescriptorProto{
+ SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+ Name: proto.String("example.proto"),
+ Package: proto.String("example"),
+ MessageType: []*descriptorpb.DescriptorProto{msgdesc},
+ Service: []*descriptorpb.ServiceDescriptorProto{svc},
+ Options: &descriptorpb.FileOptions{
+ GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
+ },
+ },
+ GoPkg: descriptor.GoPackage{
+ Path: "example.com/path/to/example/example.pb",
+ Name: "example_pb",
+ },
+ Messages: []*descriptor.Message{msg},
+ Services: []*descriptor.Service{
+ {
+ ServiceDescriptorProto: svc,
+ Methods: []*descriptor.Method{
+ {
+ MethodDescriptorProto: meth,
+ RequestType: msg,
+ ResponseType: msg,
+ Bindings: []*descriptor.Binding{
+ {
+ HTTPMethod: "GET",
+ PathTmpl: httprule.Template{
+ Version: 1,
+ OpCodes: []int{0, 0},
+ Template: "/v1/echo",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ if tc.openapiOperation != nil {
+ proto.SetExtension(
+ proto.Message(file.Services[0].Methods[0].Options),
+ openapi_options.E_Openapiv2Operation,
+ tc.openapiOperation,
+ )
+ }
+
+ reg := descriptor.NewRegistry()
+ reg.SetEnableRpcDeprecation(true)
+ fileCL := crossLinkFixture(&file)
+
+ if err := reg.Load(reqFromFile(fileCL)); err != nil {
+ t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+ }
+
+ result, err := applyTemplate(param{File: fileCL, reg: reg})
+ if err != nil {
+ t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
+ }
+
+ got := result.getPathItemObject("/v1/echo").Get.Deprecated
+ if got != tc.expectedDeprecated {
+ t.Fatalf("Wrong deprecated field, got %v want %v", got, tc.expectedDeprecated)
+ }
+ })
+ }
+}
+
+func Test_updateSwaggerObjectFromFieldBehavior(t *testing.T) {
+ type args struct {
+ s *openapiSchemaObject
+ j []annotations.FieldBehavior
+ reg *descriptor.Registry
+ field *descriptor.Field
+ }
+
+ regWithNoProto3FieldSemantics := &descriptor.Registry{}
+ regWithProto3FieldSemantics := &descriptor.Registry{}
+ regWithProto3FieldSemantics.SetUseProto3FieldSemantics(true)
+ proto3Field := &descriptor.Field{FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ }}
+ boolTrue := true
+ proto3FieldOptional := &descriptor.Field{FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+ Name: proto.String("name"),
+ Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+ Number: proto.Int32(1),
+ Proto3Optional: &boolTrue,
+ }}
+ tests := []struct {
+ name string
+ args args
+ required []string
+ }{
+ {
+ name: "FieldBehavior_REQUIRED",
+ args: args{
+ s: &openapiSchemaObject{},
+ j: []annotations.FieldBehavior{
+ annotations.FieldBehavior_REQUIRED,
+ },
+ reg: regWithNoProto3FieldSemantics,
+ field: proto3FieldOptional,
+ },
+ required: []string{"name"},
+ },
+ {
+ name: "No Required No Proto3 Optional",
+ args: args{
+ s: &openapiSchemaObject{},
+ j: []annotations.FieldBehavior{},
+ reg: regWithNoProto3FieldSemantics,
+ field: proto3FieldOptional,
+ },
+ required: nil,
+ },
+ {
+ name: "No Required Has Proto3 Optional",
+ args: args{
+ s: &openapiSchemaObject{},
+ j: []annotations.FieldBehavior{},
+ reg: regWithProto3FieldSemantics,
+ field: proto3FieldOptional,
+ },
+ required: nil,
+ },
+ {
+ name: "No Required Has Proto3 Required",
+ args: args{
+ s: &openapiSchemaObject{},
+ j: []annotations.FieldBehavior{},
+ reg: regWithProto3FieldSemantics,
+ field: proto3Field,
+ },
+ required: []string{"name"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ updateSwaggerObjectFromFieldBehavior(tt.args.s, tt.args.j, tt.args.reg, tt.args.field)
+ if !reflect.DeepEqual(tt.args.s.Required, tt.required) {
+ t.Errorf("updateSwaggerObjectFromFieldBehavior() = %v, want %v", tt.args.s.Required, tt.required)
+ }
+ })
+ }
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.prototext b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.prototext
new file mode 100644
index 00000000000..63a641be2b9
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.prototext
@@ -0,0 +1,32 @@
+file_to_generate: "your/service/v1/your_service.proto"
+proto_file: {
+ name: "your/service/v1/your_service.proto"
+ package: "your.service.v1"
+ message_type: {
+ name: "StringMessage"
+ field: {
+ name: "value"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "value"
+ }
+ }
+ service: {
+ name: "YourService"
+ method: {
+ name: "Echo"
+ input_type: ".your.service.v1.StringMessage"
+ output_type: ".your.service.v1.StringMessage"
+ options: {
+ [google.api.http]: {
+ post: "/api/echo"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "github.com/yourorg/yourprotos/gen/go/your/service/v1"
+ }
+ syntax: "proto3"
+}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.swagger.yaml b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.swagger.yaml
new file mode 100644
index 00000000000..89b65a9007f
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/path_item_object.swagger.yaml
@@ -0,0 +1,32 @@
+swagger: "2.0"
+info:
+ title: your/service/v1/your_service.proto
+ version: version not set
+tags:
+ - name: YourService
+consumes:
+ - application/json
+produces:
+ - application/json
+paths:
+ /api/echo:
+ post:
+ operationId: YourService_Echo
+ responses:
+ "200":
+ description: A successful response.
+ schema:
+ $ref: '#/definitions/v1StringMessage'
+ parameters:
+ - name: value
+ in: query
+ required: false
+ type: string
+ tags:
+ - YourService
+definitions:
+ v1StringMessage:
+ type: object
+ properties:
+ value:
+ type: string
diff --git a/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.prototext b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.prototext
new file mode 100644
index 00000000000..42ed06f5592
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.prototext
@@ -0,0 +1,32 @@
+file_to_generate: "test/service/v1/service.proto"
+proto_file: {
+ name: "test/service/v1/service.proto"
+ package: "test.service.v1"
+ message_type: {
+ name: "TestMessage"
+ field: {
+ name: "value"
+ number: 1
+ label: LABEL_OPTIONAL
+ type: TYPE_STRING
+ json_name: "value"
+ }
+ }
+ service: {
+ name: "TestService"
+ method: {
+ name: "Test"
+ input_type: ".test.service.v1.TestMessage"
+ output_type: ".test.service.v1.TestMessage"
+ options: {
+ [google.api.http]: {
+ post: "/v1/test"
+ body: "*"
+ }
+ }
+ }
+ }
+ options: {
+ go_package: "github.com/grpc-ecosystem/grpc-gateway/v2/test/service/v1;servicev1"
+ }
+}
\ No newline at end of file
diff --git a/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.swagger.yaml b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.swagger.yaml
new file mode 100644
index 00000000000..e4230eb15fc
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/testdata/generator/x_go_type.swagger.yaml
@@ -0,0 +1,37 @@
+swagger: "2.0"
+info:
+ title: test/service/v1/service.proto
+ version: version not set
+tags:
+- name: TestService
+consumes:
+- application/json
+produces:
+- application/json
+paths:
+ /v1/test:
+ post:
+ operationId: TestService_Test
+ responses:
+ "200":
+ description: A successful response.
+ schema:
+ $ref: '#/definitions/v1TestMessage'
+ parameters:
+ - name: body
+ in: body
+ required: true
+ schema:
+ $ref: '#/definitions/v1TestMessage'
+ tags:
+ - TestService
+definitions:
+ v1TestMessage:
+ type: object
+ properties:
+ value:
+ type: string
+ x-go-type:
+ import:
+ package: "github.com/grpc-ecosystem/grpc-gateway/v2/test/service/v1"
+ type: "TestMessage"
\ No newline at end of file
diff --git a/protoc-gen-openapiv3/internal/genopenapi/types.go b/protoc-gen-openapiv3/internal/genopenapi/types.go
new file mode 100644
index 00000000000..02c85784128
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/types.go
@@ -0,0 +1,361 @@
+package genopenapi
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ "gopkg.in/yaml.v3"
+)
+
+type param struct {
+ *descriptor.File
+ reg *descriptor.Registry
+}
+
+// http://swagger.io/specification/#infoObject
+type openapiInfoObject struct {
+ Title string `json:"title" yaml:"title"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
+ Version string `json:"version" yaml:"version"`
+
+ Contact *openapiContactObject `json:"contact,omitempty" yaml:"contact,omitempty"`
+ License *openapiLicenseObject `json:"license,omitempty" yaml:"license,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+// https://swagger.io/specification/#tagObject
+type openapiTagObject struct {
+ Name string `json:"name" yaml:"name"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ ExternalDocs *openapiExternalDocumentationObject `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+// http://swagger.io/specification/#contactObject
+type openapiContactObject struct {
+ Name string `json:"name,omitempty" yaml:"name,omitempty"`
+ URL string `json:"url,omitempty" yaml:"url,omitempty"`
+ Email string `json:"email,omitempty" yaml:"email,omitempty"`
+}
+
+// http://swagger.io/specification/#licenseObject
+type openapiLicenseObject struct {
+ Name string `json:"name,omitempty" yaml:"name,omitempty"`
+ URL string `json:"url,omitempty" yaml:"url,omitempty"`
+}
+
+// http://swagger.io/specification/#externalDocumentationObject
+type openapiExternalDocumentationObject struct {
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ URL string `json:"url,omitempty" yaml:"url,omitempty"`
+}
+
+type extension struct {
+ key string `json:"-" yaml:"-"`
+ value json.RawMessage `json:"-" yaml:"-"`
+}
+
+// http://swagger.io/specification/#swaggerObject
+type openapiSwaggerObject struct {
+ Swagger string `json:"swagger" yaml:"swagger"`
+ Info openapiInfoObject `json:"info" yaml:"info"`
+ Tags []openapiTagObject `json:"tags,omitempty" yaml:"tags,omitempty"`
+ Host string `json:"host,omitempty" yaml:"host,omitempty"`
+ BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
+ Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
+ Consumes []string `json:"consumes" yaml:"consumes"`
+ Produces []string `json:"produces" yaml:"produces"`
+ Paths openapiPathsObject `json:"paths" yaml:"paths"`
+ Definitions openapiDefinitionsObject `json:"definitions" yaml:"definitions"`
+ SecurityDefinitions openapiSecurityDefinitionsObject `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
+ Security []openapiSecurityRequirementObject `json:"security,omitempty" yaml:"security,omitempty"`
+ ExternalDocs *openapiExternalDocumentationObject `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+// http://swagger.io/specification/#securityDefinitionsObject
+type openapiSecurityDefinitionsObject map[string]openapiSecuritySchemeObject
+
+// http://swagger.io/specification/#securitySchemeObject
+type openapiSecuritySchemeObject struct {
+ Type string `json:"type" yaml:"type"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ Name string `json:"name,omitempty" yaml:"name,omitempty"`
+ In string `json:"in,omitempty" yaml:"in,omitempty"`
+ Flow string `json:"flow,omitempty" yaml:"flow,omitempty"`
+ AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
+ TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
+ Scopes openapiScopesObject `json:"scopes,omitempty" yaml:"scopes,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+// http://swagger.io/specification/#scopesObject
+type openapiScopesObject map[string]string
+
+// http://swagger.io/specification/#securityRequirementObject
+type openapiSecurityRequirementObject map[string][]string
+
+// http://swagger.io/specification/#pathsObject
+type openapiPathsObject []pathData
+
+type pathData struct {
+ Path string
+ PathItemObject openapiPathItemObject
+}
+
+// http://swagger.io/specification/#pathItemObject
+type openapiPathItemObject struct {
+ Get *openapiOperationObject `json:"get,omitempty" yaml:"get,omitempty"`
+ Delete *openapiOperationObject `json:"delete,omitempty" yaml:"delete,omitempty"`
+ Post *openapiOperationObject `json:"post,omitempty" yaml:"post,omitempty"`
+ Put *openapiOperationObject `json:"put,omitempty" yaml:"put,omitempty"`
+ Patch *openapiOperationObject `json:"patch,omitempty" yaml:"patch,omitempty"`
+ Head *openapiOperationObject `json:"head,omitempty" yaml:"head,omitempty"`
+ Options *openapiOperationObject `json:"options,omitempty" yaml:"options,omitempty"`
+ // While TRACE is supported in OpenAPI v3, it is not supported in OpenAPI v2
+ // Trace *openapiOperationObject `json:"trace,omitempty" yaml:"trace,omitempty"`
+}
+
+// http://swagger.io/specification/#operationObject
+type openapiOperationObject struct {
+ Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ OperationID string `json:"operationId" yaml:"operationId"`
+ Responses openapiResponsesObject `json:"responses" yaml:"responses"`
+ Parameters openapiParametersObject `json:"parameters,omitempty" yaml:"parameters,omitempty"`
+ Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
+ Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
+ Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
+ Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
+
+ Security *[]openapiSecurityRequirementObject `json:"security,omitempty" yaml:"security,omitempty"`
+ ExternalDocs *openapiExternalDocumentationObject `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+type openapiParametersObject []openapiParameterObject
+
+// http://swagger.io/specification/#parameterObject
+type openapiParameterObject struct {
+ Name string `json:"name" yaml:"name"`
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ In string `json:"in,omitempty" yaml:"in,omitempty"`
+ Required bool `json:"required" yaml:"required"`
+ Type string `json:"type,omitempty" yaml:"type,omitempty"`
+ Format string `json:"format,omitempty" yaml:"format,omitempty"`
+ UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
+ Items *openapiItemsObject `json:"items,omitempty" yaml:"items,omitempty"`
+ Enum interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
+ CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
+ Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
+ MinItems *int `json:"minItems,omitempty" yaml:"minItems,omitempty"`
+ Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
+
+ // Or you can explicitly refer to another type. If this is defined all
+ // other fields should be empty
+ Schema *openapiSchemaObject `json:"schema,omitempty" yaml:"schema,omitempty"`
+
+ extensions []extension
+}
+
+// core part of schema, which is common to itemsObject and schemaObject.
+// http://swagger.io/specification/v2/#itemsObject
+// The OAS3 spec (https://swagger.io/specification/#schemaObject) defines the
+// `nullable` field as part of a Schema Object. This behavior has been
+// "back-ported" to OAS2 as the Specification Extension `x-nullable`, and is
+// supported by generation tools such as swagger-codegen and go-swagger.
+// For protoc-gen-openapiv3, we'd want to add `nullable` instead.
+type schemaCore struct {
+ Type string `json:"type,omitempty" yaml:"type,omitempty"`
+ Format string `json:"format,omitempty" yaml:"format,omitempty"`
+ Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+ XNullable bool `json:"x-nullable,omitempty" yaml:"x-nullable,omitempty"`
+ Example RawExample `json:"example,omitempty" yaml:"example,omitempty"`
+
+ Items *openapiItemsObject `json:"items,omitempty" yaml:"items,omitempty"`
+
+ // If the item is an enumeration include a list of all the *NAMES* of the
+ // enum values. I'm not sure how well this will work but assuming all enums
+ // start from 0 index it will be great. I don't think that is a good assumption.
+ Enum interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
+ Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
+}
+
+type allOfEntry struct {
+ Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
+}
+
+type RawExample json.RawMessage
+
+func (m RawExample) MarshalJSON() ([]byte, error) {
+ return (json.RawMessage)(m).MarshalJSON()
+}
+
+func (m *RawExample) UnmarshalJSON(data []byte) error {
+ return (*json.RawMessage)(m).UnmarshalJSON(data)
+}
+
+// MarshalYAML implements yaml.Marshaler interface.
+//
+// It converts RawExample to one of yaml-supported types and returns it.
+//
+// From yaml.Marshaler docs: The Marshaler interface may be implemented
+// by types to customize their behavior when being marshaled into a YAML
+// document. The returned value is marshaled in place of the original
+// value implementing Marshaler.
+func (e RawExample) MarshalYAML() (interface{}, error) {
+ // From docs, json.Unmarshal will store one of next types to data:
+ // - bool, for JSON booleans;
+ // - float64, for JSON numbers;
+ // - string, for JSON strings;
+ // - []interface{}, for JSON arrays;
+ // - map[string]interface{}, for JSON objects;
+ // - nil for JSON null.
+ var data interface{}
+ if err := json.Unmarshal(e, &data); err != nil {
+ return nil, err
+ }
+
+ return data, nil
+}
+
+func (s *schemaCore) setRefFromFQN(ref string, reg *descriptor.Registry) error {
+ name, ok := fullyQualifiedNameToOpenAPIName(ref, reg)
+ if !ok {
+ return fmt.Errorf("setRefFromFQN: can't resolve OpenAPI name from %q", ref)
+ }
+ s.Ref = fmt.Sprintf("#/definitions/%s", name)
+ return nil
+}
+
+type openapiItemsObject openapiSchemaObject
+
+// http://swagger.io/specification/#responsesObject
+type openapiResponsesObject map[string]openapiResponseObject
+
+// http://swagger.io/specification/#responseObject
+type openapiResponseObject struct {
+ Description string `json:"description" yaml:"description"`
+ Schema openapiSchemaObject `json:"schema" yaml:"schema"`
+ Examples map[string]interface{} `json:"examples,omitempty" yaml:"examples,omitempty"`
+ Headers openapiHeadersObject `json:"headers,omitempty" yaml:"headers,omitempty"`
+
+ extensions []extension `json:"-" yaml:"-"`
+}
+
+type openapiHeadersObject map[string]openapiHeaderObject
+
+// http://swagger.io/specification/#headerObject
+type openapiHeaderObject struct {
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ Type string `json:"type,omitempty" yaml:"type,omitempty"`
+ Format string `json:"format,omitempty" yaml:"format,omitempty"`
+ Default RawExample `json:"default,omitempty" yaml:"default,omitempty"`
+ Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
+}
+
+type keyVal struct {
+ Key string
+ Value interface{}
+}
+
+type openapiSchemaObjectProperties []keyVal
+
+func (p openapiSchemaObjectProperties) MarshalYAML() (interface{}, error) {
+ n := yaml.Node{
+ Kind: yaml.MappingNode,
+ Content: make([]*yaml.Node, len(p)*2),
+ }
+ for i, v := range p {
+ keyNode := yaml.Node{}
+ if err := keyNode.Encode(v.Key); err != nil {
+ return nil, err
+ }
+ valueNode := yaml.Node{}
+ if err := valueNode.Encode(v.Value); err != nil {
+ return nil, err
+ }
+ n.Content[i*2+0] = &keyNode
+ n.Content[i*2+1] = &valueNode
+ }
+ return n, nil
+}
+
+func (op openapiSchemaObjectProperties) MarshalJSON() ([]byte, error) {
+ var buf bytes.Buffer
+ buf.WriteString("{")
+ for i, kv := range op {
+ if i != 0 {
+ buf.WriteString(",")
+ }
+ key, err := json.Marshal(kv.Key)
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(key)
+ buf.WriteString(":")
+ val, err := json.Marshal(kv.Value)
+ if err != nil {
+ return nil, err
+ }
+ buf.Write(val)
+ }
+
+ buf.WriteString("}")
+ return buf.Bytes(), nil
+}
+
+// http://swagger.io/specification/#schemaObject
+type openapiSchemaObject struct {
+ schemaCore `yaml:",inline"`
+ // Properties can be recursively defined
+ Properties *openapiSchemaObjectProperties `json:"properties,omitempty" yaml:"properties,omitempty"`
+ AdditionalProperties *openapiSchemaObject `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
+
+ Description string `json:"description,omitempty" yaml:"description,omitempty"`
+ Title string `json:"title,omitempty" yaml:"title,omitempty"`
+
+ ExternalDocs *openapiExternalDocumentationObject `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
+
+ ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
+ MultipleOf float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
+ Maximum float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
+ ExclusiveMaximum bool `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"`
+ Minimum float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
+ ExclusiveMinimum bool `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
+ MaxLength uint64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
+ MinLength uint64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
+ Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
+ MaxItems uint64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
+ MinItems uint64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
+ UniqueItems bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
+ MaxProperties uint64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
+ MinProperties uint64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
+ Required []string `json:"required,omitempty" yaml:"required,omitempty"`
+
+ extensions []extension
+
+ AllOf []allOfEntry `json:"allOf,omitempty" yaml:"allOf,omitempty"`
+}
+
+// http://swagger.io/specification/#definitionsObject
+type openapiDefinitionsObject map[string]openapiSchemaObject
+
+// Internal type mapping from FQMN to descriptor.Message. Used as a set by the
+// findServiceMessages function.
+type messageMap map[string]*descriptor.Message
+
+// Internal type mapping from FQEN to descriptor.Enum. Used as a set by the
+// findServiceMessages function.
+type enumMap map[string]*descriptor.Enum
+
+// Internal type to store used references.
+type refMap map[string]struct{}
diff --git a/protoc-gen-openapiv3/internal/genopenapi/types_test.go b/protoc-gen-openapiv3/internal/genopenapi/types_test.go
new file mode 100644
index 00000000000..44596d52117
--- /dev/null
+++ b/protoc-gen-openapiv3/internal/genopenapi/types_test.go
@@ -0,0 +1,112 @@
+package genopenapi
+
+import (
+ "encoding/json"
+ "strings"
+ "testing"
+
+ "gopkg.in/yaml.v3"
+)
+
+func newSpaceReplacer() *strings.Replacer {
+ return strings.NewReplacer(" ", "", "\n", "", "\t", "")
+}
+
+func TestRawExample(t *testing.T) {
+ t.Parallel()
+
+ testCases := [...]struct {
+ In RawExample
+ Exp string
+ }{{
+ In: RawExample(`1`),
+ Exp: `1`,
+ }, {
+ In: RawExample(`"1"`),
+ Exp: `"1"`,
+ }, {
+ In: RawExample(`{"hello":"worldr"}`),
+ Exp: `
+ hello:
+ worldr
+ `,
+ }}
+
+ sr := newSpaceReplacer()
+
+ for _, tc := range testCases {
+ tc := tc
+
+ t.Run(string(tc.In), func(t *testing.T) {
+ t.Parallel()
+
+ ex := tc.In
+
+ out, err := yaml.Marshal(ex)
+ switch {
+ case err != nil:
+ t.Fatalf("expect no yaml marshal error, got: %s", err)
+ case !json.Valid(tc.In):
+ t.Fatalf("json is invalid: %#q", tc.In)
+ case sr.Replace(tc.Exp) != sr.Replace(string(out)):
+ t.Fatalf("expected: %s, actual: %s", tc.Exp, out)
+ }
+
+ out, err = json.Marshal(tc.In)
+ switch {
+ case err != nil:
+ t.Fatalf("expect no json marshal error, got: %s", err)
+ case sr.Replace(string(tc.In)) != sr.Replace(string(out)):
+ t.Fatalf("expected: %s, actual: %s", tc.In, out)
+ }
+ })
+ }
+}
+
+func TestOpenapiSchemaObjectProperties(t *testing.T) {
+ t.Parallel()
+
+ v := map[string]interface{}{
+ "example": openapiSchemaObjectProperties{{
+ Key: "test1",
+ Value: 1,
+ }, {
+ Key: "test2",
+ Value: 2,
+ }},
+ }
+
+ t.Run("yaml", func(t *testing.T) {
+ t.Parallel()
+
+ const exp = `
+ example:
+ test1: 1
+ test2: 2
+ `
+
+ sr := newSpaceReplacer()
+
+ out, err := yaml.Marshal(v)
+ switch {
+ case err != nil:
+ t.Fatalf("expect no marshal error, got: %s", err)
+ case sr.Replace(exp) != sr.Replace(string(out)):
+ t.Fatalf("expected: %s, actual: %s", exp, out)
+ }
+ })
+
+ t.Run("json", func(t *testing.T) {
+ t.Parallel()
+
+ const exp = `{"example":{"test1":1,"test2":2}}`
+
+ got, err := json.Marshal(v)
+ switch {
+ case err != nil:
+ t.Fatalf("expect no marshal error, got: %s", err)
+ case exp != string(got):
+ t.Fatalf("expected: %s, actual: %s", exp, got)
+ }
+ })
+}
diff --git a/protoc-gen-openapiv3/main.go b/protoc-gen-openapiv3/main.go
new file mode 100644
index 00000000000..43f7593a477
--- /dev/null
+++ b/protoc-gen-openapiv3/main.go
@@ -0,0 +1,314 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "os"
+ "runtime/debug"
+ "strings"
+
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/codegenerator"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/internal/genopenapi"
+ "github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
+ "google.golang.org/grpc/grpclog"
+ "google.golang.org/protobuf/proto"
+ "google.golang.org/protobuf/types/pluginpb"
+)
+
+var (
+ importPrefix = flag.String("import_prefix", "", "prefix to be added to go package paths for imported proto files")
+ file = flag.String("file", "-", "where to load data from")
+ allowDeleteBody = flag.Bool("allow_delete_body", false, "unless set, HTTP DELETE methods may not have a body")
+ grpcAPIConfiguration = flag.String("grpc_api_configuration", "", "path to file which describes the gRPC API Configuration in YAML format")
+ allowMerge = flag.Bool("allow_merge", false, "if set, generation one OpenAPI file out of multiple protos")
+ mergeFileName = flag.String("merge_file_name", "apidocs", "target OpenAPI file name prefix after merge")
+ useJSONNamesForFields = flag.Bool("json_names_for_fields", true, "if disabled, the original proto name will be used for generating OpenAPI definitions")
+ repeatedPathParamSeparator = flag.String("repeated_path_param_separator", "csv", "configures how repeated fields should be split. Allowed values are `csv`, `pipes`, `ssv` and `tsv`")
+ versionFlag = flag.Bool("version", false, "print the current version")
+ _ = flag.Bool("allow_repeated_fields_in_body", true, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option. DEPRECATED: the value is ignored and always behaves as `true`.")
+ includePackageInTags = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. If set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
+ useFQNForOpenAPIName = flag.Bool("fqn_for_openapi_name", false, "if set, the object's OpenAPI names will use the fully qualified names from the proto definition (ie my.package.MyMessage.MyInnerMessage). DEPRECATED: prefer `openapi_naming_strategy=fqn`")
+ openAPINamingStrategy = flag.String("openapi_naming_strategy", "", "use the given OpenAPI naming strategy. Allowed values are `legacy`, `fqn`, `simple`, `package`. If unset, either `legacy` or `fqn` are selected, depending on the value of the `fqn_for_openapi_name` flag")
+ useGoTemplate = flag.Bool("use_go_templates", false, "if set, you can use Go templates in protofile comments")
+ goTemplateArgs = utilities.StringArrayFlag(flag.CommandLine, "go_template_args", "provide a custom value that can override a key in the Go template. Requires the `use_go_templates` option to be set")
+ ignoreComments = flag.Bool("ignore_comments", false, "if set, all protofile comments are excluded from output")
+ removeInternalComments = flag.Bool("remove_internal_comments", false, "if set, removes all substrings in comments that start with `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments")
+ disableDefaultErrors = flag.Bool("disable_default_errors", false, "if set, disables generation of default errors. This is useful if you have defined custom error handling")
+ enumsAsInts = flag.Bool("enums_as_ints", false, "whether to render enum values as integers, as opposed to string values")
+ simpleOperationIDs = flag.Bool("simple_operation_ids", false, "whether to remove the service prefix in the operationID generation. Can introduce duplicate operationIDs, use with caution.")
+ proto3OptionalNullable = flag.Bool("proto3_optional_nullable", false, "whether Proto3 Optional fields should be marked as x-nullable")
+ openAPIConfiguration = flag.String("openapi_configuration", "", "path to file which describes the OpenAPI Configuration in YAML format")
+ generateUnboundMethods = flag.Bool("generate_unbound_methods", false, "generate swagger metadata even for RPC methods that have no HttpRule annotation")
+ recursiveDepth = flag.Int("recursive-depth", 1000, "maximum recursion count allowed for a field type")
+ omitEnumDefaultValue = flag.Bool("omit_enum_default_value", false, "if set, omit default enum value")
+ outputFormat = flag.String("output_format", string(genopenapi.FormatJSON), fmt.Sprintf("output content format. Allowed values are: `%s`, `%s`", genopenapi.FormatJSON, genopenapi.FormatYAML))
+ visibilityRestrictionSelectors = utilities.StringArrayFlag(flag.CommandLine, "visibility_restriction_selectors", "list of `google.api.VisibilityRule` visibility labels to include in the generated output when a visibility annotation is defined. Repeat this option to supply multiple values. Elements without visibility annotations are unaffected by this setting.")
+ disableServiceTags = flag.Bool("disable_service_tags", false, "if set, disables generation of service tags. This is useful if you do not want to expose the names of your backend grpc services.")
+ disableDefaultResponses = flag.Bool("disable_default_responses", false, "if set, disables generation of default responses. Useful if you have to support custom response codes that are not 200.")
+ useAllOfForRefs = flag.Bool("use_allof_for_refs", false, "if set, will use allOf as container for $ref to preserve same-level properties.")
+ allowPatchFeature = flag.Bool("allow_patch_feature", true, "whether to hide update_mask fields in PATCH requests from the generated swagger file.")
+ preserveRPCOrder = flag.Bool("preserve_rpc_order", false, "if true, will ensure the order of paths emitted in openapi swagger files mirror the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically.")
+ enableRpcDeprecation = flag.Bool("enable_rpc_deprecation", false, "whether to process grpc method's deprecated option.")
+ expandSlashedPathPatterns = flag.Bool("expand_slashed_path_patterns", false, "if set, expands path parameters with URI sub-paths into the URI. For example, \"/v1/{name=projects/*}/resource\" becomes \"/v1/projects/{project}/resource\".")
+ useProto3FieldSemantics = flag.Bool("use_proto3_field_semantics", false, "if set, uses proto3 field semantics for the OpenAPI schema. This means that fields are required by default.")
+ generateXGoType = flag.Bool("generate_x_go_type", false, "if set, generates x-go-type extension using the go_package option from proto files")
+
+ _ = flag.Bool("logtostderr", false, "Legacy glog compatibility. This flag is a no-op, you can safely remove it")
+)
+
+// Variables set by goreleaser at build time
+var (
+ version = "dev"
+ commit = "unknown"
+ date = "unknown"
+)
+
+func main() {
+ flag.Parse()
+
+ if *versionFlag {
+ if commit == "unknown" {
+ buildInfo, ok := debug.ReadBuildInfo()
+ if ok {
+ version = buildInfo.Main.Version
+ for _, setting := range buildInfo.Settings {
+ if setting.Key == "vcs.revision" {
+ commit = setting.Value
+ }
+ if setting.Key == "vcs.time" {
+ date = setting.Value
+ }
+ }
+ }
+ }
+ fmt.Printf("Version %v, commit %v, built at %v\n", version, commit, date)
+ os.Exit(0)
+ }
+
+ reg := descriptor.NewRegistry()
+ if grpclog.V(1) {
+ grpclog.Info("Processing code generator request")
+ }
+ f := os.Stdin
+ if *file != "-" {
+ var err error
+ f, err = os.Open(*file)
+ if err != nil {
+ grpclog.Fatal(err)
+ }
+ }
+ if grpclog.V(1) {
+ grpclog.Info("Parsing code generator request")
+ }
+ req, err := codegenerator.ParseRequest(f)
+ if err != nil {
+ grpclog.Fatal(err)
+ }
+ if grpclog.V(1) {
+ grpclog.Info("Parsed code generator request")
+ }
+ pkgMap := make(map[string]string)
+ if req.Parameter != nil {
+ if err := parseReqParam(req.GetParameter(), flag.CommandLine, pkgMap); err != nil {
+ grpclog.Fatalf("Error parsing flags: %v", err)
+ }
+ }
+
+ reg.SetPrefix(*importPrefix)
+ reg.SetAllowDeleteBody(*allowDeleteBody)
+ reg.SetAllowMerge(*allowMerge)
+ reg.SetMergeFileName(*mergeFileName)
+ reg.SetUseJSONNamesForFields(*useJSONNamesForFields)
+ reg.SetUseProto3FieldSemantics(*useProto3FieldSemantics)
+
+ flag.Visit(func(f *flag.Flag) {
+ if f.Name == "allow_repeated_fields_in_body" {
+ grpclog.Warning("The `allow_repeated_fields_in_body` flag is deprecated and will always behave as `true`.")
+ }
+ })
+
+ reg.SetIncludePackageInTags(*includePackageInTags)
+
+ reg.SetUseFQNForOpenAPIName(*useFQNForOpenAPIName)
+ // Set the naming strategy either directly from the flag, or via the value of the legacy fqn_for_openapi_name
+ // flag.
+ namingStrategy := *openAPINamingStrategy
+ if *useFQNForOpenAPIName {
+ if namingStrategy != "" {
+ grpclog.Fatal("The deprecated `fqn_for_openapi_name` flag must remain unset if `openapi_naming_strategy` is set.")
+ }
+ grpclog.Warning("The `fqn_for_openapi_name` flag is deprecated. Please use `openapi_naming_strategy=fqn` instead.")
+ namingStrategy = "fqn"
+ } else if namingStrategy == "" {
+ namingStrategy = "legacy"
+ }
+ if strategyFn := genopenapi.LookupNamingStrategy(namingStrategy); strategyFn == nil {
+ emitError(fmt.Errorf("invalid naming strategy %q", namingStrategy))
+ return
+ }
+
+ if *useGoTemplate && *ignoreComments {
+ emitError(fmt.Errorf("`ignore_comments` and `use_go_templates` are mutually exclusive and cannot be enabled at the same time"))
+ return
+ }
+ reg.SetUseGoTemplate(*useGoTemplate)
+ reg.SetIgnoreComments(*ignoreComments)
+ reg.SetRemoveInternalComments(*removeInternalComments)
+
+ if len(*goTemplateArgs) > 0 && !*useGoTemplate {
+ emitError(fmt.Errorf("`go_template_args` requires `use_go_templates` to be enabled"))
+ return
+ }
+ reg.SetGoTemplateArgs(*goTemplateArgs)
+
+ reg.SetOpenAPINamingStrategy(namingStrategy)
+ reg.SetEnumsAsInts(*enumsAsInts)
+ reg.SetDisableDefaultErrors(*disableDefaultErrors)
+ reg.SetSimpleOperationIDs(*simpleOperationIDs)
+ reg.SetProto3OptionalNullable(*proto3OptionalNullable)
+ reg.SetGenerateUnboundMethods(*generateUnboundMethods)
+ reg.SetRecursiveDepth(*recursiveDepth)
+ reg.SetOmitEnumDefaultValue(*omitEnumDefaultValue)
+ reg.SetVisibilityRestrictionSelectors(*visibilityRestrictionSelectors)
+ reg.SetDisableServiceTags(*disableServiceTags)
+ reg.SetDisableDefaultResponses(*disableDefaultResponses)
+ reg.SetUseAllOfForRefs(*useAllOfForRefs)
+ reg.SetAllowPatchFeature(*allowPatchFeature)
+ reg.SetPreserveRPCOrder(*preserveRPCOrder)
+ reg.SetEnableRpcDeprecation(*enableRpcDeprecation)
+ reg.SetExpandSlashedPathPatterns(*expandSlashedPathPatterns)
+ reg.SetGenerateXGoType(*generateXGoType)
+
+ if err := reg.SetRepeatedPathParamSeparator(*repeatedPathParamSeparator); err != nil {
+ emitError(err)
+ return
+ }
+ for k, v := range pkgMap {
+ reg.AddPkgMap(k, v)
+ }
+
+ if *grpcAPIConfiguration != "" {
+ if err := reg.LoadGrpcAPIServiceFromYAML(*grpcAPIConfiguration); err != nil {
+ emitError(err)
+ return
+ }
+ }
+
+ format := genopenapi.Format(*outputFormat)
+ if err := format.Validate(); err != nil {
+ emitError(err)
+ return
+ }
+
+ g := genopenapi.New(reg, format)
+
+ if err := genopenapi.AddErrorDefs(reg); err != nil {
+ emitError(err)
+ return
+ }
+
+ if err := reg.Load(req); err != nil {
+ emitError(err)
+ return
+ }
+
+ if *openAPIConfiguration != "" {
+ if err := reg.LoadOpenAPIConfigFromYAML(*openAPIConfiguration); err != nil {
+ emitError(err)
+ return
+ }
+ }
+
+ targets := make([]*descriptor.File, 0, len(req.FileToGenerate))
+ for _, target := range req.FileToGenerate {
+ f, err := reg.LookupFile(target)
+ if err != nil {
+ grpclog.Fatal(err)
+ }
+ targets = append(targets, f)
+ }
+
+ out, err := g.Generate(targets)
+ if grpclog.V(1) {
+ grpclog.Info("Processed code generator request")
+ }
+ if err != nil {
+ emitError(err)
+ return
+ }
+ emitFiles(out)
+}
+
+func emitFiles(out []*descriptor.ResponseFile) {
+ files := make([]*pluginpb.CodeGeneratorResponse_File, len(out))
+ for idx, item := range out {
+ files[idx] = item.CodeGeneratorResponse_File
+ }
+ resp := &pluginpb.CodeGeneratorResponse{File: files}
+ codegenerator.SetSupportedFeaturesOnCodeGeneratorResponse(resp)
+ emitResp(resp)
+}
+
+func emitError(err error) {
+ emitResp(&pluginpb.CodeGeneratorResponse{Error: proto.String(err.Error())})
+}
+
+func emitResp(resp *pluginpb.CodeGeneratorResponse) {
+ buf, err := proto.Marshal(resp)
+ if err != nil {
+ grpclog.Fatal(err)
+ }
+ if _, err := os.Stdout.Write(buf); err != nil {
+ grpclog.Fatal(err)
+ }
+}
+
+// parseReqParam parses a CodeGeneratorRequest parameter and adds the
+// extracted values to the given FlagSet and pkgMap. Returns a non-nil
+// error if setting a flag failed.
+func parseReqParam(param string, f *flag.FlagSet, pkgMap map[string]string) error {
+ if param == "" {
+ return nil
+ }
+ for _, p := range strings.Split(param, ",") {
+ spec := strings.SplitN(p, "=", 2)
+ if len(spec) == 1 {
+ switch spec[0] {
+ case "allow_delete_body":
+ if err := f.Set(spec[0], "true"); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ continue
+ case "allow_merge":
+ if err := f.Set(spec[0], "true"); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ continue
+ case "allow_repeated_fields_in_body":
+ if err := f.Set(spec[0], "true"); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ continue
+ case "include_package_in_tags":
+ if err := f.Set(spec[0], "true"); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ continue
+ }
+ if err := f.Set(spec[0], ""); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ continue
+ }
+ name, value := spec[0], spec[1]
+ if strings.HasPrefix(name, "M") {
+ pkgMap[name[1:]] = value
+ continue
+ }
+ if err := f.Set(name, value); err != nil {
+ return fmt.Errorf("cannot set flag %s: %w", p, err)
+ }
+ }
+ return nil
+}
diff --git a/protoc-gen-openapiv3/main_test.go b/protoc-gen-openapiv3/main_test.go
new file mode 100644
index 00000000000..a6f95c628ea
--- /dev/null
+++ b/protoc-gen-openapiv3/main_test.go
@@ -0,0 +1,260 @@
+package main
+
+import (
+ "errors"
+ "flag"
+ "reflect"
+ "testing"
+)
+
+func TestParseReqParam(t *testing.T) {
+
+ testcases := []struct {
+ name string
+ expected map[string]string
+ request string
+ expectedError error
+ allowDeleteBodyV bool
+ allowMergeV bool
+ includePackageInTagsV bool
+ fileV string
+ importPathV string
+ mergeFileNameV string
+ useFQNForOpenAPINameV bool
+ openAPINamingStrategyV string
+ }{
+ {
+ // this one must be first - with no leading clearFlags call it
+ // verifies our expectation of default values as we reset by
+ // clearFlags
+ name: "Test 0",
+ expected: map[string]string{},
+ request: "",
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "-",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 1",
+ expected: map[string]string{"google/api/annotations.proto": "github.com/googleapis/googleapis/google/api"},
+ request: "allow_delete_body,allow_merge,allow_repeated_fields_in_body,include_package_in_tags,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/googleapis/googleapis/google/api",
+ allowDeleteBodyV: true,
+ allowMergeV: true,
+ includePackageInTagsV: true,
+ fileV: "./foo.pb",
+ importPathV: "/bar/baz",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 2",
+ expected: map[string]string{"google/api/annotations.proto": "github.com/googleapis/googleapis/google/api"},
+ request: "allow_delete_body=true,allow_merge=true,allow_repeated_fields_in_body=true,include_package_in_tags=true,merge_file_name=test_name,file=./foo.pb,import_prefix=/bar/baz,Mgoogle/api/annotations.proto=github.com/googleapis/googleapis/google/api",
+ allowDeleteBodyV: true,
+ allowMergeV: true,
+ includePackageInTagsV: true,
+ fileV: "./foo.pb",
+ importPathV: "/bar/baz",
+ mergeFileNameV: "test_name",
+ },
+ {
+ name: "Test 3",
+ expected: map[string]string{"a/b/c.proto": "github.com/x/y/z", "f/g/h.proto": "github.com/1/2/3/"},
+ request: "allow_delete_body=false,allow_merge=false,Ma/b/c.proto=github.com/x/y/z,Mf/g/h.proto=github.com/1/2/3/",
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 4",
+ expected: map[string]string{},
+ request: "",
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 5",
+ expected: map[string]string{},
+ request: "unknown_param=17",
+ expectedError: errors.New("cannot set flag unknown_param=17: no such flag -unknown_param"),
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 6",
+ expected: map[string]string{},
+ request: "Mfoo",
+ expectedError: errors.New("cannot set flag Mfoo: no such flag -Mfoo"),
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 7",
+ expected: map[string]string{},
+ request: "allow_delete_body,file,import_prefix,allow_merge,allow_repeated_fields_in_body,include_package_in_tags,merge_file_name",
+ allowDeleteBodyV: true,
+ allowMergeV: true,
+ includePackageInTagsV: true,
+ fileV: "",
+ importPathV: "",
+ mergeFileNameV: "",
+ },
+ {
+ name: "Test 8",
+ expected: map[string]string{},
+ request: "allow_delete_body,file,import_prefix,allow_merge,allow_repeated_fields_in_body=3,merge_file_name",
+ expectedError: errors.New(`cannot set flag allow_repeated_fields_in_body=3: parse error`),
+ allowDeleteBodyV: true,
+ allowMergeV: true,
+ includePackageInTagsV: false,
+ fileV: "",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 9",
+ expected: map[string]string{},
+ request: "include_package_in_tags=3",
+ expectedError: errors.New(`cannot set flag include_package_in_tags=3: parse error`),
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 10",
+ expected: map[string]string{},
+ request: "fqn_for_openapi_name=3",
+ expectedError: errors.New(`cannot set flag fqn_for_openapi_name=3: parse error`),
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ useFQNForOpenAPINameV: false,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 11",
+ expected: map[string]string{},
+ request: "fqn_for_openapi_name=true",
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ useFQNForOpenAPINameV: true,
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ {
+ name: "Test 12",
+ expected: map[string]string{},
+ request: "openapi_naming_strategy=simple",
+ allowDeleteBodyV: false,
+ allowMergeV: false,
+ includePackageInTagsV: false,
+ useFQNForOpenAPINameV: false,
+ openAPINamingStrategyV: "simple",
+ fileV: "stdin",
+ importPathV: "",
+ mergeFileNameV: "apidocs",
+ },
+ }
+
+ for i, tc := range testcases {
+ t.Run(tc.name, func(tt *testing.T) {
+ f := flag.CommandLine
+ pkgMap := make(map[string]string)
+ err := parseReqParam(tc.request, f, pkgMap)
+ if tc.expectedError == nil {
+ if err != nil {
+ tt.Errorf("unexpected parse error '%v'", err)
+ }
+ if !reflect.DeepEqual(pkgMap, tc.expected) {
+ tt.Errorf("pkgMap parse error, expected '%v', got '%v'", tc.expected, pkgMap)
+ }
+ } else {
+ if err == nil {
+ tt.Error("expected parse error not returned")
+ }
+ if !reflect.DeepEqual(pkgMap, tc.expected) {
+ tt.Errorf("pkgMap parse error, expected '%v', got '%v'", tc.expected, pkgMap)
+ }
+ if err.Error() != tc.expectedError.Error() {
+ tt.Errorf("expected error malformed, expected %q, got %q", tc.expectedError.Error(), err.Error())
+ }
+ }
+ checkFlags(tc.allowDeleteBodyV, tc.allowMergeV, tc.includePackageInTagsV, tc.useFQNForOpenAPINameV, tc.openAPINamingStrategyV, tc.fileV, tc.importPathV, tc.mergeFileNameV, tt, i)
+
+ clearFlags()
+ })
+ }
+}
+
+func checkFlags(
+ allowDeleteV,
+ allowMergeV,
+ includePackageInTagsV bool,
+ useFQNForOpenAPINameV bool,
+ openAPINamingStrategyV,
+ fileV,
+ importPathV,
+ mergeFileNameV string,
+ t *testing.T,
+ tid int,
+) {
+ if *importPrefix != importPathV {
+ t.Errorf("Test %v: import_prefix misparsed, expected '%v', got '%v'", tid, importPathV, *importPrefix)
+ }
+ if *file != fileV {
+ t.Errorf("Test %v: file misparsed, expected '%v', got '%v'", tid, fileV, *file)
+ }
+ if *allowDeleteBody != allowDeleteV {
+ t.Errorf("Test %v: allow_delete_body misparsed, expected '%v', got '%v'", tid, allowDeleteV, *allowDeleteBody)
+ }
+ if *allowMerge != allowMergeV {
+ t.Errorf("Test %v: allow_merge misparsed, expected '%v', got '%v'", tid, allowMergeV, *allowMerge)
+ }
+ if *mergeFileName != mergeFileNameV {
+ t.Errorf("Test %v: merge_file_name misparsed, expected '%v', got '%v'", tid, mergeFileNameV, *mergeFileName)
+ }
+ if *includePackageInTags != includePackageInTagsV {
+ t.Errorf("Test %v: include_package_in_tags misparsed, expected '%v', got '%v'", tid, includePackageInTagsV, *includePackageInTags)
+ }
+ if *useFQNForOpenAPIName != useFQNForOpenAPINameV {
+ t.Errorf("Test %v: fqn_for_openapi_name misparsed, expected '%v', got '%v'", tid, useFQNForOpenAPINameV, *useFQNForOpenAPIName)
+ }
+ if *openAPINamingStrategy != openAPINamingStrategyV {
+ t.Errorf("Test %v: openapi_naming_strategy misparsed, expected '%v', got '%v'", tid, openAPINamingStrategyV, *openAPINamingStrategy)
+ }
+}
+
+func clearFlags() {
+ *importPrefix = ""
+ *file = "stdin"
+ *allowDeleteBody = false
+ *allowMerge = false
+ *includePackageInTags = false
+ *mergeFileName = "apidocs"
+ *useFQNForOpenAPIName = false
+ *openAPINamingStrategy = ""
+}
diff --git a/protoc-gen-openapiv3/options/BUILD.bazel b/protoc-gen-openapiv3/options/BUILD.bazel
new file mode 100644
index 00000000000..a9b18bb531a
--- /dev/null
+++ b/protoc-gen-openapiv3/options/BUILD.bazel
@@ -0,0 +1,44 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+load("@rules_proto//proto:defs.bzl", "proto_library")
+
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "options_proto_files",
+ srcs = [
+ "annotations.proto",
+ "openapiv3.proto",
+ ],
+)
+
+go_library(
+ name = "options",
+ embed = [":options_go_proto"],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options",
+)
+
+proto_library(
+ name = "options_proto",
+ srcs = [
+ "annotations.proto",
+ "openapiv3.proto",
+ ],
+ deps = [
+ "@com_google_protobuf//:descriptor_proto",
+ "@com_google_protobuf//:struct_proto",
+ ],
+)
+
+go_proto_library(
+ name = "options_go_proto",
+ compilers = ["//:go_apiv2"],
+ importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options",
+ proto = ":options_proto",
+)
+
+alias(
+ name = "go_default_library",
+ actual = ":options",
+ visibility = ["//visibility:public"],
+)
diff --git a/protoc-gen-openapiv3/options/annotations.pb.go b/protoc-gen-openapiv3/options/annotations.pb.go
new file mode 100644
index 00000000000..fca788288dd
--- /dev/null
+++ b/protoc-gen-openapiv3/options/annotations.pb.go
@@ -0,0 +1,269 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.0
+// protoc (unknown)
+// source: protoc-gen-openapiv3/options/annotations.proto
+
+//go:build !protoopaque
+
+package options
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+ reflect "reflect"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+var file_protoc_gen_openapiv3_options_annotations_proto_extTypes = []protoimpl.ExtensionInfo{
+ {
+ ExtendedType: (*descriptorpb.FileOptions)(nil),
+ ExtensionType: (*Swagger)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger",
+ Tag: "bytes,1043,opt,name=openapiv2_swagger",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.MethodOptions)(nil),
+ ExtensionType: (*Operation)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation",
+ Tag: "bytes,1043,opt,name=openapiv2_operation",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.MessageOptions)(nil),
+ ExtensionType: (*Schema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema",
+ Tag: "bytes,1043,opt,name=openapiv2_schema",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.EnumOptions)(nil),
+ ExtensionType: (*EnumSchema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum",
+ Tag: "bytes,1043,opt,name=openapiv2_enum",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.ServiceOptions)(nil),
+ ExtensionType: (*Tag)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag",
+ Tag: "bytes,1043,opt,name=openapiv2_tag",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*JSONSchema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field",
+ Tag: "bytes,1043,opt,name=openapiv2_field",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+}
+
+// Extension fields to descriptorpb.FileOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Swagger openapiv2_swagger = 1043;
+ E_Openapiv2Swagger = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[0]
+)
+
+// Extension fields to descriptorpb.MethodOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Operation openapiv2_operation = 1043;
+ E_Openapiv2Operation = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[1]
+)
+
+// Extension fields to descriptorpb.MessageOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Schema openapiv2_schema = 1043;
+ E_Openapiv2Schema = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[2]
+)
+
+// Extension fields to descriptorpb.EnumOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.EnumSchema openapiv2_enum = 1043;
+ E_Openapiv2Enum = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[3]
+)
+
+// Extension fields to descriptorpb.ServiceOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Tag openapiv2_tag = 1043;
+ E_Openapiv2Tag = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[4]
+)
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.JSONSchema openapiv2_field = 1043;
+ E_Openapiv2Field = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[5]
+)
+
+var File_protoc_gen_openapiv3_options_annotations_proto protoreflect.FileDescriptor
+
+var file_protoc_gen_openapiv3_options_annotations_proto_rawDesc = []byte{
+ 0x0a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x12, 0x29, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x7e, 0x0a, 0x11, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72,
+ 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93,
+ 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x52, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x32, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x3a, 0x86, 0x01, 0x0a, 0x13,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x4f, 0x70, 0x65, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x7e, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x32, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x53, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x3a, 0x7b, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x32, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x45, 0x6e, 0x75,
+ 0x6d, 0x3a, 0x75, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x74,
+ 0x61, 0x67, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x32, 0x54, 0x61, 0x67, 0x3a, 0x7e, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69,
+ 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53,
+ 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x32, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68,
+ 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x65, 0x63, 0x6f, 0x73,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e,
+ 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_protoc_gen_openapiv3_options_annotations_proto_goTypes = []any{
+ (*descriptorpb.FileOptions)(nil), // 0: google.protobuf.FileOptions
+ (*descriptorpb.MethodOptions)(nil), // 1: google.protobuf.MethodOptions
+ (*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions
+ (*descriptorpb.EnumOptions)(nil), // 3: google.protobuf.EnumOptions
+ (*descriptorpb.ServiceOptions)(nil), // 4: google.protobuf.ServiceOptions
+ (*descriptorpb.FieldOptions)(nil), // 5: google.protobuf.FieldOptions
+ (*Swagger)(nil), // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ (*Operation)(nil), // 7: grpc.gateway.protoc_gen_openapiv3.options.Operation
+ (*Schema)(nil), // 8: grpc.gateway.protoc_gen_openapiv3.options.Schema
+ (*EnumSchema)(nil), // 9: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ (*Tag)(nil), // 10: grpc.gateway.protoc_gen_openapiv3.options.Tag
+ (*JSONSchema)(nil), // 11: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+}
+var file_protoc_gen_openapiv3_options_annotations_proto_depIdxs = []int32{
+ 0, // 0: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger:extendee -> google.protobuf.FileOptions
+ 1, // 1: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation:extendee -> google.protobuf.MethodOptions
+ 2, // 2: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema:extendee -> google.protobuf.MessageOptions
+ 3, // 3: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum:extendee -> google.protobuf.EnumOptions
+ 4, // 4: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag:extendee -> google.protobuf.ServiceOptions
+ 5, // 5: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field:extendee -> google.protobuf.FieldOptions
+ 6, // 6: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ 7, // 7: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation
+ 8, // 8: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Schema
+ 9, // 9: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum:type_name -> grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ 10, // 10: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag
+ 11, // 11: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ 12, // [12:12] is the sub-list for method output_type
+ 12, // [12:12] is the sub-list for method input_type
+ 6, // [6:12] is the sub-list for extension type_name
+ 0, // [0:6] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_protoc_gen_openapiv3_options_annotations_proto_init() }
+func file_protoc_gen_openapiv3_options_annotations_proto_init() {
+ if File_protoc_gen_openapiv3_options_annotations_proto != nil {
+ return
+ }
+ file_protoc_gen_openapiv3_options_openapiv3_proto_init()
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_protoc_gen_openapiv3_options_annotations_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 0,
+ NumExtensions: 6,
+ NumServices: 0,
+ },
+ GoTypes: file_protoc_gen_openapiv3_options_annotations_proto_goTypes,
+ DependencyIndexes: file_protoc_gen_openapiv3_options_annotations_proto_depIdxs,
+ ExtensionInfos: file_protoc_gen_openapiv3_options_annotations_proto_extTypes,
+ }.Build()
+ File_protoc_gen_openapiv3_options_annotations_proto = out.File
+ file_protoc_gen_openapiv3_options_annotations_proto_rawDesc = nil
+ file_protoc_gen_openapiv3_options_annotations_proto_goTypes = nil
+ file_protoc_gen_openapiv3_options_annotations_proto_depIdxs = nil
+}
diff --git a/protoc-gen-openapiv3/options/annotations.proto b/protoc-gen-openapiv3/options/annotations.proto
new file mode 100644
index 00000000000..c67ca482759
--- /dev/null
+++ b/protoc-gen-openapiv3/options/annotations.proto
@@ -0,0 +1,51 @@
+syntax = "proto3";
+
+package grpc.gateway.protoc_gen_openapiv3.options;
+
+import "google/protobuf/descriptor.proto";
+import "protoc-gen-openapiv3/options/openapiv3.proto";
+
+option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options";
+
+extend google.protobuf.FileOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ Swagger openapiv2_swagger = 1043;
+}
+extend google.protobuf.MethodOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ Operation openapiv2_operation = 1043;
+}
+extend google.protobuf.MessageOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ Schema openapiv2_schema = 1043;
+}
+extend google.protobuf.EnumOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ EnumSchema openapiv2_enum = 1043;
+}
+extend google.protobuf.ServiceOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ Tag openapiv2_tag = 1043;
+}
+extend google.protobuf.FieldOptions {
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ JSONSchema openapiv2_field = 1043;
+}
diff --git a/protoc-gen-openapiv3/options/annotations.swagger.json b/protoc-gen-openapiv3/options/annotations.swagger.json
new file mode 100644
index 00000000000..0b787d88730
--- /dev/null
+++ b/protoc-gen-openapiv3/options/annotations.swagger.json
@@ -0,0 +1,44 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "protoc-gen-openapiv3/options/annotations.proto",
+ "version": "version not set"
+ },
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {},
+ "definitions": {
+ "protobufAny": {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": {}
+ },
+ "rpcStatus": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/protobufAny"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/protoc-gen-openapiv3/options/annotations_protoopaque.pb.go b/protoc-gen-openapiv3/options/annotations_protoopaque.pb.go
new file mode 100644
index 00000000000..3b87ed04bde
--- /dev/null
+++ b/protoc-gen-openapiv3/options/annotations_protoopaque.pb.go
@@ -0,0 +1,269 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.0
+// protoc (unknown)
+// source: protoc-gen-openapiv3/options/annotations.proto
+
+//go:build protoopaque
+
+package options
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ descriptorpb "google.golang.org/protobuf/types/descriptorpb"
+ reflect "reflect"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+var file_protoc_gen_openapiv3_options_annotations_proto_extTypes = []protoimpl.ExtensionInfo{
+ {
+ ExtendedType: (*descriptorpb.FileOptions)(nil),
+ ExtensionType: (*Swagger)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger",
+ Tag: "bytes,1043,opt,name=openapiv2_swagger",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.MethodOptions)(nil),
+ ExtensionType: (*Operation)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation",
+ Tag: "bytes,1043,opt,name=openapiv2_operation",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.MessageOptions)(nil),
+ ExtensionType: (*Schema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema",
+ Tag: "bytes,1043,opt,name=openapiv2_schema",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.EnumOptions)(nil),
+ ExtensionType: (*EnumSchema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum",
+ Tag: "bytes,1043,opt,name=openapiv2_enum",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.ServiceOptions)(nil),
+ ExtensionType: (*Tag)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag",
+ Tag: "bytes,1043,opt,name=openapiv2_tag",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+ {
+ ExtendedType: (*descriptorpb.FieldOptions)(nil),
+ ExtensionType: (*JSONSchema)(nil),
+ Field: 1043,
+ Name: "grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field",
+ Tag: "bytes,1043,opt,name=openapiv2_field",
+ Filename: "protoc-gen-openapiv3/options/annotations.proto",
+ },
+}
+
+// Extension fields to descriptorpb.FileOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Swagger openapiv2_swagger = 1043;
+ E_Openapiv2Swagger = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[0]
+)
+
+// Extension fields to descriptorpb.MethodOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Operation openapiv2_operation = 1043;
+ E_Openapiv2Operation = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[1]
+)
+
+// Extension fields to descriptorpb.MessageOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Schema openapiv2_schema = 1043;
+ E_Openapiv2Schema = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[2]
+)
+
+// Extension fields to descriptorpb.EnumOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.EnumSchema openapiv2_enum = 1043;
+ E_Openapiv2Enum = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[3]
+)
+
+// Extension fields to descriptorpb.ServiceOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.Tag openapiv2_tag = 1043;
+ E_Openapiv2Tag = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[4]
+)
+
+// Extension fields to descriptorpb.FieldOptions.
+var (
+ // ID assigned by protobuf-global-extension-registry@google.com for gRPC-Gateway project.
+ //
+ // All IDs are the same, as assigned. It is okay that they are the same, as they extend
+ // different descriptor messages.
+ //
+ // optional grpc.gateway.protoc_gen_openapiv3.options.JSONSchema openapiv2_field = 1043;
+ E_Openapiv2Field = &file_protoc_gen_openapiv3_options_annotations_proto_extTypes[5]
+)
+
+var File_protoc_gen_openapiv3_options_annotations_proto protoreflect.FileDescriptor
+
+var file_protoc_gen_openapiv3_options_annotations_proto_rawDesc = []byte{
+ 0x0a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x61,
+ 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x12, 0x29, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x20, 0x67, 0x6f, 0x6f,
+ 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2c, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3a, 0x7e, 0x0a, 0x11, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72,
+ 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+ 0x75, 0x66, 0x2e, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93,
+ 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x52, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x32, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x3a, 0x86, 0x01, 0x0a, 0x13,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x52, 0x12, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x4f, 0x70, 0x65, 0x72, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x7e, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x32, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b,
+ 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x68,
+ 0x65, 0x6d, 0x61, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x53, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x3a, 0x7b, 0x0a, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x32, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x52, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x45, 0x6e, 0x75,
+ 0x6d, 0x3a, 0x75, 0x0a, 0x0d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x74,
+ 0x61, 0x67, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+ 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x0c, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x32, 0x54, 0x61, 0x67, 0x3a, 0x7e, 0x0a, 0x0f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x32, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x1d, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69,
+ 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x93, 0x08, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53,
+ 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x32, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68,
+ 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x65, 0x63, 0x6f, 0x73,
+ 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e,
+ 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var file_protoc_gen_openapiv3_options_annotations_proto_goTypes = []any{
+ (*descriptorpb.FileOptions)(nil), // 0: google.protobuf.FileOptions
+ (*descriptorpb.MethodOptions)(nil), // 1: google.protobuf.MethodOptions
+ (*descriptorpb.MessageOptions)(nil), // 2: google.protobuf.MessageOptions
+ (*descriptorpb.EnumOptions)(nil), // 3: google.protobuf.EnumOptions
+ (*descriptorpb.ServiceOptions)(nil), // 4: google.protobuf.ServiceOptions
+ (*descriptorpb.FieldOptions)(nil), // 5: google.protobuf.FieldOptions
+ (*Swagger)(nil), // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ (*Operation)(nil), // 7: grpc.gateway.protoc_gen_openapiv3.options.Operation
+ (*Schema)(nil), // 8: grpc.gateway.protoc_gen_openapiv3.options.Schema
+ (*EnumSchema)(nil), // 9: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ (*Tag)(nil), // 10: grpc.gateway.protoc_gen_openapiv3.options.Tag
+ (*JSONSchema)(nil), // 11: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+}
+var file_protoc_gen_openapiv3_options_annotations_proto_depIdxs = []int32{
+ 0, // 0: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger:extendee -> google.protobuf.FileOptions
+ 1, // 1: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation:extendee -> google.protobuf.MethodOptions
+ 2, // 2: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema:extendee -> google.protobuf.MessageOptions
+ 3, // 3: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum:extendee -> google.protobuf.EnumOptions
+ 4, // 4: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag:extendee -> google.protobuf.ServiceOptions
+ 5, // 5: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field:extendee -> google.protobuf.FieldOptions
+ 6, // 6: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_swagger:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ 7, // 7: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_operation:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation
+ 8, // 8: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Schema
+ 9, // 9: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_enum:type_name -> grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ 10, // 10: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_tag:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag
+ 11, // 11: grpc.gateway.protoc_gen_openapiv3.options.openapiv2_field:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ 12, // [12:12] is the sub-list for method output_type
+ 12, // [12:12] is the sub-list for method input_type
+ 6, // [6:12] is the sub-list for extension type_name
+ 0, // [0:6] is the sub-list for extension extendee
+ 0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_protoc_gen_openapiv3_options_annotations_proto_init() }
+func file_protoc_gen_openapiv3_options_annotations_proto_init() {
+ if File_protoc_gen_openapiv3_options_annotations_proto != nil {
+ return
+ }
+ file_protoc_gen_openapiv3_options_openapiv3_proto_init()
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_protoc_gen_openapiv3_options_annotations_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 0,
+ NumExtensions: 6,
+ NumServices: 0,
+ },
+ GoTypes: file_protoc_gen_openapiv3_options_annotations_proto_goTypes,
+ DependencyIndexes: file_protoc_gen_openapiv3_options_annotations_proto_depIdxs,
+ ExtensionInfos: file_protoc_gen_openapiv3_options_annotations_proto_extTypes,
+ }.Build()
+ File_protoc_gen_openapiv3_options_annotations_proto = out.File
+ file_protoc_gen_openapiv3_options_annotations_proto_rawDesc = nil
+ file_protoc_gen_openapiv3_options_annotations_proto_goTypes = nil
+ file_protoc_gen_openapiv3_options_annotations_proto_depIdxs = nil
+}
diff --git a/protoc-gen-openapiv3/options/buf.gen.yaml b/protoc-gen-openapiv3/options/buf.gen.yaml
new file mode 100644
index 00000000000..07dfb958f1e
--- /dev/null
+++ b/protoc-gen-openapiv3/options/buf.gen.yaml
@@ -0,0 +1,7 @@
+version: v2
+plugins:
+ - remote: buf.build/protocolbuffers/go:v1.36.0
+ out: .
+ opt:
+ - paths=source_relative
+ - default_api_level=API_HYBRID
diff --git a/protoc-gen-openapiv3/options/openapiv3.pb.go b/protoc-gen-openapiv3/options/openapiv3.pb.go
new file mode 100644
index 00000000000..b5f8bd86ce2
--- /dev/null
+++ b/protoc-gen-openapiv3/options/openapiv3.pb.go
@@ -0,0 +1,4263 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.0
+// protoc (unknown)
+// source: protoc-gen-openapiv3/options/openapiv3.proto
+
+//go:build !protoopaque
+
+package options
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ structpb "google.golang.org/protobuf/types/known/structpb"
+ reflect "reflect"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Scheme describes the schemes supported by the OpenAPI Swagger
+// and Operation objects.
+type Scheme int32
+
+const (
+ Scheme_UNKNOWN Scheme = 0
+ Scheme_HTTP Scheme = 1
+ Scheme_HTTPS Scheme = 2
+ Scheme_WS Scheme = 3
+ Scheme_WSS Scheme = 4
+)
+
+// Enum value maps for Scheme.
+var (
+ Scheme_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "HTTP",
+ 2: "HTTPS",
+ 3: "WS",
+ 4: "WSS",
+ }
+ Scheme_value = map[string]int32{
+ "UNKNOWN": 0,
+ "HTTP": 1,
+ "HTTPS": 2,
+ "WS": 3,
+ "WSS": 4,
+ }
+)
+
+func (x Scheme) Enum() *Scheme {
+ p := new(Scheme)
+ *p = x
+ return p
+}
+
+func (x Scheme) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Scheme) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[0].Descriptor()
+}
+
+func (Scheme) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[0]
+}
+
+func (x Scheme) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// `Type` is a supported HTTP header type.
+// See https://swagger.io/specification/v2/#parameterType.
+type HeaderParameter_Type int32
+
+const (
+ HeaderParameter_UNKNOWN HeaderParameter_Type = 0
+ HeaderParameter_STRING HeaderParameter_Type = 1
+ HeaderParameter_NUMBER HeaderParameter_Type = 2
+ HeaderParameter_INTEGER HeaderParameter_Type = 3
+ HeaderParameter_BOOLEAN HeaderParameter_Type = 4
+)
+
+// Enum value maps for HeaderParameter_Type.
+var (
+ HeaderParameter_Type_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "STRING",
+ 2: "NUMBER",
+ 3: "INTEGER",
+ 4: "BOOLEAN",
+ }
+ HeaderParameter_Type_value = map[string]int32{
+ "UNKNOWN": 0,
+ "STRING": 1,
+ "NUMBER": 2,
+ "INTEGER": 3,
+ "BOOLEAN": 4,
+ }
+)
+
+func (x HeaderParameter_Type) Enum() *HeaderParameter_Type {
+ p := new(HeaderParameter_Type)
+ *p = x
+ return p
+}
+
+func (x HeaderParameter_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (HeaderParameter_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[1].Descriptor()
+}
+
+func (HeaderParameter_Type) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[1]
+}
+
+func (x HeaderParameter_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+type JSONSchema_JSONSchemaSimpleTypes int32
+
+const (
+ JSONSchema_UNKNOWN JSONSchema_JSONSchemaSimpleTypes = 0
+ JSONSchema_ARRAY JSONSchema_JSONSchemaSimpleTypes = 1
+ JSONSchema_BOOLEAN JSONSchema_JSONSchemaSimpleTypes = 2
+ JSONSchema_INTEGER JSONSchema_JSONSchemaSimpleTypes = 3
+ JSONSchema_NULL JSONSchema_JSONSchemaSimpleTypes = 4
+ JSONSchema_NUMBER JSONSchema_JSONSchemaSimpleTypes = 5
+ JSONSchema_OBJECT JSONSchema_JSONSchemaSimpleTypes = 6
+ JSONSchema_STRING JSONSchema_JSONSchemaSimpleTypes = 7
+)
+
+// Enum value maps for JSONSchema_JSONSchemaSimpleTypes.
+var (
+ JSONSchema_JSONSchemaSimpleTypes_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "ARRAY",
+ 2: "BOOLEAN",
+ 3: "INTEGER",
+ 4: "NULL",
+ 5: "NUMBER",
+ 6: "OBJECT",
+ 7: "STRING",
+ }
+ JSONSchema_JSONSchemaSimpleTypes_value = map[string]int32{
+ "UNKNOWN": 0,
+ "ARRAY": 1,
+ "BOOLEAN": 2,
+ "INTEGER": 3,
+ "NULL": 4,
+ "NUMBER": 5,
+ "OBJECT": 6,
+ "STRING": 7,
+ }
+)
+
+func (x JSONSchema_JSONSchemaSimpleTypes) Enum() *JSONSchema_JSONSchemaSimpleTypes {
+ p := new(JSONSchema_JSONSchemaSimpleTypes)
+ *p = x
+ return p
+}
+
+func (x JSONSchema_JSONSchemaSimpleTypes) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (JSONSchema_JSONSchemaSimpleTypes) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[2].Descriptor()
+}
+
+func (JSONSchema_JSONSchemaSimpleTypes) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[2]
+}
+
+func (x JSONSchema_JSONSchemaSimpleTypes) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The type of the security scheme. Valid values are "basic",
+// "apiKey" or "oauth2".
+type SecurityScheme_Type int32
+
+const (
+ SecurityScheme_TYPE_INVALID SecurityScheme_Type = 0
+ SecurityScheme_TYPE_BASIC SecurityScheme_Type = 1
+ SecurityScheme_TYPE_API_KEY SecurityScheme_Type = 2
+ SecurityScheme_TYPE_OAUTH2 SecurityScheme_Type = 3
+)
+
+// Enum value maps for SecurityScheme_Type.
+var (
+ SecurityScheme_Type_name = map[int32]string{
+ 0: "TYPE_INVALID",
+ 1: "TYPE_BASIC",
+ 2: "TYPE_API_KEY",
+ 3: "TYPE_OAUTH2",
+ }
+ SecurityScheme_Type_value = map[string]int32{
+ "TYPE_INVALID": 0,
+ "TYPE_BASIC": 1,
+ "TYPE_API_KEY": 2,
+ "TYPE_OAUTH2": 3,
+ }
+)
+
+func (x SecurityScheme_Type) Enum() *SecurityScheme_Type {
+ p := new(SecurityScheme_Type)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[3].Descriptor()
+}
+
+func (SecurityScheme_Type) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[3]
+}
+
+func (x SecurityScheme_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The location of the API key. Valid values are "query" or "header".
+type SecurityScheme_In int32
+
+const (
+ SecurityScheme_IN_INVALID SecurityScheme_In = 0
+ SecurityScheme_IN_QUERY SecurityScheme_In = 1
+ SecurityScheme_IN_HEADER SecurityScheme_In = 2
+)
+
+// Enum value maps for SecurityScheme_In.
+var (
+ SecurityScheme_In_name = map[int32]string{
+ 0: "IN_INVALID",
+ 1: "IN_QUERY",
+ 2: "IN_HEADER",
+ }
+ SecurityScheme_In_value = map[string]int32{
+ "IN_INVALID": 0,
+ "IN_QUERY": 1,
+ "IN_HEADER": 2,
+ }
+)
+
+func (x SecurityScheme_In) Enum() *SecurityScheme_In {
+ p := new(SecurityScheme_In)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_In) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_In) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[4].Descriptor()
+}
+
+func (SecurityScheme_In) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[4]
+}
+
+func (x SecurityScheme_In) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The flow used by the OAuth2 security scheme. Valid values are
+// "implicit", "password", "application" or "accessCode".
+type SecurityScheme_Flow int32
+
+const (
+ SecurityScheme_FLOW_INVALID SecurityScheme_Flow = 0
+ SecurityScheme_FLOW_IMPLICIT SecurityScheme_Flow = 1
+ SecurityScheme_FLOW_PASSWORD SecurityScheme_Flow = 2
+ SecurityScheme_FLOW_APPLICATION SecurityScheme_Flow = 3
+ SecurityScheme_FLOW_ACCESS_CODE SecurityScheme_Flow = 4
+)
+
+// Enum value maps for SecurityScheme_Flow.
+var (
+ SecurityScheme_Flow_name = map[int32]string{
+ 0: "FLOW_INVALID",
+ 1: "FLOW_IMPLICIT",
+ 2: "FLOW_PASSWORD",
+ 3: "FLOW_APPLICATION",
+ 4: "FLOW_ACCESS_CODE",
+ }
+ SecurityScheme_Flow_value = map[string]int32{
+ "FLOW_INVALID": 0,
+ "FLOW_IMPLICIT": 1,
+ "FLOW_PASSWORD": 2,
+ "FLOW_APPLICATION": 3,
+ "FLOW_ACCESS_CODE": 4,
+ }
+)
+
+func (x SecurityScheme_Flow) Enum() *SecurityScheme_Flow {
+ p := new(SecurityScheme_Flow)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_Flow) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_Flow) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[5].Descriptor()
+}
+
+func (SecurityScheme_Flow) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[5]
+}
+
+func (x SecurityScheme_Flow) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// `Swagger` is a representation of OpenAPI v2 specification's Swagger object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// schemes: HTTPS;
+// consumes: "application/json";
+// produces: "application/json";
+// };
+type Swagger struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // Specifies the OpenAPI Specification version being used. It can be
+ // used by the OpenAPI UI and other clients to interpret the API listing. The
+ // value MUST be "2.0".
+ Swagger string `protobuf:"bytes,1,opt,name=swagger,proto3" json:"swagger,omitempty"`
+ // Provides metadata about the API. The metadata can be used by the
+ // clients if needed.
+ Info *Info `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
+ // The host (name or ip) serving the API. This MUST be the host only and does
+ // not include the scheme nor sub-paths. It MAY include a port. If the host is
+ // not included, the host serving the documentation is to be used (including
+ // the port). The host does not support path templating.
+ Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"`
+ // The base path on which the API is served, which is relative to the host. If
+ // it is not included, the API is served directly under the host. The value
+ // MUST start with a leading slash (/). The basePath does not support path
+ // templating.
+ // Note that using `base_path` does not change the endpoint paths that are
+ // generated in the resulting OpenAPI file. If you wish to use `base_path`
+ // with relatively generated OpenAPI paths, the `base_path` prefix must be
+ // manually removed from your `google.api.http` paths and your code changed to
+ // serve the API from the `base_path`.
+ BasePath string `protobuf:"bytes,4,opt,name=base_path,json=basePath,proto3" json:"base_path,omitempty"`
+ // The transfer protocol of the API. Values MUST be from the list: "http",
+ // "https", "ws", "wss". If the schemes is not included, the default scheme to
+ // be used is the one used to access the OpenAPI definition itself.
+ Schemes []Scheme `protobuf:"varint,5,rep,packed,name=schemes,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.Scheme" json:"schemes,omitempty"`
+ // A list of MIME types the APIs can consume. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Consumes []string `protobuf:"bytes,6,rep,name=consumes,proto3" json:"consumes,omitempty"`
+ // A list of MIME types the APIs can produce. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Produces []string `protobuf:"bytes,7,rep,name=produces,proto3" json:"produces,omitempty"`
+ // An object to hold responses that can be used across operations. This
+ // property does not define global responses for all operations.
+ Responses map[string]*Response `protobuf:"bytes,10,rep,name=responses,proto3" json:"responses,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // Security scheme definitions that can be used across the specification.
+ SecurityDefinitions *SecurityDefinitions `protobuf:"bytes,11,opt,name=security_definitions,json=securityDefinitions,proto3" json:"security_definitions,omitempty"`
+ // A declaration of which security schemes are applied for the API as a whole.
+ // The list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements).
+ // Individual operations can override this definition.
+ Security []*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"`
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []*Tag `protobuf:"bytes,13,rep,name=tags,proto3" json:"tags,omitempty"`
+ // Additional external documentation.
+ ExternalDocs *ExternalDocumentation `protobuf:"bytes,14,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,15,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Swagger) Reset() {
+ *x = Swagger{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Swagger) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Swagger) ProtoMessage() {}
+
+func (x *Swagger) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Swagger) GetSwagger() string {
+ if x != nil {
+ return x.Swagger
+ }
+ return ""
+}
+
+func (x *Swagger) GetInfo() *Info {
+ if x != nil {
+ return x.Info
+ }
+ return nil
+}
+
+func (x *Swagger) GetHost() string {
+ if x != nil {
+ return x.Host
+ }
+ return ""
+}
+
+func (x *Swagger) GetBasePath() string {
+ if x != nil {
+ return x.BasePath
+ }
+ return ""
+}
+
+func (x *Swagger) GetSchemes() []Scheme {
+ if x != nil {
+ return x.Schemes
+ }
+ return nil
+}
+
+func (x *Swagger) GetConsumes() []string {
+ if x != nil {
+ return x.Consumes
+ }
+ return nil
+}
+
+func (x *Swagger) GetProduces() []string {
+ if x != nil {
+ return x.Produces
+ }
+ return nil
+}
+
+func (x *Swagger) GetResponses() map[string]*Response {
+ if x != nil {
+ return x.Responses
+ }
+ return nil
+}
+
+func (x *Swagger) GetSecurityDefinitions() *SecurityDefinitions {
+ if x != nil {
+ return x.SecurityDefinitions
+ }
+ return nil
+}
+
+func (x *Swagger) GetSecurity() []*SecurityRequirement {
+ if x != nil {
+ return x.Security
+ }
+ return nil
+}
+
+func (x *Swagger) GetTags() []*Tag {
+ if x != nil {
+ return x.Tags
+ }
+ return nil
+}
+
+func (x *Swagger) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.ExternalDocs
+ }
+ return nil
+}
+
+func (x *Swagger) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *Swagger) SetSwagger(v string) {
+ x.Swagger = v
+}
+
+func (x *Swagger) SetInfo(v *Info) {
+ x.Info = v
+}
+
+func (x *Swagger) SetHost(v string) {
+ x.Host = v
+}
+
+func (x *Swagger) SetBasePath(v string) {
+ x.BasePath = v
+}
+
+func (x *Swagger) SetSchemes(v []Scheme) {
+ x.Schemes = v
+}
+
+func (x *Swagger) SetConsumes(v []string) {
+ x.Consumes = v
+}
+
+func (x *Swagger) SetProduces(v []string) {
+ x.Produces = v
+}
+
+func (x *Swagger) SetResponses(v map[string]*Response) {
+ x.Responses = v
+}
+
+func (x *Swagger) SetSecurityDefinitions(v *SecurityDefinitions) {
+ x.SecurityDefinitions = v
+}
+
+func (x *Swagger) SetSecurity(v []*SecurityRequirement) {
+ x.Security = v
+}
+
+func (x *Swagger) SetTags(v []*Tag) {
+ x.Tags = v
+}
+
+func (x *Swagger) SetExternalDocs(v *ExternalDocumentation) {
+ x.ExternalDocs = v
+}
+
+func (x *Swagger) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *Swagger) HasInfo() bool {
+ if x == nil {
+ return false
+ }
+ return x.Info != nil
+}
+
+func (x *Swagger) HasSecurityDefinitions() bool {
+ if x == nil {
+ return false
+ }
+ return x.SecurityDefinitions != nil
+}
+
+func (x *Swagger) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.ExternalDocs != nil
+}
+
+func (x *Swagger) ClearInfo() {
+ x.Info = nil
+}
+
+func (x *Swagger) ClearSecurityDefinitions() {
+ x.SecurityDefinitions = nil
+}
+
+func (x *Swagger) ClearExternalDocs() {
+ x.ExternalDocs = nil
+}
+
+type Swagger_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Specifies the OpenAPI Specification version being used. It can be
+ // used by the OpenAPI UI and other clients to interpret the API listing. The
+ // value MUST be "2.0".
+ Swagger string
+ // Provides metadata about the API. The metadata can be used by the
+ // clients if needed.
+ Info *Info
+ // The host (name or ip) serving the API. This MUST be the host only and does
+ // not include the scheme nor sub-paths. It MAY include a port. If the host is
+ // not included, the host serving the documentation is to be used (including
+ // the port). The host does not support path templating.
+ Host string
+ // The base path on which the API is served, which is relative to the host. If
+ // it is not included, the API is served directly under the host. The value
+ // MUST start with a leading slash (/). The basePath does not support path
+ // templating.
+ // Note that using `base_path` does not change the endpoint paths that are
+ // generated in the resulting OpenAPI file. If you wish to use `base_path`
+ // with relatively generated OpenAPI paths, the `base_path` prefix must be
+ // manually removed from your `google.api.http` paths and your code changed to
+ // serve the API from the `base_path`.
+ BasePath string
+ // The transfer protocol of the API. Values MUST be from the list: "http",
+ // "https", "ws", "wss". If the schemes is not included, the default scheme to
+ // be used is the one used to access the OpenAPI definition itself.
+ Schemes []Scheme
+ // A list of MIME types the APIs can consume. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Consumes []string
+ // A list of MIME types the APIs can produce. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Produces []string
+ // An object to hold responses that can be used across operations. This
+ // property does not define global responses for all operations.
+ Responses map[string]*Response
+ // Security scheme definitions that can be used across the specification.
+ SecurityDefinitions *SecurityDefinitions
+ // A declaration of which security schemes are applied for the API as a whole.
+ // The list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements).
+ // Individual operations can override this definition.
+ Security []*SecurityRequirement
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []*Tag
+ // Additional external documentation.
+ ExternalDocs *ExternalDocumentation
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Swagger_builder) Build() *Swagger {
+ m0 := &Swagger{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Swagger = b.Swagger
+ x.Info = b.Info
+ x.Host = b.Host
+ x.BasePath = b.BasePath
+ x.Schemes = b.Schemes
+ x.Consumes = b.Consumes
+ x.Produces = b.Produces
+ x.Responses = b.Responses
+ x.SecurityDefinitions = b.SecurityDefinitions
+ x.Security = b.Security
+ x.Tags = b.Tags
+ x.ExternalDocs = b.ExternalDocs
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `Operation` is a representation of OpenAPI v2 specification's Operation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject
+//
+// Example:
+//
+// service EchoService {
+// rpc Echo(SimpleMessage) returns (SimpleMessage) {
+// option (google.api.http) = {
+// get: "/v1/example/echo/{id}"
+// };
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+// summary: "Get a message.";
+// operation_id: "getMessage";
+// tags: "echo";
+// responses: {
+// key: "200"
+// value: {
+// description: "OK";
+// }
+// }
+// };
+// }
+// }
+type Operation struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []string `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"`
+ // A short summary of what the operation does. For maximum readability in the
+ // swagger-ui, this field SHOULD be less than 120 characters.
+ Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"`
+ // A verbose explanation of the operation behavior. GFM syntax can be used for
+ // rich text representation.
+ Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
+ // Additional external documentation for this operation.
+ ExternalDocs *ExternalDocumentation `protobuf:"bytes,4,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ // Unique string used to identify the operation. The id MUST be unique among
+ // all operations described in the API. Tools and libraries MAY use the
+ // operationId to uniquely identify an operation, therefore, it is recommended
+ // to follow common programming naming conventions.
+ OperationId string `protobuf:"bytes,5,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"`
+ // A list of MIME types the operation can consume. This overrides the consumes
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Consumes []string `protobuf:"bytes,6,rep,name=consumes,proto3" json:"consumes,omitempty"`
+ // A list of MIME types the operation can produce. This overrides the produces
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Produces []string `protobuf:"bytes,7,rep,name=produces,proto3" json:"produces,omitempty"`
+ // The list of possible responses as they are returned from executing this
+ // operation.
+ Responses map[string]*Response `protobuf:"bytes,9,rep,name=responses,proto3" json:"responses,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // The transfer protocol for the operation. Values MUST be from the list:
+ // "http", "https", "ws", "wss". The value overrides the OpenAPI Object
+ // schemes definition.
+ Schemes []Scheme `protobuf:"varint,10,rep,packed,name=schemes,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.Scheme" json:"schemes,omitempty"`
+ // Declares this operation to be deprecated. Usage of the declared operation
+ // should be refrained. Default value is false.
+ Deprecated bool `protobuf:"varint,11,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
+ // A declaration of which security schemes are applied for this operation. The
+ // list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements). This
+ // definition overrides any declared top-level security. To remove a top-level
+ // security declaration, an empty array can be used.
+ Security []*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,13,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // Custom parameters such as HTTP request headers.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/
+ // and https://swagger.io/specification/v2/#parameter-object.
+ Parameters *Parameters `protobuf:"bytes,14,opt,name=parameters,proto3" json:"parameters,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Operation) Reset() {
+ *x = Operation{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Operation) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Operation) ProtoMessage() {}
+
+func (x *Operation) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Operation) GetTags() []string {
+ if x != nil {
+ return x.Tags
+ }
+ return nil
+}
+
+func (x *Operation) GetSummary() string {
+ if x != nil {
+ return x.Summary
+ }
+ return ""
+}
+
+func (x *Operation) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *Operation) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.ExternalDocs
+ }
+ return nil
+}
+
+func (x *Operation) GetOperationId() string {
+ if x != nil {
+ return x.OperationId
+ }
+ return ""
+}
+
+func (x *Operation) GetConsumes() []string {
+ if x != nil {
+ return x.Consumes
+ }
+ return nil
+}
+
+func (x *Operation) GetProduces() []string {
+ if x != nil {
+ return x.Produces
+ }
+ return nil
+}
+
+func (x *Operation) GetResponses() map[string]*Response {
+ if x != nil {
+ return x.Responses
+ }
+ return nil
+}
+
+func (x *Operation) GetSchemes() []Scheme {
+ if x != nil {
+ return x.Schemes
+ }
+ return nil
+}
+
+func (x *Operation) GetDeprecated() bool {
+ if x != nil {
+ return x.Deprecated
+ }
+ return false
+}
+
+func (x *Operation) GetSecurity() []*SecurityRequirement {
+ if x != nil {
+ return x.Security
+ }
+ return nil
+}
+
+func (x *Operation) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *Operation) GetParameters() *Parameters {
+ if x != nil {
+ return x.Parameters
+ }
+ return nil
+}
+
+func (x *Operation) SetTags(v []string) {
+ x.Tags = v
+}
+
+func (x *Operation) SetSummary(v string) {
+ x.Summary = v
+}
+
+func (x *Operation) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *Operation) SetExternalDocs(v *ExternalDocumentation) {
+ x.ExternalDocs = v
+}
+
+func (x *Operation) SetOperationId(v string) {
+ x.OperationId = v
+}
+
+func (x *Operation) SetConsumes(v []string) {
+ x.Consumes = v
+}
+
+func (x *Operation) SetProduces(v []string) {
+ x.Produces = v
+}
+
+func (x *Operation) SetResponses(v map[string]*Response) {
+ x.Responses = v
+}
+
+func (x *Operation) SetSchemes(v []Scheme) {
+ x.Schemes = v
+}
+
+func (x *Operation) SetDeprecated(v bool) {
+ x.Deprecated = v
+}
+
+func (x *Operation) SetSecurity(v []*SecurityRequirement) {
+ x.Security = v
+}
+
+func (x *Operation) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *Operation) SetParameters(v *Parameters) {
+ x.Parameters = v
+}
+
+func (x *Operation) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.ExternalDocs != nil
+}
+
+func (x *Operation) HasParameters() bool {
+ if x == nil {
+ return false
+ }
+ return x.Parameters != nil
+}
+
+func (x *Operation) ClearExternalDocs() {
+ x.ExternalDocs = nil
+}
+
+func (x *Operation) ClearParameters() {
+ x.Parameters = nil
+}
+
+type Operation_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []string
+ // A short summary of what the operation does. For maximum readability in the
+ // swagger-ui, this field SHOULD be less than 120 characters.
+ Summary string
+ // A verbose explanation of the operation behavior. GFM syntax can be used for
+ // rich text representation.
+ Description string
+ // Additional external documentation for this operation.
+ ExternalDocs *ExternalDocumentation
+ // Unique string used to identify the operation. The id MUST be unique among
+ // all operations described in the API. Tools and libraries MAY use the
+ // operationId to uniquely identify an operation, therefore, it is recommended
+ // to follow common programming naming conventions.
+ OperationId string
+ // A list of MIME types the operation can consume. This overrides the consumes
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Consumes []string
+ // A list of MIME types the operation can produce. This overrides the produces
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Produces []string
+ // The list of possible responses as they are returned from executing this
+ // operation.
+ Responses map[string]*Response
+ // The transfer protocol for the operation. Values MUST be from the list:
+ // "http", "https", "ws", "wss". The value overrides the OpenAPI Object
+ // schemes definition.
+ Schemes []Scheme
+ // Declares this operation to be deprecated. Usage of the declared operation
+ // should be refrained. Default value is false.
+ Deprecated bool
+ // A declaration of which security schemes are applied for this operation. The
+ // list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements). This
+ // definition overrides any declared top-level security. To remove a top-level
+ // security declaration, an empty array can be used.
+ Security []*SecurityRequirement
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+ // Custom parameters such as HTTP request headers.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/
+ // and https://swagger.io/specification/v2/#parameter-object.
+ Parameters *Parameters
+}
+
+func (b0 Operation_builder) Build() *Operation {
+ m0 := &Operation{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Tags = b.Tags
+ x.Summary = b.Summary
+ x.Description = b.Description
+ x.ExternalDocs = b.ExternalDocs
+ x.OperationId = b.OperationId
+ x.Consumes = b.Consumes
+ x.Produces = b.Produces
+ x.Responses = b.Responses
+ x.Schemes = b.Schemes
+ x.Deprecated = b.Deprecated
+ x.Security = b.Security
+ x.Extensions = b.Extensions
+ x.Parameters = b.Parameters
+ return m0
+}
+
+// `Parameters` is a representation of OpenAPI v2 specification's parameters object.
+// Note: This technically breaks compatibility with the OpenAPI 2 definition structure as we only
+// allow header parameters to be set here since we do not want users specifying custom non-header
+// parameters beyond those inferred from the Protobuf schema.
+// See: https://swagger.io/specification/v2/#parameter-object
+type Parameters struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // `Headers` is one or more HTTP header parameter.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters
+ Headers []*HeaderParameter `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Parameters) Reset() {
+ *x = Parameters{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Parameters) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Parameters) ProtoMessage() {}
+
+func (x *Parameters) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Parameters) GetHeaders() []*HeaderParameter {
+ if x != nil {
+ return x.Headers
+ }
+ return nil
+}
+
+func (x *Parameters) SetHeaders(v []*HeaderParameter) {
+ x.Headers = v
+}
+
+type Parameters_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Headers` is one or more HTTP header parameter.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters
+ Headers []*HeaderParameter
+}
+
+func (b0 Parameters_builder) Build() *Parameters {
+ m0 := &Parameters{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Headers = b.Headers
+ return m0
+}
+
+// `HeaderParameter` a HTTP header parameter.
+// See: https://swagger.io/specification/v2/#parameter-object
+type HeaderParameter struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // `Name` is the header name.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // `Description` is a short description of the header.
+ Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ // See: https://swagger.io/specification/v2/#parameterType.
+ Type HeaderParameter_Type `protobuf:"varint,3,opt,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter_Type" json:"type,omitempty"`
+ // `Format` The extending format for the previously mentioned type.
+ Format string `protobuf:"bytes,4,opt,name=format,proto3" json:"format,omitempty"`
+ // `Required` indicates if the header is optional
+ Required bool `protobuf:"varint,5,opt,name=required,proto3" json:"required,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *HeaderParameter) Reset() {
+ *x = HeaderParameter{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *HeaderParameter) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HeaderParameter) ProtoMessage() {}
+
+func (x *HeaderParameter) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *HeaderParameter) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetType() HeaderParameter_Type {
+ if x != nil {
+ return x.Type
+ }
+ return HeaderParameter_UNKNOWN
+}
+
+func (x *HeaderParameter) GetFormat() string {
+ if x != nil {
+ return x.Format
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetRequired() bool {
+ if x != nil {
+ return x.Required
+ }
+ return false
+}
+
+func (x *HeaderParameter) SetName(v string) {
+ x.Name = v
+}
+
+func (x *HeaderParameter) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *HeaderParameter) SetType(v HeaderParameter_Type) {
+ x.Type = v
+}
+
+func (x *HeaderParameter) SetFormat(v string) {
+ x.Format = v
+}
+
+func (x *HeaderParameter) SetRequired(v bool) {
+ x.Required = v
+}
+
+type HeaderParameter_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Name` is the header name.
+ Name string
+ // `Description` is a short description of the header.
+ Description string
+ // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ // See: https://swagger.io/specification/v2/#parameterType.
+ Type HeaderParameter_Type
+ // `Format` The extending format for the previously mentioned type.
+ Format string
+ // `Required` indicates if the header is optional
+ Required bool
+}
+
+func (b0 HeaderParameter_builder) Build() *HeaderParameter {
+ m0 := &HeaderParameter{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Name = b.Name
+ x.Description = b.Description
+ x.Type = b.Type
+ x.Format = b.Format
+ x.Required = b.Required
+ return m0
+}
+
+// `Header` is a representation of OpenAPI v2 specification's Header object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject
+type Header struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // `Description` is a short description of the header.
+ Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
+ // `Format` The extending format for the previously mentioned type.
+ Format string `protobuf:"bytes,3,opt,name=format,proto3" json:"format,omitempty"`
+ // `Default` Declares the value of the header that the server will use if none is provided.
+ // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2.
+ // Unlike JSON Schema this value MUST conform to the defined type for the header.
+ Default string `protobuf:"bytes,6,opt,name=default,proto3" json:"default,omitempty"`
+ // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
+ Pattern string `protobuf:"bytes,13,opt,name=pattern,proto3" json:"pattern,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Header) Reset() {
+ *x = Header{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Header) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Header) ProtoMessage() {}
+
+func (x *Header) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Header) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *Header) GetType() string {
+ if x != nil {
+ return x.Type
+ }
+ return ""
+}
+
+func (x *Header) GetFormat() string {
+ if x != nil {
+ return x.Format
+ }
+ return ""
+}
+
+func (x *Header) GetDefault() string {
+ if x != nil {
+ return x.Default
+ }
+ return ""
+}
+
+func (x *Header) GetPattern() string {
+ if x != nil {
+ return x.Pattern
+ }
+ return ""
+}
+
+func (x *Header) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *Header) SetType(v string) {
+ x.Type = v
+}
+
+func (x *Header) SetFormat(v string) {
+ x.Format = v
+}
+
+func (x *Header) SetDefault(v string) {
+ x.Default = v
+}
+
+func (x *Header) SetPattern(v string) {
+ x.Pattern = v
+}
+
+type Header_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Description` is a short description of the header.
+ Description string
+ // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ Type string
+ // `Format` The extending format for the previously mentioned type.
+ Format string
+ // `Default` Declares the value of the header that the server will use if none is provided.
+ // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2.
+ // Unlike JSON Schema this value MUST conform to the defined type for the header.
+ Default string
+ // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
+ Pattern string
+}
+
+func (b0 Header_builder) Build() *Header {
+ m0 := &Header{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Description = b.Description
+ x.Type = b.Type
+ x.Format = b.Format
+ x.Default = b.Default
+ x.Pattern = b.Pattern
+ return m0
+}
+
+// `Response` is a representation of OpenAPI v2 specification's Response object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject
+type Response struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // `Description` is a short description of the response.
+ // GFM syntax can be used for rich text representation.
+ Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ // `Schema` optionally defines the structure of the response.
+ // If `Schema` is not provided, it means there is no content to the response.
+ Schema *Schema `protobuf:"bytes,2,opt,name=schema,proto3" json:"schema,omitempty"`
+ // `Headers` A list of headers that are sent with the response.
+ // `Header` name is expected to be a string in the canonical format of the MIME header key
+ // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey
+ Headers map[string]*Header `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // `Examples` gives per-mimetype response examples.
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object
+ Examples map[string]string `protobuf:"bytes,4,rep,name=examples,proto3" json:"examples,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,5,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Response) Reset() {
+ *x = Response{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Response) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Response) ProtoMessage() {}
+
+func (x *Response) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Response) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *Response) GetSchema() *Schema {
+ if x != nil {
+ return x.Schema
+ }
+ return nil
+}
+
+func (x *Response) GetHeaders() map[string]*Header {
+ if x != nil {
+ return x.Headers
+ }
+ return nil
+}
+
+func (x *Response) GetExamples() map[string]string {
+ if x != nil {
+ return x.Examples
+ }
+ return nil
+}
+
+func (x *Response) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *Response) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *Response) SetSchema(v *Schema) {
+ x.Schema = v
+}
+
+func (x *Response) SetHeaders(v map[string]*Header) {
+ x.Headers = v
+}
+
+func (x *Response) SetExamples(v map[string]string) {
+ x.Examples = v
+}
+
+func (x *Response) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *Response) HasSchema() bool {
+ if x == nil {
+ return false
+ }
+ return x.Schema != nil
+}
+
+func (x *Response) ClearSchema() {
+ x.Schema = nil
+}
+
+type Response_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Description` is a short description of the response.
+ // GFM syntax can be used for rich text representation.
+ Description string
+ // `Schema` optionally defines the structure of the response.
+ // If `Schema` is not provided, it means there is no content to the response.
+ Schema *Schema
+ // `Headers` A list of headers that are sent with the response.
+ // `Header` name is expected to be a string in the canonical format of the MIME header key
+ // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey
+ Headers map[string]*Header
+ // `Examples` gives per-mimetype response examples.
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object
+ Examples map[string]string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Response_builder) Build() *Response {
+ m0 := &Response{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Description = b.Description
+ x.Schema = b.Schema
+ x.Headers = b.Headers
+ x.Examples = b.Examples
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `Info` is a representation of OpenAPI v2 specification's Info object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// ...
+// };
+type Info struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // The title of the application.
+ Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
+ // A short description of the application. GFM syntax can be used for rich
+ // text representation.
+ Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ // The Terms of Service for the API.
+ TermsOfService string `protobuf:"bytes,3,opt,name=terms_of_service,json=termsOfService,proto3" json:"terms_of_service,omitempty"`
+ // The contact information for the exposed API.
+ Contact *Contact `protobuf:"bytes,4,opt,name=contact,proto3" json:"contact,omitempty"`
+ // The license information for the exposed API.
+ License *License `protobuf:"bytes,5,opt,name=license,proto3" json:"license,omitempty"`
+ // Provides the version of the application API (not to be confused
+ // with the specification version).
+ Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,7,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Info) Reset() {
+ *x = Info{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Info) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Info) ProtoMessage() {}
+
+func (x *Info) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Info) GetTitle() string {
+ if x != nil {
+ return x.Title
+ }
+ return ""
+}
+
+func (x *Info) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *Info) GetTermsOfService() string {
+ if x != nil {
+ return x.TermsOfService
+ }
+ return ""
+}
+
+func (x *Info) GetContact() *Contact {
+ if x != nil {
+ return x.Contact
+ }
+ return nil
+}
+
+func (x *Info) GetLicense() *License {
+ if x != nil {
+ return x.License
+ }
+ return nil
+}
+
+func (x *Info) GetVersion() string {
+ if x != nil {
+ return x.Version
+ }
+ return ""
+}
+
+func (x *Info) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *Info) SetTitle(v string) {
+ x.Title = v
+}
+
+func (x *Info) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *Info) SetTermsOfService(v string) {
+ x.TermsOfService = v
+}
+
+func (x *Info) SetContact(v *Contact) {
+ x.Contact = v
+}
+
+func (x *Info) SetLicense(v *License) {
+ x.License = v
+}
+
+func (x *Info) SetVersion(v string) {
+ x.Version = v
+}
+
+func (x *Info) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *Info) HasContact() bool {
+ if x == nil {
+ return false
+ }
+ return x.Contact != nil
+}
+
+func (x *Info) HasLicense() bool {
+ if x == nil {
+ return false
+ }
+ return x.License != nil
+}
+
+func (x *Info) ClearContact() {
+ x.Contact = nil
+}
+
+func (x *Info) ClearLicense() {
+ x.License = nil
+}
+
+type Info_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The title of the application.
+ Title string
+ // A short description of the application. GFM syntax can be used for rich
+ // text representation.
+ Description string
+ // The Terms of Service for the API.
+ TermsOfService string
+ // The contact information for the exposed API.
+ Contact *Contact
+ // The license information for the exposed API.
+ License *License
+ // Provides the version of the application API (not to be confused
+ // with the specification version).
+ Version string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Info_builder) Build() *Info {
+ m0 := &Info{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Title = b.Title
+ x.Description = b.Description
+ x.TermsOfService = b.TermsOfService
+ x.Contact = b.Contact
+ x.License = b.License
+ x.Version = b.Version
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `Contact` is a representation of OpenAPI v2 specification's Contact object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// ...
+// };
+// ...
+// };
+type Contact struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // The identifying name of the contact person/organization.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // The URL pointing to the contact information. MUST be in the format of a
+ // URL.
+ Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ // The email address of the contact person/organization. MUST be in the format
+ // of an email address.
+ Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Contact) Reset() {
+ *x = Contact{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Contact) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Contact) ProtoMessage() {}
+
+func (x *Contact) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[7]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Contact) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Contact) GetUrl() string {
+ if x != nil {
+ return x.Url
+ }
+ return ""
+}
+
+func (x *Contact) GetEmail() string {
+ if x != nil {
+ return x.Email
+ }
+ return ""
+}
+
+func (x *Contact) SetName(v string) {
+ x.Name = v
+}
+
+func (x *Contact) SetUrl(v string) {
+ x.Url = v
+}
+
+func (x *Contact) SetEmail(v string) {
+ x.Email = v
+}
+
+type Contact_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The identifying name of the contact person/organization.
+ Name string
+ // The URL pointing to the contact information. MUST be in the format of a
+ // URL.
+ Url string
+ // The email address of the contact person/organization. MUST be in the format
+ // of an email address.
+ Email string
+}
+
+func (b0 Contact_builder) Build() *Contact {
+ m0 := &Contact{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Name = b.Name
+ x.Url = b.Url
+ x.Email = b.Email
+ return m0
+}
+
+// `License` is a representation of OpenAPI v2 specification's License object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// ...
+// };
+// ...
+// };
+type License struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // The license name used for the API.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // A URL to the license used for the API. MUST be in the format of a URL.
+ Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *License) Reset() {
+ *x = License{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *License) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*License) ProtoMessage() {}
+
+func (x *License) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[8]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *License) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *License) GetUrl() string {
+ if x != nil {
+ return x.Url
+ }
+ return ""
+}
+
+func (x *License) SetName(v string) {
+ x.Name = v
+}
+
+func (x *License) SetUrl(v string) {
+ x.Url = v
+}
+
+type License_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The license name used for the API.
+ Name string
+ // A URL to the license used for the API. MUST be in the format of a URL.
+ Url string
+}
+
+func (b0 License_builder) Build() *License {
+ m0 := &License{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Name = b.Name
+ x.Url = b.Url
+ return m0
+}
+
+// `ExternalDocumentation` is a representation of OpenAPI v2 specification's
+// ExternalDocumentation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// ...
+// external_docs: {
+// description: "More about gRPC-Gateway";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// }
+// ...
+// };
+type ExternalDocumentation struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // A short description of the target documentation. GFM syntax can be used for
+ // rich text representation.
+ Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ // The URL for the target documentation. Value MUST be in the format
+ // of a URL.
+ Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ExternalDocumentation) Reset() {
+ *x = ExternalDocumentation{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ExternalDocumentation) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExternalDocumentation) ProtoMessage() {}
+
+func (x *ExternalDocumentation) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[9]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *ExternalDocumentation) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *ExternalDocumentation) GetUrl() string {
+ if x != nil {
+ return x.Url
+ }
+ return ""
+}
+
+func (x *ExternalDocumentation) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *ExternalDocumentation) SetUrl(v string) {
+ x.Url = v
+}
+
+type ExternalDocumentation_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A short description of the target documentation. GFM syntax can be used for
+ // rich text representation.
+ Description string
+ // The URL for the target documentation. Value MUST be in the format
+ // of a URL.
+ Url string
+}
+
+func (b0 ExternalDocumentation_builder) Build() *ExternalDocumentation {
+ m0 := &ExternalDocumentation{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Description = b.Description
+ x.Url = b.Url
+ return m0
+}
+
+// `Schema` is a representation of OpenAPI v2 specification's Schema object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+type Schema struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ JsonSchema *JSONSchema `protobuf:"bytes,1,opt,name=json_schema,json=jsonSchema,proto3" json:"json_schema,omitempty"`
+ // Adds support for polymorphism. The discriminator is the schema property
+ // name that is used to differentiate between other schema that inherit this
+ // schema. The property name used MUST be defined at this schema and it MUST
+ // be in the required property list. When used, the value MUST be the name of
+ // this schema or any schema that inherits it.
+ Discriminator string `protobuf:"bytes,2,opt,name=discriminator,proto3" json:"discriminator,omitempty"`
+ // Relevant only for Schema "properties" definitions. Declares the property as
+ // "read only". This means that it MAY be sent as part of a response but MUST
+ // NOT be sent as part of the request. Properties marked as readOnly being
+ // true SHOULD NOT be in the required list of the defined schema. Default
+ // value is false.
+ ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation `protobuf:"bytes,5,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ // A free-form property to include an example of an instance for this schema in JSON.
+ // This is copied verbatim to the output.
+ Example string `protobuf:"bytes,6,opt,name=example,proto3" json:"example,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Schema) Reset() {
+ *x = Schema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Schema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Schema) ProtoMessage() {}
+
+func (x *Schema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[10]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Schema) GetJsonSchema() *JSONSchema {
+ if x != nil {
+ return x.JsonSchema
+ }
+ return nil
+}
+
+func (x *Schema) GetDiscriminator() string {
+ if x != nil {
+ return x.Discriminator
+ }
+ return ""
+}
+
+func (x *Schema) GetReadOnly() bool {
+ if x != nil {
+ return x.ReadOnly
+ }
+ return false
+}
+
+func (x *Schema) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.ExternalDocs
+ }
+ return nil
+}
+
+func (x *Schema) GetExample() string {
+ if x != nil {
+ return x.Example
+ }
+ return ""
+}
+
+func (x *Schema) SetJsonSchema(v *JSONSchema) {
+ x.JsonSchema = v
+}
+
+func (x *Schema) SetDiscriminator(v string) {
+ x.Discriminator = v
+}
+
+func (x *Schema) SetReadOnly(v bool) {
+ x.ReadOnly = v
+}
+
+func (x *Schema) SetExternalDocs(v *ExternalDocumentation) {
+ x.ExternalDocs = v
+}
+
+func (x *Schema) SetExample(v string) {
+ x.Example = v
+}
+
+func (x *Schema) HasJsonSchema() bool {
+ if x == nil {
+ return false
+ }
+ return x.JsonSchema != nil
+}
+
+func (x *Schema) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.ExternalDocs != nil
+}
+
+func (x *Schema) ClearJsonSchema() {
+ x.JsonSchema = nil
+}
+
+func (x *Schema) ClearExternalDocs() {
+ x.ExternalDocs = nil
+}
+
+type Schema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ JsonSchema *JSONSchema
+ // Adds support for polymorphism. The discriminator is the schema property
+ // name that is used to differentiate between other schema that inherit this
+ // schema. The property name used MUST be defined at this schema and it MUST
+ // be in the required property list. When used, the value MUST be the name of
+ // this schema or any schema that inherits it.
+ Discriminator string
+ // Relevant only for Schema "properties" definitions. Declares the property as
+ // "read only". This means that it MAY be sent as part of a response but MUST
+ // NOT be sent as part of the request. Properties marked as readOnly being
+ // true SHOULD NOT be in the required list of the defined schema. Default
+ // value is false.
+ ReadOnly bool
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation
+ // A free-form property to include an example of an instance for this schema in JSON.
+ // This is copied verbatim to the output.
+ Example string
+}
+
+func (b0 Schema_builder) Build() *Schema {
+ m0 := &Schema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.JsonSchema = b.JsonSchema
+ x.Discriminator = b.Discriminator
+ x.ReadOnly = b.ReadOnly
+ x.ExternalDocs = b.ExternalDocs
+ x.Example = b.Example
+ return m0
+}
+
+// `EnumSchema` is subset of fields from the OpenAPI v2 specification's Schema object.
+// Only fields that are applicable to Enums are included
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_enum) = {
+// ...
+// title: "MyEnum";
+// description:"This is my nice enum";
+// example: "ZERO";
+// required: true;
+// ...
+// };
+type EnumSchema struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // A short description of the schema.
+ Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ Default string `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"`
+ // The title of the schema.
+ Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
+ Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"`
+ ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation `protobuf:"bytes,6,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ Example string `protobuf:"bytes,7,opt,name=example,proto3" json:"example,omitempty"`
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string `protobuf:"bytes,8,opt,name=ref,proto3" json:"ref,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,9,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *EnumSchema) Reset() {
+ *x = EnumSchema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *EnumSchema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EnumSchema) ProtoMessage() {}
+
+func (x *EnumSchema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[11]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *EnumSchema) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetDefault() string {
+ if x != nil {
+ return x.Default
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetTitle() string {
+ if x != nil {
+ return x.Title
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetRequired() bool {
+ if x != nil {
+ return x.Required
+ }
+ return false
+}
+
+func (x *EnumSchema) GetReadOnly() bool {
+ if x != nil {
+ return x.ReadOnly
+ }
+ return false
+}
+
+func (x *EnumSchema) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.ExternalDocs
+ }
+ return nil
+}
+
+func (x *EnumSchema) GetExample() string {
+ if x != nil {
+ return x.Example
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetRef() string {
+ if x != nil {
+ return x.Ref
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *EnumSchema) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *EnumSchema) SetDefault(v string) {
+ x.Default = v
+}
+
+func (x *EnumSchema) SetTitle(v string) {
+ x.Title = v
+}
+
+func (x *EnumSchema) SetRequired(v bool) {
+ x.Required = v
+}
+
+func (x *EnumSchema) SetReadOnly(v bool) {
+ x.ReadOnly = v
+}
+
+func (x *EnumSchema) SetExternalDocs(v *ExternalDocumentation) {
+ x.ExternalDocs = v
+}
+
+func (x *EnumSchema) SetExample(v string) {
+ x.Example = v
+}
+
+func (x *EnumSchema) SetRef(v string) {
+ x.Ref = v
+}
+
+func (x *EnumSchema) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *EnumSchema) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.ExternalDocs != nil
+}
+
+func (x *EnumSchema) ClearExternalDocs() {
+ x.ExternalDocs = nil
+}
+
+type EnumSchema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A short description of the schema.
+ Description string
+ Default string
+ // The title of the schema.
+ Title string
+ Required bool
+ ReadOnly bool
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation
+ Example string
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 EnumSchema_builder) Build() *EnumSchema {
+ m0 := &EnumSchema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Description = b.Description
+ x.Default = b.Default
+ x.Title = b.Title
+ x.Required = b.Required
+ x.ReadOnly = b.ReadOnly
+ x.ExternalDocs = b.ExternalDocs
+ x.Example = b.Example
+ x.Ref = b.Ref
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `JSONSchema` represents properties from JSON Schema taken, and as used, in
+// the OpenAPI v2 spec.
+//
+// This includes changes made by OpenAPI v2.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// See also: https://cswr.github.io/JsonSchema/spec/basic_types/,
+// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json
+//
+// Example:
+//
+// message SimpleMessage {
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+// json_schema: {
+// title: "SimpleMessage"
+// description: "A simple message."
+// required: ["id"]
+// }
+// };
+//
+// // Id represents the message identifier.
+// string id = 1; [
+// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+// description: "The unique identifier of the simple message."
+// }];
+// }
+type JSONSchema struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string `protobuf:"bytes,3,opt,name=ref,proto3" json:"ref,omitempty"`
+ // The title of the schema.
+ Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
+ // A short description of the schema.
+ Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
+ Default string `protobuf:"bytes,7,opt,name=default,proto3" json:"default,omitempty"`
+ ReadOnly bool `protobuf:"varint,8,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ // A free-form property to include a JSON example of this field. This is copied
+ // verbatim to the output swagger.json. Quotes must be escaped.
+ // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+ Example string `protobuf:"bytes,9,opt,name=example,proto3" json:"example,omitempty"`
+ MultipleOf float64 `protobuf:"fixed64,10,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"`
+ // Maximum represents an inclusive upper limit for a numeric instance. The
+ // value of MUST be a number,
+ Maximum float64 `protobuf:"fixed64,11,opt,name=maximum,proto3" json:"maximum,omitempty"`
+ ExclusiveMaximum bool `protobuf:"varint,12,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"`
+ // minimum represents an inclusive lower limit for a numeric instance. The
+ // value of MUST be a number,
+ Minimum float64 `protobuf:"fixed64,13,opt,name=minimum,proto3" json:"minimum,omitempty"`
+ ExclusiveMinimum bool `protobuf:"varint,14,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"`
+ MaxLength uint64 `protobuf:"varint,15,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"`
+ MinLength uint64 `protobuf:"varint,16,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"`
+ Pattern string `protobuf:"bytes,17,opt,name=pattern,proto3" json:"pattern,omitempty"`
+ MaxItems uint64 `protobuf:"varint,20,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"`
+ MinItems uint64 `protobuf:"varint,21,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"`
+ UniqueItems bool `protobuf:"varint,22,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"`
+ MaxProperties uint64 `protobuf:"varint,24,opt,name=max_properties,json=maxProperties,proto3" json:"max_properties,omitempty"`
+ MinProperties uint64 `protobuf:"varint,25,opt,name=min_properties,json=minProperties,proto3" json:"min_properties,omitempty"`
+ Required []string `protobuf:"bytes,26,rep,name=required,proto3" json:"required,omitempty"`
+ // Items in 'array' must be unique.
+ Array []string `protobuf:"bytes,34,rep,name=array,proto3" json:"array,omitempty"`
+ Type []JSONSchema_JSONSchemaSimpleTypes `protobuf:"varint,35,rep,packed,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.JSONSchema_JSONSchemaSimpleTypes" json:"type,omitempty"`
+ // `Format`
+ Format string `protobuf:"bytes,36,opt,name=format,proto3" json:"format,omitempty"`
+ // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1
+ Enum []string `protobuf:"bytes,46,rep,name=enum,proto3" json:"enum,omitempty"`
+ // Additional field level properties used when generating the OpenAPI v2 file.
+ FieldConfiguration *JSONSchema_FieldConfiguration `protobuf:"bytes,1001,opt,name=field_configuration,json=fieldConfiguration,proto3" json:"field_configuration,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,48,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *JSONSchema) Reset() {
+ *x = JSONSchema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *JSONSchema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JSONSchema) ProtoMessage() {}
+
+func (x *JSONSchema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[12]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *JSONSchema) GetRef() string {
+ if x != nil {
+ return x.Ref
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetTitle() string {
+ if x != nil {
+ return x.Title
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetDefault() string {
+ if x != nil {
+ return x.Default
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetReadOnly() bool {
+ if x != nil {
+ return x.ReadOnly
+ }
+ return false
+}
+
+func (x *JSONSchema) GetExample() string {
+ if x != nil {
+ return x.Example
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetMultipleOf() float64 {
+ if x != nil {
+ return x.MultipleOf
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMaximum() float64 {
+ if x != nil {
+ return x.Maximum
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetExclusiveMaximum() bool {
+ if x != nil {
+ return x.ExclusiveMaximum
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMinimum() float64 {
+ if x != nil {
+ return x.Minimum
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetExclusiveMinimum() bool {
+ if x != nil {
+ return x.ExclusiveMinimum
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMaxLength() uint64 {
+ if x != nil {
+ return x.MaxLength
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinLength() uint64 {
+ if x != nil {
+ return x.MinLength
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetPattern() string {
+ if x != nil {
+ return x.Pattern
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetMaxItems() uint64 {
+ if x != nil {
+ return x.MaxItems
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinItems() uint64 {
+ if x != nil {
+ return x.MinItems
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetUniqueItems() bool {
+ if x != nil {
+ return x.UniqueItems
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMaxProperties() uint64 {
+ if x != nil {
+ return x.MaxProperties
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinProperties() uint64 {
+ if x != nil {
+ return x.MinProperties
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetRequired() []string {
+ if x != nil {
+ return x.Required
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetArray() []string {
+ if x != nil {
+ return x.Array
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetType() []JSONSchema_JSONSchemaSimpleTypes {
+ if x != nil {
+ return x.Type
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetFormat() string {
+ if x != nil {
+ return x.Format
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetEnum() []string {
+ if x != nil {
+ return x.Enum
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetFieldConfiguration() *JSONSchema_FieldConfiguration {
+ if x != nil {
+ return x.FieldConfiguration
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *JSONSchema) SetRef(v string) {
+ x.Ref = v
+}
+
+func (x *JSONSchema) SetTitle(v string) {
+ x.Title = v
+}
+
+func (x *JSONSchema) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *JSONSchema) SetDefault(v string) {
+ x.Default = v
+}
+
+func (x *JSONSchema) SetReadOnly(v bool) {
+ x.ReadOnly = v
+}
+
+func (x *JSONSchema) SetExample(v string) {
+ x.Example = v
+}
+
+func (x *JSONSchema) SetMultipleOf(v float64) {
+ x.MultipleOf = v
+}
+
+func (x *JSONSchema) SetMaximum(v float64) {
+ x.Maximum = v
+}
+
+func (x *JSONSchema) SetExclusiveMaximum(v bool) {
+ x.ExclusiveMaximum = v
+}
+
+func (x *JSONSchema) SetMinimum(v float64) {
+ x.Minimum = v
+}
+
+func (x *JSONSchema) SetExclusiveMinimum(v bool) {
+ x.ExclusiveMinimum = v
+}
+
+func (x *JSONSchema) SetMaxLength(v uint64) {
+ x.MaxLength = v
+}
+
+func (x *JSONSchema) SetMinLength(v uint64) {
+ x.MinLength = v
+}
+
+func (x *JSONSchema) SetPattern(v string) {
+ x.Pattern = v
+}
+
+func (x *JSONSchema) SetMaxItems(v uint64) {
+ x.MaxItems = v
+}
+
+func (x *JSONSchema) SetMinItems(v uint64) {
+ x.MinItems = v
+}
+
+func (x *JSONSchema) SetUniqueItems(v bool) {
+ x.UniqueItems = v
+}
+
+func (x *JSONSchema) SetMaxProperties(v uint64) {
+ x.MaxProperties = v
+}
+
+func (x *JSONSchema) SetMinProperties(v uint64) {
+ x.MinProperties = v
+}
+
+func (x *JSONSchema) SetRequired(v []string) {
+ x.Required = v
+}
+
+func (x *JSONSchema) SetArray(v []string) {
+ x.Array = v
+}
+
+func (x *JSONSchema) SetType(v []JSONSchema_JSONSchemaSimpleTypes) {
+ x.Type = v
+}
+
+func (x *JSONSchema) SetFormat(v string) {
+ x.Format = v
+}
+
+func (x *JSONSchema) SetEnum(v []string) {
+ x.Enum = v
+}
+
+func (x *JSONSchema) SetFieldConfiguration(v *JSONSchema_FieldConfiguration) {
+ x.FieldConfiguration = v
+}
+
+func (x *JSONSchema) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *JSONSchema) HasFieldConfiguration() bool {
+ if x == nil {
+ return false
+ }
+ return x.FieldConfiguration != nil
+}
+
+func (x *JSONSchema) ClearFieldConfiguration() {
+ x.FieldConfiguration = nil
+}
+
+type JSONSchema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string
+ // The title of the schema.
+ Title string
+ // A short description of the schema.
+ Description string
+ Default string
+ ReadOnly bool
+ // A free-form property to include a JSON example of this field. This is copied
+ // verbatim to the output swagger.json. Quotes must be escaped.
+ // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+ Example string
+ MultipleOf float64
+ // Maximum represents an inclusive upper limit for a numeric instance. The
+ // value of MUST be a number,
+ Maximum float64
+ ExclusiveMaximum bool
+ // minimum represents an inclusive lower limit for a numeric instance. The
+ // value of MUST be a number,
+ Minimum float64
+ ExclusiveMinimum bool
+ MaxLength uint64
+ MinLength uint64
+ Pattern string
+ MaxItems uint64
+ MinItems uint64
+ UniqueItems bool
+ MaxProperties uint64
+ MinProperties uint64
+ Required []string
+ // Items in 'array' must be unique.
+ Array []string
+ Type []JSONSchema_JSONSchemaSimpleTypes
+ // `Format`
+ Format string
+ // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1
+ Enum []string
+ // Additional field level properties used when generating the OpenAPI v2 file.
+ FieldConfiguration *JSONSchema_FieldConfiguration
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 JSONSchema_builder) Build() *JSONSchema {
+ m0 := &JSONSchema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Ref = b.Ref
+ x.Title = b.Title
+ x.Description = b.Description
+ x.Default = b.Default
+ x.ReadOnly = b.ReadOnly
+ x.Example = b.Example
+ x.MultipleOf = b.MultipleOf
+ x.Maximum = b.Maximum
+ x.ExclusiveMaximum = b.ExclusiveMaximum
+ x.Minimum = b.Minimum
+ x.ExclusiveMinimum = b.ExclusiveMinimum
+ x.MaxLength = b.MaxLength
+ x.MinLength = b.MinLength
+ x.Pattern = b.Pattern
+ x.MaxItems = b.MaxItems
+ x.MinItems = b.MinItems
+ x.UniqueItems = b.UniqueItems
+ x.MaxProperties = b.MaxProperties
+ x.MinProperties = b.MinProperties
+ x.Required = b.Required
+ x.Array = b.Array
+ x.Type = b.Type
+ x.Format = b.Format
+ x.Enum = b.Enum
+ x.FieldConfiguration = b.FieldConfiguration
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `Tag` is a representation of OpenAPI v2 specification's Tag object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject
+type Tag struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // The name of the tag. Use it to allow override of the name of a
+ // global Tag object, then use that name to reference the tag throughout the
+ // OpenAPI file.
+ Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ // A short description for the tag. GFM syntax can be used for rich text
+ // representation.
+ Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ // Additional external documentation for this tag.
+ ExternalDocs *ExternalDocumentation `protobuf:"bytes,3,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,4,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Tag) Reset() {
+ *x = Tag{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Tag) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Tag) ProtoMessage() {}
+
+func (x *Tag) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[13]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Tag) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Tag) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *Tag) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.ExternalDocs
+ }
+ return nil
+}
+
+func (x *Tag) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *Tag) SetName(v string) {
+ x.Name = v
+}
+
+func (x *Tag) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *Tag) SetExternalDocs(v *ExternalDocumentation) {
+ x.ExternalDocs = v
+}
+
+func (x *Tag) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *Tag) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.ExternalDocs != nil
+}
+
+func (x *Tag) ClearExternalDocs() {
+ x.ExternalDocs = nil
+}
+
+type Tag_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The name of the tag. Use it to allow override of the name of a
+ // global Tag object, then use that name to reference the tag throughout the
+ // OpenAPI file.
+ Name string
+ // A short description for the tag. GFM syntax can be used for rich text
+ // representation.
+ Description string
+ // Additional external documentation for this tag.
+ ExternalDocs *ExternalDocumentation
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Tag_builder) Build() *Tag {
+ m0 := &Tag{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Name = b.Name
+ x.Description = b.Description
+ x.ExternalDocs = b.ExternalDocs
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `SecurityDefinitions` is a representation of OpenAPI v2 specification's
+// Security Definitions object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject
+//
+// A declaration of the security schemes available to be used in the
+// specification. This does not enforce the security schemes on the operations
+// and only serves to provide the relevant details for each scheme.
+type SecurityDefinitions struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // A single security scheme definition, mapping a "name" to the scheme it
+ // defines.
+ Security map[string]*SecurityScheme `protobuf:"bytes,1,rep,name=security,proto3" json:"security,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityDefinitions) Reset() {
+ *x = SecurityDefinitions{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityDefinitions) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityDefinitions) ProtoMessage() {}
+
+func (x *SecurityDefinitions) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[14]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityDefinitions) GetSecurity() map[string]*SecurityScheme {
+ if x != nil {
+ return x.Security
+ }
+ return nil
+}
+
+func (x *SecurityDefinitions) SetSecurity(v map[string]*SecurityScheme) {
+ x.Security = v
+}
+
+type SecurityDefinitions_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A single security scheme definition, mapping a "name" to the scheme it
+ // defines.
+ Security map[string]*SecurityScheme
+}
+
+func (b0 SecurityDefinitions_builder) Build() *SecurityDefinitions {
+ m0 := &SecurityDefinitions{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Security = b.Security
+ return m0
+}
+
+// `SecurityScheme` is a representation of OpenAPI v2 specification's
+// Security Scheme object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject
+//
+// Allows the definition of a security scheme that can be used by the
+// operations. Supported schemes are basic authentication, an API key (either as
+// a header or as a query parameter) and OAuth2's common flows (implicit,
+// password, application and access code).
+type SecurityScheme struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // The type of the security scheme. Valid values are "basic",
+ // "apiKey" or "oauth2".
+ Type SecurityScheme_Type `protobuf:"varint,1,opt,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_Type" json:"type,omitempty"`
+ // A short description for security scheme.
+ Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ // The name of the header or query parameter to be used.
+ // Valid for apiKey.
+ Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+ // The location of the API key. Valid values are "query" or
+ // "header".
+ // Valid for apiKey.
+ In SecurityScheme_In `protobuf:"varint,4,opt,name=in,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_In" json:"in,omitempty"`
+ // The flow used by the OAuth2 security scheme. Valid values are
+ // "implicit", "password", "application" or "accessCode".
+ // Valid for oauth2.
+ Flow SecurityScheme_Flow `protobuf:"varint,5,opt,name=flow,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_Flow" json:"flow,omitempty"`
+ // The authorization URL to be used for this flow. This SHOULD be in
+ // the form of a URL.
+ // Valid for oauth2/implicit and oauth2/accessCode.
+ AuthorizationUrl string `protobuf:"bytes,6,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"`
+ // The token URL to be used for this flow. This SHOULD be in the
+ // form of a URL.
+ // Valid for oauth2/password, oauth2/application and oauth2/accessCode.
+ TokenUrl string `protobuf:"bytes,7,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"`
+ // The available scopes for the OAuth2 security scheme.
+ // Valid for oauth2.
+ Scopes *Scopes `protobuf:"bytes,8,opt,name=scopes,proto3" json:"scopes,omitempty"`
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value `protobuf:"bytes,9,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityScheme) Reset() {
+ *x = SecurityScheme{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityScheme) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityScheme) ProtoMessage() {}
+
+func (x *SecurityScheme) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[15]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityScheme) GetType() SecurityScheme_Type {
+ if x != nil {
+ return x.Type
+ }
+ return SecurityScheme_TYPE_INVALID
+}
+
+func (x *SecurityScheme) GetDescription() string {
+ if x != nil {
+ return x.Description
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetIn() SecurityScheme_In {
+ if x != nil {
+ return x.In
+ }
+ return SecurityScheme_IN_INVALID
+}
+
+func (x *SecurityScheme) GetFlow() SecurityScheme_Flow {
+ if x != nil {
+ return x.Flow
+ }
+ return SecurityScheme_FLOW_INVALID
+}
+
+func (x *SecurityScheme) GetAuthorizationUrl() string {
+ if x != nil {
+ return x.AuthorizationUrl
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetTokenUrl() string {
+ if x != nil {
+ return x.TokenUrl
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetScopes() *Scopes {
+ if x != nil {
+ return x.Scopes
+ }
+ return nil
+}
+
+func (x *SecurityScheme) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.Extensions
+ }
+ return nil
+}
+
+func (x *SecurityScheme) SetType(v SecurityScheme_Type) {
+ x.Type = v
+}
+
+func (x *SecurityScheme) SetDescription(v string) {
+ x.Description = v
+}
+
+func (x *SecurityScheme) SetName(v string) {
+ x.Name = v
+}
+
+func (x *SecurityScheme) SetIn(v SecurityScheme_In) {
+ x.In = v
+}
+
+func (x *SecurityScheme) SetFlow(v SecurityScheme_Flow) {
+ x.Flow = v
+}
+
+func (x *SecurityScheme) SetAuthorizationUrl(v string) {
+ x.AuthorizationUrl = v
+}
+
+func (x *SecurityScheme) SetTokenUrl(v string) {
+ x.TokenUrl = v
+}
+
+func (x *SecurityScheme) SetScopes(v *Scopes) {
+ x.Scopes = v
+}
+
+func (x *SecurityScheme) SetExtensions(v map[string]*structpb.Value) {
+ x.Extensions = v
+}
+
+func (x *SecurityScheme) HasScopes() bool {
+ if x == nil {
+ return false
+ }
+ return x.Scopes != nil
+}
+
+func (x *SecurityScheme) ClearScopes() {
+ x.Scopes = nil
+}
+
+type SecurityScheme_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The type of the security scheme. Valid values are "basic",
+ // "apiKey" or "oauth2".
+ Type SecurityScheme_Type
+ // A short description for security scheme.
+ Description string
+ // The name of the header or query parameter to be used.
+ // Valid for apiKey.
+ Name string
+ // The location of the API key. Valid values are "query" or
+ // "header".
+ // Valid for apiKey.
+ In SecurityScheme_In
+ // The flow used by the OAuth2 security scheme. Valid values are
+ // "implicit", "password", "application" or "accessCode".
+ // Valid for oauth2.
+ Flow SecurityScheme_Flow
+ // The authorization URL to be used for this flow. This SHOULD be in
+ // the form of a URL.
+ // Valid for oauth2/implicit and oauth2/accessCode.
+ AuthorizationUrl string
+ // The token URL to be used for this flow. This SHOULD be in the
+ // form of a URL.
+ // Valid for oauth2/password, oauth2/application and oauth2/accessCode.
+ TokenUrl string
+ // The available scopes for the OAuth2 security scheme.
+ // Valid for oauth2.
+ Scopes *Scopes
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 SecurityScheme_builder) Build() *SecurityScheme {
+ m0 := &SecurityScheme{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Type = b.Type
+ x.Description = b.Description
+ x.Name = b.Name
+ x.In = b.In
+ x.Flow = b.Flow
+ x.AuthorizationUrl = b.AuthorizationUrl
+ x.TokenUrl = b.TokenUrl
+ x.Scopes = b.Scopes
+ x.Extensions = b.Extensions
+ return m0
+}
+
+// `SecurityRequirement` is a representation of OpenAPI v2 specification's
+// Security Requirement object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject
+//
+// Lists the required security schemes to execute this operation. The object can
+// have multiple security schemes declared in it which are all required (that
+// is, there is a logical AND between the schemes).
+//
+// The name used for each property MUST correspond to a security scheme
+// declared in the Security Definitions.
+type SecurityRequirement struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // Each name must correspond to a security scheme which is declared in
+ // the Security Definitions. If the security scheme is of type "oauth2",
+ // then the value is a list of scope names required for the execution.
+ // For other security scheme types, the array MUST be empty.
+ SecurityRequirement map[string]*SecurityRequirement_SecurityRequirementValue `protobuf:"bytes,1,rep,name=security_requirement,json=securityRequirement,proto3" json:"security_requirement,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityRequirement) Reset() {
+ *x = SecurityRequirement{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityRequirement) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityRequirement) ProtoMessage() {}
+
+func (x *SecurityRequirement) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[16]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityRequirement) GetSecurityRequirement() map[string]*SecurityRequirement_SecurityRequirementValue {
+ if x != nil {
+ return x.SecurityRequirement
+ }
+ return nil
+}
+
+func (x *SecurityRequirement) SetSecurityRequirement(v map[string]*SecurityRequirement_SecurityRequirementValue) {
+ x.SecurityRequirement = v
+}
+
+type SecurityRequirement_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Each name must correspond to a security scheme which is declared in
+ // the Security Definitions. If the security scheme is of type "oauth2",
+ // then the value is a list of scope names required for the execution.
+ // For other security scheme types, the array MUST be empty.
+ SecurityRequirement map[string]*SecurityRequirement_SecurityRequirementValue
+}
+
+func (b0 SecurityRequirement_builder) Build() *SecurityRequirement {
+ m0 := &SecurityRequirement{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.SecurityRequirement = b.SecurityRequirement
+ return m0
+}
+
+// `Scopes` is a representation of OpenAPI v2 specification's Scopes object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject
+//
+// Lists the available scopes for an OAuth2 security scheme.
+type Scopes struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // Maps between a name of a scope to a short description of it (as the value
+ // of the property).
+ Scope map[string]string `protobuf:"bytes,1,rep,name=scope,proto3" json:"scope,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Scopes) Reset() {
+ *x = Scopes{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Scopes) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Scopes) ProtoMessage() {}
+
+func (x *Scopes) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[17]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Scopes) GetScope() map[string]string {
+ if x != nil {
+ return x.Scope
+ }
+ return nil
+}
+
+func (x *Scopes) SetScope(v map[string]string) {
+ x.Scope = v
+}
+
+type Scopes_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Maps between a name of a scope to a short description of it (as the value
+ // of the property).
+ Scope map[string]string
+}
+
+func (b0 Scopes_builder) Build() *Scopes {
+ m0 := &Scopes{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Scope = b.Scope
+ return m0
+}
+
+// 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file.
+// These properties are not defined by OpenAPIv2, but they are used to control the generation.
+type JSONSchema_FieldConfiguration struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ // Alternative parameter name when used as path parameter. If set, this will
+ // be used as the complete parameter name when this field is used as a path
+ // parameter. Use this to avoid having auto generated path parameter names
+ // for overlapping paths.
+ PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *JSONSchema_FieldConfiguration) Reset() {
+ *x = JSONSchema_FieldConfiguration{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *JSONSchema_FieldConfiguration) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JSONSchema_FieldConfiguration) ProtoMessage() {}
+
+func (x *JSONSchema_FieldConfiguration) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[27]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
+ if x != nil {
+ return x.PathParamName
+ }
+ return ""
+}
+
+func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
+ x.PathParamName = v
+}
+
+type JSONSchema_FieldConfiguration_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Alternative parameter name when used as path parameter. If set, this will
+ // be used as the complete parameter name when this field is used as a path
+ // parameter. Use this to avoid having auto generated path parameter names
+ // for overlapping paths.
+ PathParamName string
+}
+
+func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
+ m0 := &JSONSchema_FieldConfiguration{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.PathParamName = b.PathParamName
+ return m0
+}
+
+// If the security scheme is of type "oauth2", then the value is a list of
+// scope names required for the execution. For other security scheme types,
+// the array MUST be empty.
+type SecurityRequirement_SecurityRequirementValue struct {
+ state protoimpl.MessageState `protogen:"hybrid.v1"`
+ Scope []string `protobuf:"bytes,1,rep,name=scope,proto3" json:"scope,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) Reset() {
+ *x = SecurityRequirement_SecurityRequirementValue{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[32]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityRequirement_SecurityRequirementValue) ProtoMessage() {}
+
+func (x *SecurityRequirement_SecurityRequirementValue) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[32]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) GetScope() []string {
+ if x != nil {
+ return x.Scope
+ }
+ return nil
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) SetScope(v []string) {
+ x.Scope = v
+}
+
+type SecurityRequirement_SecurityRequirementValue_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ Scope []string
+}
+
+func (b0 SecurityRequirement_SecurityRequirementValue_builder) Build() *SecurityRequirement_SecurityRequirementValue {
+ m0 := &SecurityRequirement_SecurityRequirementValue{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.Scope = b.Scope
+ return m0
+}
+
+var File_protoc_gen_openapiv3_options_openapiv3_proto protoreflect.FileDescriptor
+
+var file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc = []byte{
+ 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x08, 0x0a, 0x07, 0x53, 0x77, 0x61, 0x67,
+ 0x67, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, 0x43, 0x0a,
+ 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
+ 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70,
+ 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, 0x50,
+ 0x61, 0x74, 0x68, 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x05,
+ 0x20, 0x03, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73,
+ 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03,
+ 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08,
+ 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,
+ 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09,
+ 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x71, 0x0a, 0x14, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69,
+ 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x13, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5a, 0x0a, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69,
+ 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72,
+ 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73,
+ 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0d,
+ 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44,
+ 0x6f, 0x63, 0x73, 0x12, 0x62, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x71, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52,
+ 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0xd6, 0x07,
+ 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74,
+ 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12,
+ 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0d, 0x65,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f,
+ 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+ 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
+ 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
+ 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x61, 0x0a,
+ 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x43, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73,
+ 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
+ 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63,
+ 0x68, 0x65, 0x6d, 0x65, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a,
+ 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x5a, 0x0a,
+ 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52,
+ 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x64, 0x0a, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e,
+ 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+ 0x55, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61,
+ 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x71, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f,
+ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
+ 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c,
+ 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+ 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
+ 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0x62, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65,
+ 0x74, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
+ 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0xa3, 0x02, 0x0a, 0x0f, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x53, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x0e, 0x32, 0x3f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2e, 0x54,
+ 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72,
+ 0x6d, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x45, 0x0a,
+ 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
+ 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0a,
+ 0x0a, 0x06, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e,
+ 0x54, 0x45, 0x47, 0x45, 0x52, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45,
+ 0x41, 0x4e, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08,
+ 0x22, 0xd8, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a,
+ 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
+ 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66,
+ 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61,
+ 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0d,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x4a, 0x04, 0x08,
+ 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a,
+ 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10,
+ 0x0b, 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08,
+ 0x0e, 0x10, 0x0f, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a,
+ 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x22, 0x9a, 0x05, 0x0a, 0x08,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x5a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
+ 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x73, 0x12, 0x5d, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
+ 0x12, 0x63, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+ 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6d, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd6, 0x03, 0x0a, 0x04, 0x49, 0x6e, 0x66,
+ 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x65, 0x72,
+ 0x6d, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x4f, 0x66, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63,
+ 0x74, 0x12, 0x4c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c,
+ 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12,
+ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x5f, 0x0a, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a,
+ 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x22, 0x45, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+ 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x2f, 0x0a, 0x07, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x4b, 0x0a, 0x15, 0x45, 0x78, 0x74,
+ 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xaa, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x12, 0x56, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0a, 0x6a,
+ 0x73, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x69, 0x73,
+ 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12,
+ 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x65, 0x0a, 0x0d,
+ 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44,
+ 0x6f, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4a, 0x04, 0x08,
+ 0x04, 0x10, 0x05, 0x22, 0xe8, 0x03, 0x0a, 0x0a, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x14,
+ 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
+ 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x65, 0x0a,
+ 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x44, 0x6f, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18,
+ 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x10,
+ 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66,
+ 0x12, 0x65, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
+ 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
+ 0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
+ 0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
+ 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+ 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74,
+ 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d,
+ 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78,
+ 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69,
+ 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65,
+ 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+ 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d,
+ 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28,
+ 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78,
+ 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18,
+ 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65,
+ 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x78,
+ 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e,
+ 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12,
+ 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x14, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09,
+ 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69,
+ 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e,
+ 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x18,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74,
+ 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65,
+ 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e,
+ 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x18,
+ 0x22, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x12, 0x5f, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x4b, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x69, 0x6d, 0x70,
+ 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a,
+ 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x2e, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x7a, 0x0a, 0x13, 0x66, 0x69, 0x65,
+ 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x48, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x46,
+ 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x30, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f,
+ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
+ 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
+ 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
+ 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x22, 0x77, 0x0a, 0x15, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53,
+ 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e,
+ 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x52, 0x41, 0x59,
+ 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x02, 0x12,
+ 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04,
+ 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52,
+ 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x06, 0x12, 0x0a,
+ 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02,
+ 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x12,
+ 0x10, 0x13, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x18, 0x4a, 0x04,
+ 0x08, 0x1b, 0x10, 0x1c, 0x4a, 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x1d, 0x10, 0x1e,
+ 0x4a, 0x04, 0x08, 0x1e, 0x10, 0x22, 0x4a, 0x04, 0x08, 0x25, 0x10, 0x2a, 0x4a, 0x04, 0x08, 0x2a,
+ 0x10, 0x2b, 0x4a, 0x04, 0x08, 0x2b, 0x10, 0x2e, 0x22, 0xd9, 0x02, 0x0a, 0x03, 0x54, 0x61, 0x67,
+ 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
+ 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e,
+ 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x5e, 0x0a,
+ 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61,
+ 0x67, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a,
+ 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+ 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x68, 0x0a, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4c,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69,
+ 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72,
+ 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65,
+ 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x1a, 0x76, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+ 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4f, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
+ 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67,
+ 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68,
+ 0x65, 0x6d, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xff,
+ 0x06, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x65, 0x12, 0x52, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52,
+ 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x02, 0x69,
+ 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x65, 0x2e, 0x49, 0x6e, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x52, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
+ 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x65, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x2b, 0x0a,
+ 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75,
+ 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f,
+ 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74,
+ 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65,
+ 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70,
+ 0x65, 0x73, 0x12, 0x69, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+ 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a,
+ 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+ 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c,
+ 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0e,
+ 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x01, 0x12, 0x10,
+ 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02,
+ 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x41, 0x55, 0x54, 0x48, 0x32, 0x10,
+ 0x03, 0x22, 0x31, 0x0a, 0x02, 0x49, 0x6e, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4e, 0x5f, 0x49, 0x4e,
+ 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4e, 0x5f, 0x51, 0x55,
+ 0x45, 0x52, 0x59, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x48, 0x45, 0x41, 0x44,
+ 0x45, 0x52, 0x10, 0x02, 0x22, 0x6a, 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x10, 0x0a, 0x0c,
+ 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x11,
+ 0x0a, 0x0d, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10,
+ 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f,
+ 0x52, 0x44, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x50, 0x50,
+ 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c,
+ 0x4f, 0x57, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x04,
+ 0x22, 0xf6, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x8a, 0x01, 0x0a, 0x14, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+ 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x52, 0x13, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x30, 0x0a, 0x18, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75,
+ 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x1a, 0x9f, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x6d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x96, 0x01, 0x0a, 0x06, 0x53, 0x63,
+ 0x6f, 0x70, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x1a, 0x38, 0x0a, 0x0a, 0x53, 0x63, 0x6f, 0x70,
+ 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x2a, 0x3b, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x0b, 0x0a, 0x07,
+ 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54,
+ 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x12, 0x06,
+ 0x0a, 0x02, 0x57, 0x53, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x57, 0x53, 0x53, 0x10, 0x04, 0x42,
+ 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72,
+ 0x70, 0x63, 0x2d, 0x65, 0x63, 0x6f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x67, 0x72, 0x70,
+ 0x63, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
+}
+
+var file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
+var file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes = make([]protoimpl.MessageInfo, 35)
+var file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes = []any{
+ (Scheme)(0), // 0: grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ (HeaderParameter_Type)(0), // 1: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.Type
+ (JSONSchema_JSONSchemaSimpleTypes)(0), // 2: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.JSONSchemaSimpleTypes
+ (SecurityScheme_Type)(0), // 3: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Type
+ (SecurityScheme_In)(0), // 4: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.In
+ (SecurityScheme_Flow)(0), // 5: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Flow
+ (*Swagger)(nil), // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ (*Operation)(nil), // 7: grpc.gateway.protoc_gen_openapiv3.options.Operation
+ (*Parameters)(nil), // 8: grpc.gateway.protoc_gen_openapiv3.options.Parameters
+ (*HeaderParameter)(nil), // 9: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter
+ (*Header)(nil), // 10: grpc.gateway.protoc_gen_openapiv3.options.Header
+ (*Response)(nil), // 11: grpc.gateway.protoc_gen_openapiv3.options.Response
+ (*Info)(nil), // 12: grpc.gateway.protoc_gen_openapiv3.options.Info
+ (*Contact)(nil), // 13: grpc.gateway.protoc_gen_openapiv3.options.Contact
+ (*License)(nil), // 14: grpc.gateway.protoc_gen_openapiv3.options.License
+ (*ExternalDocumentation)(nil), // 15: grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ (*Schema)(nil), // 16: grpc.gateway.protoc_gen_openapiv3.options.Schema
+ (*EnumSchema)(nil), // 17: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ (*JSONSchema)(nil), // 18: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ (*Tag)(nil), // 19: grpc.gateway.protoc_gen_openapiv3.options.Tag
+ (*SecurityDefinitions)(nil), // 20: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions
+ (*SecurityScheme)(nil), // 21: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme
+ (*SecurityRequirement)(nil), // 22: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ (*Scopes)(nil), // 23: grpc.gateway.protoc_gen_openapiv3.options.Scopes
+ nil, // 24: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry
+ nil, // 25: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry
+ nil, // 26: grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry
+ nil, // 27: grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry
+ nil, // 28: grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry
+ nil, // 29: grpc.gateway.protoc_gen_openapiv3.options.Response.ExamplesEntry
+ nil, // 30: grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry
+ nil, // 31: grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry
+ nil, // 32: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry
+ (*JSONSchema_FieldConfiguration)(nil), // 33: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.FieldConfiguration
+ nil, // 34: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry
+ nil, // 35: grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry
+ nil, // 36: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry
+ nil, // 37: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry
+ (*SecurityRequirement_SecurityRequirementValue)(nil), // 38: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementValue
+ nil, // 39: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry
+ nil, // 40: grpc.gateway.protoc_gen_openapiv3.options.Scopes.ScopeEntry
+ (*structpb.Value)(nil), // 41: google.protobuf.Value
+}
+var file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs = []int32{
+ 12, // 0: grpc.gateway.protoc_gen_openapiv3.options.Swagger.info:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Info
+ 0, // 1: grpc.gateway.protoc_gen_openapiv3.options.Swagger.schemes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ 24, // 2: grpc.gateway.protoc_gen_openapiv3.options.Swagger.responses:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry
+ 20, // 3: grpc.gateway.protoc_gen_openapiv3.options.Swagger.security_definitions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions
+ 22, // 4: grpc.gateway.protoc_gen_openapiv3.options.Swagger.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ 19, // 5: grpc.gateway.protoc_gen_openapiv3.options.Swagger.tags:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag
+ 15, // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 25, // 7: grpc.gateway.protoc_gen_openapiv3.options.Swagger.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry
+ 15, // 8: grpc.gateway.protoc_gen_openapiv3.options.Operation.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 26, // 9: grpc.gateway.protoc_gen_openapiv3.options.Operation.responses:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry
+ 0, // 10: grpc.gateway.protoc_gen_openapiv3.options.Operation.schemes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ 22, // 11: grpc.gateway.protoc_gen_openapiv3.options.Operation.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ 27, // 12: grpc.gateway.protoc_gen_openapiv3.options.Operation.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry
+ 8, // 13: grpc.gateway.protoc_gen_openapiv3.options.Operation.parameters:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Parameters
+ 9, // 14: grpc.gateway.protoc_gen_openapiv3.options.Parameters.headers:type_name -> grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter
+ 1, // 15: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.Type
+ 16, // 16: grpc.gateway.protoc_gen_openapiv3.options.Response.schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Schema
+ 28, // 17: grpc.gateway.protoc_gen_openapiv3.options.Response.headers:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry
+ 29, // 18: grpc.gateway.protoc_gen_openapiv3.options.Response.examples:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.ExamplesEntry
+ 30, // 19: grpc.gateway.protoc_gen_openapiv3.options.Response.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry
+ 13, // 20: grpc.gateway.protoc_gen_openapiv3.options.Info.contact:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Contact
+ 14, // 21: grpc.gateway.protoc_gen_openapiv3.options.Info.license:type_name -> grpc.gateway.protoc_gen_openapiv3.options.License
+ 31, // 22: grpc.gateway.protoc_gen_openapiv3.options.Info.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry
+ 18, // 23: grpc.gateway.protoc_gen_openapiv3.options.Schema.json_schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ 15, // 24: grpc.gateway.protoc_gen_openapiv3.options.Schema.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 15, // 25: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 32, // 26: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry
+ 2, // 27: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.JSONSchemaSimpleTypes
+ 33, // 28: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.field_configuration:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.FieldConfiguration
+ 34, // 29: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry
+ 15, // 30: grpc.gateway.protoc_gen_openapiv3.options.Tag.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 35, // 31: grpc.gateway.protoc_gen_openapiv3.options.Tag.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry
+ 36, // 32: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry
+ 3, // 33: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Type
+ 4, // 34: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.in:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.In
+ 5, // 35: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.flow:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Flow
+ 23, // 36: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.scopes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scopes
+ 37, // 37: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry
+ 39, // 38: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.security_requirement:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry
+ 40, // 39: grpc.gateway.protoc_gen_openapiv3.options.Scopes.scope:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scopes.ScopeEntry
+ 11, // 40: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response
+ 41, // 41: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 11, // 42: grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response
+ 41, // 43: grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 10, // 44: grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Header
+ 41, // 45: grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 46: grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 47: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 48: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 49: grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 21, // 50: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme
+ 41, // 51: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 38, // 52: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementValue
+ 53, // [53:53] is the sub-list for method output_type
+ 53, // [53:53] is the sub-list for method input_type
+ 53, // [53:53] is the sub-list for extension type_name
+ 53, // [53:53] is the sub-list for extension extendee
+ 0, // [0:53] is the sub-list for field type_name
+}
+
+func init() { file_protoc_gen_openapiv3_options_openapiv3_proto_init() }
+func file_protoc_gen_openapiv3_options_openapiv3_proto_init() {
+ if File_protoc_gen_openapiv3_options_openapiv3_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc,
+ NumEnums: 6,
+ NumMessages: 35,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes,
+ DependencyIndexes: file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs,
+ EnumInfos: file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes,
+ MessageInfos: file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes,
+ }.Build()
+ File_protoc_gen_openapiv3_options_openapiv3_proto = out.File
+ file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc = nil
+ file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes = nil
+ file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs = nil
+}
diff --git a/protoc-gen-openapiv3/options/openapiv3.proto b/protoc-gen-openapiv3/options/openapiv3.proto
new file mode 100644
index 00000000000..a9acfbae237
--- /dev/null
+++ b/protoc-gen-openapiv3/options/openapiv3.proto
@@ -0,0 +1,759 @@
+syntax = "proto3";
+
+package grpc.gateway.protoc_gen_openapiv3.options;
+
+import "google/protobuf/struct.proto";
+
+option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv3/options";
+
+// Scheme describes the schemes supported by the OpenAPI Swagger
+// and Operation objects.
+enum Scheme {
+ UNKNOWN = 0;
+ HTTP = 1;
+ HTTPS = 2;
+ WS = 3;
+ WSS = 4;
+}
+
+// `Swagger` is a representation of OpenAPI v2 specification's Swagger object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// schemes: HTTPS;
+// consumes: "application/json";
+// produces: "application/json";
+// };
+//
+message Swagger {
+ // Specifies the OpenAPI Specification version being used. It can be
+ // used by the OpenAPI UI and other clients to interpret the API listing. The
+ // value MUST be "2.0".
+ string swagger = 1;
+ // Provides metadata about the API. The metadata can be used by the
+ // clients if needed.
+ Info info = 2;
+ // The host (name or ip) serving the API. This MUST be the host only and does
+ // not include the scheme nor sub-paths. It MAY include a port. If the host is
+ // not included, the host serving the documentation is to be used (including
+ // the port). The host does not support path templating.
+ string host = 3;
+ // The base path on which the API is served, which is relative to the host. If
+ // it is not included, the API is served directly under the host. The value
+ // MUST start with a leading slash (/). The basePath does not support path
+ // templating.
+ // Note that using `base_path` does not change the endpoint paths that are
+ // generated in the resulting OpenAPI file. If you wish to use `base_path`
+ // with relatively generated OpenAPI paths, the `base_path` prefix must be
+ // manually removed from your `google.api.http` paths and your code changed to
+ // serve the API from the `base_path`.
+ string base_path = 4;
+ // The transfer protocol of the API. Values MUST be from the list: "http",
+ // "https", "ws", "wss". If the schemes is not included, the default scheme to
+ // be used is the one used to access the OpenAPI definition itself.
+ repeated Scheme schemes = 5;
+ // A list of MIME types the APIs can consume. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ repeated string consumes = 6;
+ // A list of MIME types the APIs can produce. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ repeated string produces = 7;
+ // field 8 is reserved for 'paths'.
+ reserved 8;
+ // field 9 is reserved for 'definitions', which at this time are already
+ // exposed as and customizable as proto messages.
+ reserved 9;
+ // An object to hold responses that can be used across operations. This
+ // property does not define global responses for all operations.
+ map responses = 10;
+ // Security scheme definitions that can be used across the specification.
+ SecurityDefinitions security_definitions = 11;
+ // A declaration of which security schemes are applied for the API as a whole.
+ // The list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements).
+ // Individual operations can override this definition.
+ repeated SecurityRequirement security = 12;
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ repeated Tag tags = 13;
+ // Additional external documentation.
+ ExternalDocumentation external_docs = 14;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 15;
+}
+
+// `Operation` is a representation of OpenAPI v2 specification's Operation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject
+//
+// Example:
+//
+// service EchoService {
+// rpc Echo(SimpleMessage) returns (SimpleMessage) {
+// option (google.api.http) = {
+// get: "/v1/example/echo/{id}"
+// };
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+// summary: "Get a message.";
+// operation_id: "getMessage";
+// tags: "echo";
+// responses: {
+// key: "200"
+// value: {
+// description: "OK";
+// }
+// }
+// };
+// }
+// }
+message Operation {
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ repeated string tags = 1;
+ // A short summary of what the operation does. For maximum readability in the
+ // swagger-ui, this field SHOULD be less than 120 characters.
+ string summary = 2;
+ // A verbose explanation of the operation behavior. GFM syntax can be used for
+ // rich text representation.
+ string description = 3;
+ // Additional external documentation for this operation.
+ ExternalDocumentation external_docs = 4;
+ // Unique string used to identify the operation. The id MUST be unique among
+ // all operations described in the API. Tools and libraries MAY use the
+ // operationId to uniquely identify an operation, therefore, it is recommended
+ // to follow common programming naming conventions.
+ string operation_id = 5;
+ // A list of MIME types the operation can consume. This overrides the consumes
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ repeated string consumes = 6;
+ // A list of MIME types the operation can produce. This overrides the produces
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ repeated string produces = 7;
+ // field 8 is reserved for 'parameters'.
+ reserved 8;
+ // The list of possible responses as they are returned from executing this
+ // operation.
+ map responses = 9;
+ // The transfer protocol for the operation. Values MUST be from the list:
+ // "http", "https", "ws", "wss". The value overrides the OpenAPI Object
+ // schemes definition.
+ repeated Scheme schemes = 10;
+ // Declares this operation to be deprecated. Usage of the declared operation
+ // should be refrained. Default value is false.
+ bool deprecated = 11;
+ // A declaration of which security schemes are applied for this operation. The
+ // list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements). This
+ // definition overrides any declared top-level security. To remove a top-level
+ // security declaration, an empty array can be used.
+ repeated SecurityRequirement security = 12;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 13;
+ // Custom parameters such as HTTP request headers.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/
+ // and https://swagger.io/specification/v2/#parameter-object.
+ Parameters parameters = 14;
+}
+
+// `Parameters` is a representation of OpenAPI v2 specification's parameters object.
+// Note: This technically breaks compatibility with the OpenAPI 2 definition structure as we only
+// allow header parameters to be set here since we do not want users specifying custom non-header
+// parameters beyond those inferred from the Protobuf schema.
+// See: https://swagger.io/specification/v2/#parameter-object
+message Parameters {
+ // `Headers` is one or more HTTP header parameter.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters
+ repeated HeaderParameter headers = 1;
+}
+
+// `HeaderParameter` a HTTP header parameter.
+// See: https://swagger.io/specification/v2/#parameter-object
+message HeaderParameter {
+ // `Type` is a supported HTTP header type.
+ // See https://swagger.io/specification/v2/#parameterType.
+ enum Type {
+ UNKNOWN = 0;
+ STRING = 1;
+ NUMBER = 2;
+ INTEGER = 3;
+ BOOLEAN = 4;
+ }
+
+ // `Name` is the header name.
+ string name = 1;
+ // `Description` is a short description of the header.
+ string description = 2;
+ // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ // See: https://swagger.io/specification/v2/#parameterType.
+ Type type = 3;
+ // `Format` The extending format for the previously mentioned type.
+ string format = 4;
+ // `Required` indicates if the header is optional
+ bool required = 5;
+ // field 6 is reserved for 'items', but in OpenAPI-specific way.
+ reserved 6;
+ // field 7 is reserved `Collection Format`. Determines the format of the array if type array is used.
+ reserved 7;
+}
+
+// `Header` is a representation of OpenAPI v2 specification's Header object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject
+//
+message Header {
+ // `Description` is a short description of the header.
+ string description = 1;
+ // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ string type = 2;
+ // `Format` The extending format for the previously mentioned type.
+ string format = 3;
+ // field 4 is reserved for 'items', but in OpenAPI-specific way.
+ reserved 4;
+ // field 5 is reserved `Collection Format` Determines the format of the array if type array is used.
+ reserved 5;
+ // `Default` Declares the value of the header that the server will use if none is provided.
+ // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2.
+ // Unlike JSON Schema this value MUST conform to the defined type for the header.
+ string default = 6;
+ // field 7 is reserved for 'maximum'.
+ reserved 7;
+ // field 8 is reserved for 'exclusiveMaximum'.
+ reserved 8;
+ // field 9 is reserved for 'minimum'.
+ reserved 9;
+ // field 10 is reserved for 'exclusiveMinimum'.
+ reserved 10;
+ // field 11 is reserved for 'maxLength'.
+ reserved 11;
+ // field 12 is reserved for 'minLength'.
+ reserved 12;
+ // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
+ string pattern = 13;
+ // field 14 is reserved for 'maxItems'.
+ reserved 14;
+ // field 15 is reserved for 'minItems'.
+ reserved 15;
+ // field 16 is reserved for 'uniqueItems'.
+ reserved 16;
+ // field 17 is reserved for 'enum'.
+ reserved 17;
+ // field 18 is reserved for 'multipleOf'.
+ reserved 18;
+}
+
+// `Response` is a representation of OpenAPI v2 specification's Response object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject
+//
+message Response {
+ // `Description` is a short description of the response.
+ // GFM syntax can be used for rich text representation.
+ string description = 1;
+ // `Schema` optionally defines the structure of the response.
+ // If `Schema` is not provided, it means there is no content to the response.
+ Schema schema = 2;
+ // `Headers` A list of headers that are sent with the response.
+ // `Header` name is expected to be a string in the canonical format of the MIME header key
+ // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey
+ map headers = 3;
+ // `Examples` gives per-mimetype response examples.
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object
+ map examples = 4;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 5;
+}
+
+// `Info` is a representation of OpenAPI v2 specification's Info object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// ...
+// };
+//
+message Info {
+ // The title of the application.
+ string title = 1;
+ // A short description of the application. GFM syntax can be used for rich
+ // text representation.
+ string description = 2;
+ // The Terms of Service for the API.
+ string terms_of_service = 3;
+ // The contact information for the exposed API.
+ Contact contact = 4;
+ // The license information for the exposed API.
+ License license = 5;
+ // Provides the version of the application API (not to be confused
+ // with the specification version).
+ string version = 6;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 7;
+}
+
+// `Contact` is a representation of OpenAPI v2 specification's Contact object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// ...
+// };
+// ...
+// };
+//
+message Contact {
+ // The identifying name of the contact person/organization.
+ string name = 1;
+ // The URL pointing to the contact information. MUST be in the format of a
+ // URL.
+ string url = 2;
+ // The email address of the contact person/organization. MUST be in the format
+ // of an email address.
+ string email = 3;
+}
+
+// `License` is a representation of OpenAPI v2 specification's License object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// ...
+// };
+// ...
+// };
+//
+message License {
+ // The license name used for the API.
+ string name = 1;
+ // A URL to the license used for the API. MUST be in the format of a URL.
+ string url = 2;
+}
+
+// `ExternalDocumentation` is a representation of OpenAPI v2 specification's
+// ExternalDocumentation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// ...
+// external_docs: {
+// description: "More about gRPC-Gateway";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// }
+// ...
+// };
+//
+message ExternalDocumentation {
+ // A short description of the target documentation. GFM syntax can be used for
+ // rich text representation.
+ string description = 1;
+ // The URL for the target documentation. Value MUST be in the format
+ // of a URL.
+ string url = 2;
+}
+
+// `Schema` is a representation of OpenAPI v2 specification's Schema object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+message Schema {
+ JSONSchema json_schema = 1;
+ // Adds support for polymorphism. The discriminator is the schema property
+ // name that is used to differentiate between other schema that inherit this
+ // schema. The property name used MUST be defined at this schema and it MUST
+ // be in the required property list. When used, the value MUST be the name of
+ // this schema or any schema that inherits it.
+ string discriminator = 2;
+ // Relevant only for Schema "properties" definitions. Declares the property as
+ // "read only". This means that it MAY be sent as part of a response but MUST
+ // NOT be sent as part of the request. Properties marked as readOnly being
+ // true SHOULD NOT be in the required list of the defined schema. Default
+ // value is false.
+ bool read_only = 3;
+ // field 4 is reserved for 'xml'.
+ reserved 4;
+ // Additional external documentation for this schema.
+ ExternalDocumentation external_docs = 5;
+ // A free-form property to include an example of an instance for this schema in JSON.
+ // This is copied verbatim to the output.
+ string example = 6;
+}
+
+// `EnumSchema` is subset of fields from the OpenAPI v2 specification's Schema object.
+// Only fields that are applicable to Enums are included
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_enum) = {
+// ...
+// title: "MyEnum";
+// description:"This is my nice enum";
+// example: "ZERO";
+// required: true;
+// ...
+// };
+//
+message EnumSchema {
+ // A short description of the schema.
+ string description = 1;
+ string default = 2;
+ // The title of the schema.
+ string title = 3;
+ bool required = 4;
+ bool read_only = 5;
+ // Additional external documentation for this schema.
+ ExternalDocumentation external_docs = 6;
+ string example = 7;
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ // `ref: ".google.protobuf.Timestamp"`.
+ string ref = 8;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 9;
+}
+
+// `JSONSchema` represents properties from JSON Schema taken, and as used, in
+// the OpenAPI v2 spec.
+//
+// This includes changes made by OpenAPI v2.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// See also: https://cswr.github.io/JsonSchema/spec/basic_types/,
+// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json
+//
+// Example:
+//
+// message SimpleMessage {
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+// json_schema: {
+// title: "SimpleMessage"
+// description: "A simple message."
+// required: ["id"]
+// }
+// };
+//
+// // Id represents the message identifier.
+// string id = 1; [
+// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+// description: "The unique identifier of the simple message."
+// }];
+// }
+//
+message JSONSchema {
+ // field 1 is reserved for '$id', omitted from OpenAPI v2.
+ reserved 1;
+ // field 2 is reserved for '$schema', omitted from OpenAPI v2.
+ reserved 2;
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ // `ref: ".google.protobuf.Timestamp"`.
+ string ref = 3;
+ // field 4 is reserved for '$comment', omitted from OpenAPI v2.
+ reserved 4;
+ // The title of the schema.
+ string title = 5;
+ // A short description of the schema.
+ string description = 6;
+ string default = 7;
+ bool read_only = 8;
+ // A free-form property to include a JSON example of this field. This is copied
+ // verbatim to the output swagger.json. Quotes must be escaped.
+ // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+ string example = 9;
+ double multiple_of = 10;
+ // Maximum represents an inclusive upper limit for a numeric instance. The
+ // value of MUST be a number,
+ double maximum = 11;
+ bool exclusive_maximum = 12;
+ // minimum represents an inclusive lower limit for a numeric instance. The
+ // value of MUST be a number,
+ double minimum = 13;
+ bool exclusive_minimum = 14;
+ uint64 max_length = 15;
+ uint64 min_length = 16;
+ string pattern = 17;
+ // field 18 is reserved for 'additionalItems', omitted from OpenAPI v2.
+ reserved 18;
+ // field 19 is reserved for 'items', but in OpenAPI-specific way.
+ // TODO(ivucica): add 'items'?
+ reserved 19;
+ uint64 max_items = 20;
+ uint64 min_items = 21;
+ bool unique_items = 22;
+ // field 23 is reserved for 'contains', omitted from OpenAPI v2.
+ reserved 23;
+ uint64 max_properties = 24;
+ uint64 min_properties = 25;
+ repeated string required = 26;
+ // field 27 is reserved for 'additionalProperties', but in OpenAPI-specific
+ // way. TODO(ivucica): add 'additionalProperties'?
+ reserved 27;
+ // field 28 is reserved for 'definitions', omitted from OpenAPI v2.
+ reserved 28;
+ // field 29 is reserved for 'properties', but in OpenAPI-specific way.
+ // TODO(ivucica): add 'additionalProperties'?
+ reserved 29;
+ // following fields are reserved, as the properties have been omitted from
+ // OpenAPI v2:
+ // patternProperties, dependencies, propertyNames, const
+ reserved 30 to 33;
+ // Items in 'array' must be unique.
+ repeated string array = 34;
+
+ enum JSONSchemaSimpleTypes {
+ UNKNOWN = 0;
+ ARRAY = 1;
+ BOOLEAN = 2;
+ INTEGER = 3;
+ NULL = 4;
+ NUMBER = 5;
+ OBJECT = 6;
+ STRING = 7;
+ }
+
+ repeated JSONSchemaSimpleTypes type = 35;
+ // `Format`
+ string format = 36;
+ // following fields are reserved, as the properties have been omitted from
+ // OpenAPI v2: contentMediaType, contentEncoding, if, then, else
+ reserved 37 to 41;
+ // field 42 is reserved for 'allOf', but in OpenAPI-specific way.
+ // TODO(ivucica): add 'allOf'?
+ reserved 42;
+ // following fields are reserved, as the properties have been omitted from
+ // OpenAPI v2:
+ // anyOf, oneOf, not
+ reserved 43 to 45;
+ // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1
+ repeated string enum = 46;
+
+ // Additional field level properties used when generating the OpenAPI v2 file.
+ FieldConfiguration field_configuration = 1001;
+
+ // 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file.
+ // These properties are not defined by OpenAPIv2, but they are used to control the generation.
+ message FieldConfiguration {
+ // Alternative parameter name when used as path parameter. If set, this will
+ // be used as the complete parameter name when this field is used as a path
+ // parameter. Use this to avoid having auto generated path parameter names
+ // for overlapping paths.
+ string path_param_name = 47;
+ }
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 48;
+}
+
+// `Tag` is a representation of OpenAPI v2 specification's Tag object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject
+//
+message Tag {
+ // The name of the tag. Use it to allow override of the name of a
+ // global Tag object, then use that name to reference the tag throughout the
+ // OpenAPI file.
+ string name = 1;
+ // A short description for the tag. GFM syntax can be used for rich text
+ // representation.
+ string description = 2;
+ // Additional external documentation for this tag.
+ ExternalDocumentation external_docs = 3;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 4;
+}
+
+// `SecurityDefinitions` is a representation of OpenAPI v2 specification's
+// Security Definitions object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject
+//
+// A declaration of the security schemes available to be used in the
+// specification. This does not enforce the security schemes on the operations
+// and only serves to provide the relevant details for each scheme.
+message SecurityDefinitions {
+ // A single security scheme definition, mapping a "name" to the scheme it
+ // defines.
+ map security = 1;
+}
+
+// `SecurityScheme` is a representation of OpenAPI v2 specification's
+// Security Scheme object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject
+//
+// Allows the definition of a security scheme that can be used by the
+// operations. Supported schemes are basic authentication, an API key (either as
+// a header or as a query parameter) and OAuth2's common flows (implicit,
+// password, application and access code).
+message SecurityScheme {
+ // The type of the security scheme. Valid values are "basic",
+ // "apiKey" or "oauth2".
+ enum Type {
+ TYPE_INVALID = 0;
+ TYPE_BASIC = 1;
+ TYPE_API_KEY = 2;
+ TYPE_OAUTH2 = 3;
+ }
+
+ // The location of the API key. Valid values are "query" or "header".
+ enum In {
+ IN_INVALID = 0;
+ IN_QUERY = 1;
+ IN_HEADER = 2;
+ }
+
+ // The flow used by the OAuth2 security scheme. Valid values are
+ // "implicit", "password", "application" or "accessCode".
+ enum Flow {
+ FLOW_INVALID = 0;
+ FLOW_IMPLICIT = 1;
+ FLOW_PASSWORD = 2;
+ FLOW_APPLICATION = 3;
+ FLOW_ACCESS_CODE = 4;
+ }
+
+ // The type of the security scheme. Valid values are "basic",
+ // "apiKey" or "oauth2".
+ Type type = 1;
+ // A short description for security scheme.
+ string description = 2;
+ // The name of the header or query parameter to be used.
+ // Valid for apiKey.
+ string name = 3;
+ // The location of the API key. Valid values are "query" or
+ // "header".
+ // Valid for apiKey.
+ In in = 4;
+ // The flow used by the OAuth2 security scheme. Valid values are
+ // "implicit", "password", "application" or "accessCode".
+ // Valid for oauth2.
+ Flow flow = 5;
+ // The authorization URL to be used for this flow. This SHOULD be in
+ // the form of a URL.
+ // Valid for oauth2/implicit and oauth2/accessCode.
+ string authorization_url = 6;
+ // The token URL to be used for this flow. This SHOULD be in the
+ // form of a URL.
+ // Valid for oauth2/password, oauth2/application and oauth2/accessCode.
+ string token_url = 7;
+ // The available scopes for the OAuth2 security scheme.
+ // Valid for oauth2.
+ Scopes scopes = 8;
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ map extensions = 9;
+}
+
+// `SecurityRequirement` is a representation of OpenAPI v2 specification's
+// Security Requirement object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject
+//
+// Lists the required security schemes to execute this operation. The object can
+// have multiple security schemes declared in it which are all required (that
+// is, there is a logical AND between the schemes).
+//
+// The name used for each property MUST correspond to a security scheme
+// declared in the Security Definitions.
+message SecurityRequirement {
+ // If the security scheme is of type "oauth2", then the value is a list of
+ // scope names required for the execution. For other security scheme types,
+ // the array MUST be empty.
+ message SecurityRequirementValue {
+ repeated string scope = 1;
+ }
+ // Each name must correspond to a security scheme which is declared in
+ // the Security Definitions. If the security scheme is of type "oauth2",
+ // then the value is a list of scope names required for the execution.
+ // For other security scheme types, the array MUST be empty.
+ map security_requirement = 1;
+}
+
+// `Scopes` is a representation of OpenAPI v2 specification's Scopes object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject
+//
+// Lists the available scopes for an OAuth2 security scheme.
+message Scopes {
+ // Maps between a name of a scope to a short description of it (as the value
+ // of the property).
+ map scope = 1;
+}
diff --git a/protoc-gen-openapiv3/options/openapiv3.swagger.json b/protoc-gen-openapiv3/options/openapiv3.swagger.json
new file mode 100644
index 00000000000..96c133b92ca
--- /dev/null
+++ b/protoc-gen-openapiv3/options/openapiv3.swagger.json
@@ -0,0 +1,44 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "title": "protoc-gen-openapiv3/options/openapiv3.proto",
+ "version": "version not set"
+ },
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "paths": {},
+ "definitions": {
+ "protobufAny": {
+ "type": "object",
+ "properties": {
+ "@type": {
+ "type": "string"
+ }
+ },
+ "additionalProperties": {}
+ },
+ "rpcStatus": {
+ "type": "object",
+ "properties": {
+ "code": {
+ "type": "integer",
+ "format": "int32"
+ },
+ "message": {
+ "type": "string"
+ },
+ "details": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/protobufAny"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/protoc-gen-openapiv3/options/openapiv3_protoopaque.pb.go b/protoc-gen-openapiv3/options/openapiv3_protoopaque.pb.go
new file mode 100644
index 00000000000..85f40fce86f
--- /dev/null
+++ b/protoc-gen-openapiv3/options/openapiv3_protoopaque.pb.go
@@ -0,0 +1,4055 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.36.0
+// protoc (unknown)
+// source: protoc-gen-openapiv3/options/openapiv3.proto
+
+//go:build protoopaque
+
+package options
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ structpb "google.golang.org/protobuf/types/known/structpb"
+ reflect "reflect"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Scheme describes the schemes supported by the OpenAPI Swagger
+// and Operation objects.
+type Scheme int32
+
+const (
+ Scheme_UNKNOWN Scheme = 0
+ Scheme_HTTP Scheme = 1
+ Scheme_HTTPS Scheme = 2
+ Scheme_WS Scheme = 3
+ Scheme_WSS Scheme = 4
+)
+
+// Enum value maps for Scheme.
+var (
+ Scheme_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "HTTP",
+ 2: "HTTPS",
+ 3: "WS",
+ 4: "WSS",
+ }
+ Scheme_value = map[string]int32{
+ "UNKNOWN": 0,
+ "HTTP": 1,
+ "HTTPS": 2,
+ "WS": 3,
+ "WSS": 4,
+ }
+)
+
+func (x Scheme) Enum() *Scheme {
+ p := new(Scheme)
+ *p = x
+ return p
+}
+
+func (x Scheme) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (Scheme) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[0].Descriptor()
+}
+
+func (Scheme) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[0]
+}
+
+func (x Scheme) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// `Type` is a supported HTTP header type.
+// See https://swagger.io/specification/v2/#parameterType.
+type HeaderParameter_Type int32
+
+const (
+ HeaderParameter_UNKNOWN HeaderParameter_Type = 0
+ HeaderParameter_STRING HeaderParameter_Type = 1
+ HeaderParameter_NUMBER HeaderParameter_Type = 2
+ HeaderParameter_INTEGER HeaderParameter_Type = 3
+ HeaderParameter_BOOLEAN HeaderParameter_Type = 4
+)
+
+// Enum value maps for HeaderParameter_Type.
+var (
+ HeaderParameter_Type_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "STRING",
+ 2: "NUMBER",
+ 3: "INTEGER",
+ 4: "BOOLEAN",
+ }
+ HeaderParameter_Type_value = map[string]int32{
+ "UNKNOWN": 0,
+ "STRING": 1,
+ "NUMBER": 2,
+ "INTEGER": 3,
+ "BOOLEAN": 4,
+ }
+)
+
+func (x HeaderParameter_Type) Enum() *HeaderParameter_Type {
+ p := new(HeaderParameter_Type)
+ *p = x
+ return p
+}
+
+func (x HeaderParameter_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (HeaderParameter_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[1].Descriptor()
+}
+
+func (HeaderParameter_Type) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[1]
+}
+
+func (x HeaderParameter_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+type JSONSchema_JSONSchemaSimpleTypes int32
+
+const (
+ JSONSchema_UNKNOWN JSONSchema_JSONSchemaSimpleTypes = 0
+ JSONSchema_ARRAY JSONSchema_JSONSchemaSimpleTypes = 1
+ JSONSchema_BOOLEAN JSONSchema_JSONSchemaSimpleTypes = 2
+ JSONSchema_INTEGER JSONSchema_JSONSchemaSimpleTypes = 3
+ JSONSchema_NULL JSONSchema_JSONSchemaSimpleTypes = 4
+ JSONSchema_NUMBER JSONSchema_JSONSchemaSimpleTypes = 5
+ JSONSchema_OBJECT JSONSchema_JSONSchemaSimpleTypes = 6
+ JSONSchema_STRING JSONSchema_JSONSchemaSimpleTypes = 7
+)
+
+// Enum value maps for JSONSchema_JSONSchemaSimpleTypes.
+var (
+ JSONSchema_JSONSchemaSimpleTypes_name = map[int32]string{
+ 0: "UNKNOWN",
+ 1: "ARRAY",
+ 2: "BOOLEAN",
+ 3: "INTEGER",
+ 4: "NULL",
+ 5: "NUMBER",
+ 6: "OBJECT",
+ 7: "STRING",
+ }
+ JSONSchema_JSONSchemaSimpleTypes_value = map[string]int32{
+ "UNKNOWN": 0,
+ "ARRAY": 1,
+ "BOOLEAN": 2,
+ "INTEGER": 3,
+ "NULL": 4,
+ "NUMBER": 5,
+ "OBJECT": 6,
+ "STRING": 7,
+ }
+)
+
+func (x JSONSchema_JSONSchemaSimpleTypes) Enum() *JSONSchema_JSONSchemaSimpleTypes {
+ p := new(JSONSchema_JSONSchemaSimpleTypes)
+ *p = x
+ return p
+}
+
+func (x JSONSchema_JSONSchemaSimpleTypes) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (JSONSchema_JSONSchemaSimpleTypes) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[2].Descriptor()
+}
+
+func (JSONSchema_JSONSchemaSimpleTypes) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[2]
+}
+
+func (x JSONSchema_JSONSchemaSimpleTypes) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The type of the security scheme. Valid values are "basic",
+// "apiKey" or "oauth2".
+type SecurityScheme_Type int32
+
+const (
+ SecurityScheme_TYPE_INVALID SecurityScheme_Type = 0
+ SecurityScheme_TYPE_BASIC SecurityScheme_Type = 1
+ SecurityScheme_TYPE_API_KEY SecurityScheme_Type = 2
+ SecurityScheme_TYPE_OAUTH2 SecurityScheme_Type = 3
+)
+
+// Enum value maps for SecurityScheme_Type.
+var (
+ SecurityScheme_Type_name = map[int32]string{
+ 0: "TYPE_INVALID",
+ 1: "TYPE_BASIC",
+ 2: "TYPE_API_KEY",
+ 3: "TYPE_OAUTH2",
+ }
+ SecurityScheme_Type_value = map[string]int32{
+ "TYPE_INVALID": 0,
+ "TYPE_BASIC": 1,
+ "TYPE_API_KEY": 2,
+ "TYPE_OAUTH2": 3,
+ }
+)
+
+func (x SecurityScheme_Type) Enum() *SecurityScheme_Type {
+ p := new(SecurityScheme_Type)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_Type) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_Type) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[3].Descriptor()
+}
+
+func (SecurityScheme_Type) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[3]
+}
+
+func (x SecurityScheme_Type) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The location of the API key. Valid values are "query" or "header".
+type SecurityScheme_In int32
+
+const (
+ SecurityScheme_IN_INVALID SecurityScheme_In = 0
+ SecurityScheme_IN_QUERY SecurityScheme_In = 1
+ SecurityScheme_IN_HEADER SecurityScheme_In = 2
+)
+
+// Enum value maps for SecurityScheme_In.
+var (
+ SecurityScheme_In_name = map[int32]string{
+ 0: "IN_INVALID",
+ 1: "IN_QUERY",
+ 2: "IN_HEADER",
+ }
+ SecurityScheme_In_value = map[string]int32{
+ "IN_INVALID": 0,
+ "IN_QUERY": 1,
+ "IN_HEADER": 2,
+ }
+)
+
+func (x SecurityScheme_In) Enum() *SecurityScheme_In {
+ p := new(SecurityScheme_In)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_In) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_In) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[4].Descriptor()
+}
+
+func (SecurityScheme_In) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[4]
+}
+
+func (x SecurityScheme_In) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// The flow used by the OAuth2 security scheme. Valid values are
+// "implicit", "password", "application" or "accessCode".
+type SecurityScheme_Flow int32
+
+const (
+ SecurityScheme_FLOW_INVALID SecurityScheme_Flow = 0
+ SecurityScheme_FLOW_IMPLICIT SecurityScheme_Flow = 1
+ SecurityScheme_FLOW_PASSWORD SecurityScheme_Flow = 2
+ SecurityScheme_FLOW_APPLICATION SecurityScheme_Flow = 3
+ SecurityScheme_FLOW_ACCESS_CODE SecurityScheme_Flow = 4
+)
+
+// Enum value maps for SecurityScheme_Flow.
+var (
+ SecurityScheme_Flow_name = map[int32]string{
+ 0: "FLOW_INVALID",
+ 1: "FLOW_IMPLICIT",
+ 2: "FLOW_PASSWORD",
+ 3: "FLOW_APPLICATION",
+ 4: "FLOW_ACCESS_CODE",
+ }
+ SecurityScheme_Flow_value = map[string]int32{
+ "FLOW_INVALID": 0,
+ "FLOW_IMPLICIT": 1,
+ "FLOW_PASSWORD": 2,
+ "FLOW_APPLICATION": 3,
+ "FLOW_ACCESS_CODE": 4,
+ }
+)
+
+func (x SecurityScheme_Flow) Enum() *SecurityScheme_Flow {
+ p := new(SecurityScheme_Flow)
+ *p = x
+ return p
+}
+
+func (x SecurityScheme_Flow) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (SecurityScheme_Flow) Descriptor() protoreflect.EnumDescriptor {
+ return file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[5].Descriptor()
+}
+
+func (SecurityScheme_Flow) Type() protoreflect.EnumType {
+ return &file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes[5]
+}
+
+func (x SecurityScheme_Flow) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// `Swagger` is a representation of OpenAPI v2 specification's Swagger object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#swaggerObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// schemes: HTTPS;
+// consumes: "application/json";
+// produces: "application/json";
+// };
+type Swagger struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Swagger string `protobuf:"bytes,1,opt,name=swagger,proto3" json:"swagger,omitempty"`
+ xxx_hidden_Info *Info `protobuf:"bytes,2,opt,name=info,proto3" json:"info,omitempty"`
+ xxx_hidden_Host string `protobuf:"bytes,3,opt,name=host,proto3" json:"host,omitempty"`
+ xxx_hidden_BasePath string `protobuf:"bytes,4,opt,name=base_path,json=basePath,proto3" json:"base_path,omitempty"`
+ xxx_hidden_Schemes []Scheme `protobuf:"varint,5,rep,packed,name=schemes,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.Scheme" json:"schemes,omitempty"`
+ xxx_hidden_Consumes []string `protobuf:"bytes,6,rep,name=consumes,proto3" json:"consumes,omitempty"`
+ xxx_hidden_Produces []string `protobuf:"bytes,7,rep,name=produces,proto3" json:"produces,omitempty"`
+ xxx_hidden_Responses map[string]*Response `protobuf:"bytes,10,rep,name=responses,proto3" json:"responses,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ xxx_hidden_SecurityDefinitions *SecurityDefinitions `protobuf:"bytes,11,opt,name=security_definitions,json=securityDefinitions,proto3" json:"security_definitions,omitempty"`
+ xxx_hidden_Security *[]*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"`
+ xxx_hidden_Tags *[]*Tag `protobuf:"bytes,13,rep,name=tags,proto3" json:"tags,omitempty"`
+ xxx_hidden_ExternalDocs *ExternalDocumentation `protobuf:"bytes,14,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,15,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Swagger) Reset() {
+ *x = Swagger{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Swagger) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Swagger) ProtoMessage() {}
+
+func (x *Swagger) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[0]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Swagger) GetSwagger() string {
+ if x != nil {
+ return x.xxx_hidden_Swagger
+ }
+ return ""
+}
+
+func (x *Swagger) GetInfo() *Info {
+ if x != nil {
+ return x.xxx_hidden_Info
+ }
+ return nil
+}
+
+func (x *Swagger) GetHost() string {
+ if x != nil {
+ return x.xxx_hidden_Host
+ }
+ return ""
+}
+
+func (x *Swagger) GetBasePath() string {
+ if x != nil {
+ return x.xxx_hidden_BasePath
+ }
+ return ""
+}
+
+func (x *Swagger) GetSchemes() []Scheme {
+ if x != nil {
+ return x.xxx_hidden_Schemes
+ }
+ return nil
+}
+
+func (x *Swagger) GetConsumes() []string {
+ if x != nil {
+ return x.xxx_hidden_Consumes
+ }
+ return nil
+}
+
+func (x *Swagger) GetProduces() []string {
+ if x != nil {
+ return x.xxx_hidden_Produces
+ }
+ return nil
+}
+
+func (x *Swagger) GetResponses() map[string]*Response {
+ if x != nil {
+ return x.xxx_hidden_Responses
+ }
+ return nil
+}
+
+func (x *Swagger) GetSecurityDefinitions() *SecurityDefinitions {
+ if x != nil {
+ return x.xxx_hidden_SecurityDefinitions
+ }
+ return nil
+}
+
+func (x *Swagger) GetSecurity() []*SecurityRequirement {
+ if x != nil {
+ if x.xxx_hidden_Security != nil {
+ return *x.xxx_hidden_Security
+ }
+ }
+ return nil
+}
+
+func (x *Swagger) GetTags() []*Tag {
+ if x != nil {
+ if x.xxx_hidden_Tags != nil {
+ return *x.xxx_hidden_Tags
+ }
+ }
+ return nil
+}
+
+func (x *Swagger) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.xxx_hidden_ExternalDocs
+ }
+ return nil
+}
+
+func (x *Swagger) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *Swagger) SetSwagger(v string) {
+ x.xxx_hidden_Swagger = v
+}
+
+func (x *Swagger) SetInfo(v *Info) {
+ x.xxx_hidden_Info = v
+}
+
+func (x *Swagger) SetHost(v string) {
+ x.xxx_hidden_Host = v
+}
+
+func (x *Swagger) SetBasePath(v string) {
+ x.xxx_hidden_BasePath = v
+}
+
+func (x *Swagger) SetSchemes(v []Scheme) {
+ x.xxx_hidden_Schemes = v
+}
+
+func (x *Swagger) SetConsumes(v []string) {
+ x.xxx_hidden_Consumes = v
+}
+
+func (x *Swagger) SetProduces(v []string) {
+ x.xxx_hidden_Produces = v
+}
+
+func (x *Swagger) SetResponses(v map[string]*Response) {
+ x.xxx_hidden_Responses = v
+}
+
+func (x *Swagger) SetSecurityDefinitions(v *SecurityDefinitions) {
+ x.xxx_hidden_SecurityDefinitions = v
+}
+
+func (x *Swagger) SetSecurity(v []*SecurityRequirement) {
+ x.xxx_hidden_Security = &v
+}
+
+func (x *Swagger) SetTags(v []*Tag) {
+ x.xxx_hidden_Tags = &v
+}
+
+func (x *Swagger) SetExternalDocs(v *ExternalDocumentation) {
+ x.xxx_hidden_ExternalDocs = v
+}
+
+func (x *Swagger) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *Swagger) HasInfo() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_Info != nil
+}
+
+func (x *Swagger) HasSecurityDefinitions() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_SecurityDefinitions != nil
+}
+
+func (x *Swagger) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_ExternalDocs != nil
+}
+
+func (x *Swagger) ClearInfo() {
+ x.xxx_hidden_Info = nil
+}
+
+func (x *Swagger) ClearSecurityDefinitions() {
+ x.xxx_hidden_SecurityDefinitions = nil
+}
+
+func (x *Swagger) ClearExternalDocs() {
+ x.xxx_hidden_ExternalDocs = nil
+}
+
+type Swagger_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Specifies the OpenAPI Specification version being used. It can be
+ // used by the OpenAPI UI and other clients to interpret the API listing. The
+ // value MUST be "2.0".
+ Swagger string
+ // Provides metadata about the API. The metadata can be used by the
+ // clients if needed.
+ Info *Info
+ // The host (name or ip) serving the API. This MUST be the host only and does
+ // not include the scheme nor sub-paths. It MAY include a port. If the host is
+ // not included, the host serving the documentation is to be used (including
+ // the port). The host does not support path templating.
+ Host string
+ // The base path on which the API is served, which is relative to the host. If
+ // it is not included, the API is served directly under the host. The value
+ // MUST start with a leading slash (/). The basePath does not support path
+ // templating.
+ // Note that using `base_path` does not change the endpoint paths that are
+ // generated in the resulting OpenAPI file. If you wish to use `base_path`
+ // with relatively generated OpenAPI paths, the `base_path` prefix must be
+ // manually removed from your `google.api.http` paths and your code changed to
+ // serve the API from the `base_path`.
+ BasePath string
+ // The transfer protocol of the API. Values MUST be from the list: "http",
+ // "https", "ws", "wss". If the schemes is not included, the default scheme to
+ // be used is the one used to access the OpenAPI definition itself.
+ Schemes []Scheme
+ // A list of MIME types the APIs can consume. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Consumes []string
+ // A list of MIME types the APIs can produce. This is global to all APIs but
+ // can be overridden on specific API calls. Value MUST be as described under
+ // Mime Types.
+ Produces []string
+ // An object to hold responses that can be used across operations. This
+ // property does not define global responses for all operations.
+ Responses map[string]*Response
+ // Security scheme definitions that can be used across the specification.
+ SecurityDefinitions *SecurityDefinitions
+ // A declaration of which security schemes are applied for the API as a whole.
+ // The list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements).
+ // Individual operations can override this definition.
+ Security []*SecurityRequirement
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []*Tag
+ // Additional external documentation.
+ ExternalDocs *ExternalDocumentation
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Swagger_builder) Build() *Swagger {
+ m0 := &Swagger{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Swagger = b.Swagger
+ x.xxx_hidden_Info = b.Info
+ x.xxx_hidden_Host = b.Host
+ x.xxx_hidden_BasePath = b.BasePath
+ x.xxx_hidden_Schemes = b.Schemes
+ x.xxx_hidden_Consumes = b.Consumes
+ x.xxx_hidden_Produces = b.Produces
+ x.xxx_hidden_Responses = b.Responses
+ x.xxx_hidden_SecurityDefinitions = b.SecurityDefinitions
+ x.xxx_hidden_Security = &b.Security
+ x.xxx_hidden_Tags = &b.Tags
+ x.xxx_hidden_ExternalDocs = b.ExternalDocs
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `Operation` is a representation of OpenAPI v2 specification's Operation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#operationObject
+//
+// Example:
+//
+// service EchoService {
+// rpc Echo(SimpleMessage) returns (SimpleMessage) {
+// option (google.api.http) = {
+// get: "/v1/example/echo/{id}"
+// };
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
+// summary: "Get a message.";
+// operation_id: "getMessage";
+// tags: "echo";
+// responses: {
+// key: "200"
+// value: {
+// description: "OK";
+// }
+// }
+// };
+// }
+// }
+type Operation struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Tags []string `protobuf:"bytes,1,rep,name=tags,proto3" json:"tags,omitempty"`
+ xxx_hidden_Summary string `protobuf:"bytes,2,opt,name=summary,proto3" json:"summary,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_ExternalDocs *ExternalDocumentation `protobuf:"bytes,4,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ xxx_hidden_OperationId string `protobuf:"bytes,5,opt,name=operation_id,json=operationId,proto3" json:"operation_id,omitempty"`
+ xxx_hidden_Consumes []string `protobuf:"bytes,6,rep,name=consumes,proto3" json:"consumes,omitempty"`
+ xxx_hidden_Produces []string `protobuf:"bytes,7,rep,name=produces,proto3" json:"produces,omitempty"`
+ xxx_hidden_Responses map[string]*Response `protobuf:"bytes,9,rep,name=responses,proto3" json:"responses,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ xxx_hidden_Schemes []Scheme `protobuf:"varint,10,rep,packed,name=schemes,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.Scheme" json:"schemes,omitempty"`
+ xxx_hidden_Deprecated bool `protobuf:"varint,11,opt,name=deprecated,proto3" json:"deprecated,omitempty"`
+ xxx_hidden_Security *[]*SecurityRequirement `protobuf:"bytes,12,rep,name=security,proto3" json:"security,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,13,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ xxx_hidden_Parameters *Parameters `protobuf:"bytes,14,opt,name=parameters,proto3" json:"parameters,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Operation) Reset() {
+ *x = Operation{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Operation) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Operation) ProtoMessage() {}
+
+func (x *Operation) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Operation) GetTags() []string {
+ if x != nil {
+ return x.xxx_hidden_Tags
+ }
+ return nil
+}
+
+func (x *Operation) GetSummary() string {
+ if x != nil {
+ return x.xxx_hidden_Summary
+ }
+ return ""
+}
+
+func (x *Operation) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *Operation) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.xxx_hidden_ExternalDocs
+ }
+ return nil
+}
+
+func (x *Operation) GetOperationId() string {
+ if x != nil {
+ return x.xxx_hidden_OperationId
+ }
+ return ""
+}
+
+func (x *Operation) GetConsumes() []string {
+ if x != nil {
+ return x.xxx_hidden_Consumes
+ }
+ return nil
+}
+
+func (x *Operation) GetProduces() []string {
+ if x != nil {
+ return x.xxx_hidden_Produces
+ }
+ return nil
+}
+
+func (x *Operation) GetResponses() map[string]*Response {
+ if x != nil {
+ return x.xxx_hidden_Responses
+ }
+ return nil
+}
+
+func (x *Operation) GetSchemes() []Scheme {
+ if x != nil {
+ return x.xxx_hidden_Schemes
+ }
+ return nil
+}
+
+func (x *Operation) GetDeprecated() bool {
+ if x != nil {
+ return x.xxx_hidden_Deprecated
+ }
+ return false
+}
+
+func (x *Operation) GetSecurity() []*SecurityRequirement {
+ if x != nil {
+ if x.xxx_hidden_Security != nil {
+ return *x.xxx_hidden_Security
+ }
+ }
+ return nil
+}
+
+func (x *Operation) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *Operation) GetParameters() *Parameters {
+ if x != nil {
+ return x.xxx_hidden_Parameters
+ }
+ return nil
+}
+
+func (x *Operation) SetTags(v []string) {
+ x.xxx_hidden_Tags = v
+}
+
+func (x *Operation) SetSummary(v string) {
+ x.xxx_hidden_Summary = v
+}
+
+func (x *Operation) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *Operation) SetExternalDocs(v *ExternalDocumentation) {
+ x.xxx_hidden_ExternalDocs = v
+}
+
+func (x *Operation) SetOperationId(v string) {
+ x.xxx_hidden_OperationId = v
+}
+
+func (x *Operation) SetConsumes(v []string) {
+ x.xxx_hidden_Consumes = v
+}
+
+func (x *Operation) SetProduces(v []string) {
+ x.xxx_hidden_Produces = v
+}
+
+func (x *Operation) SetResponses(v map[string]*Response) {
+ x.xxx_hidden_Responses = v
+}
+
+func (x *Operation) SetSchemes(v []Scheme) {
+ x.xxx_hidden_Schemes = v
+}
+
+func (x *Operation) SetDeprecated(v bool) {
+ x.xxx_hidden_Deprecated = v
+}
+
+func (x *Operation) SetSecurity(v []*SecurityRequirement) {
+ x.xxx_hidden_Security = &v
+}
+
+func (x *Operation) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *Operation) SetParameters(v *Parameters) {
+ x.xxx_hidden_Parameters = v
+}
+
+func (x *Operation) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_ExternalDocs != nil
+}
+
+func (x *Operation) HasParameters() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_Parameters != nil
+}
+
+func (x *Operation) ClearExternalDocs() {
+ x.xxx_hidden_ExternalDocs = nil
+}
+
+func (x *Operation) ClearParameters() {
+ x.xxx_hidden_Parameters = nil
+}
+
+type Operation_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A list of tags for API documentation control. Tags can be used for logical
+ // grouping of operations by resources or any other qualifier.
+ Tags []string
+ // A short summary of what the operation does. For maximum readability in the
+ // swagger-ui, this field SHOULD be less than 120 characters.
+ Summary string
+ // A verbose explanation of the operation behavior. GFM syntax can be used for
+ // rich text representation.
+ Description string
+ // Additional external documentation for this operation.
+ ExternalDocs *ExternalDocumentation
+ // Unique string used to identify the operation. The id MUST be unique among
+ // all operations described in the API. Tools and libraries MAY use the
+ // operationId to uniquely identify an operation, therefore, it is recommended
+ // to follow common programming naming conventions.
+ OperationId string
+ // A list of MIME types the operation can consume. This overrides the consumes
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Consumes []string
+ // A list of MIME types the operation can produce. This overrides the produces
+ // definition at the OpenAPI Object. An empty value MAY be used to clear the
+ // global definition. Value MUST be as described under Mime Types.
+ Produces []string
+ // The list of possible responses as they are returned from executing this
+ // operation.
+ Responses map[string]*Response
+ // The transfer protocol for the operation. Values MUST be from the list:
+ // "http", "https", "ws", "wss". The value overrides the OpenAPI Object
+ // schemes definition.
+ Schemes []Scheme
+ // Declares this operation to be deprecated. Usage of the declared operation
+ // should be refrained. Default value is false.
+ Deprecated bool
+ // A declaration of which security schemes are applied for this operation. The
+ // list of values describes alternative security schemes that can be used
+ // (that is, there is a logical OR between the security requirements). This
+ // definition overrides any declared top-level security. To remove a top-level
+ // security declaration, an empty array can be used.
+ Security []*SecurityRequirement
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+ // Custom parameters such as HTTP request headers.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/
+ // and https://swagger.io/specification/v2/#parameter-object.
+ Parameters *Parameters
+}
+
+func (b0 Operation_builder) Build() *Operation {
+ m0 := &Operation{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Tags = b.Tags
+ x.xxx_hidden_Summary = b.Summary
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_ExternalDocs = b.ExternalDocs
+ x.xxx_hidden_OperationId = b.OperationId
+ x.xxx_hidden_Consumes = b.Consumes
+ x.xxx_hidden_Produces = b.Produces
+ x.xxx_hidden_Responses = b.Responses
+ x.xxx_hidden_Schemes = b.Schemes
+ x.xxx_hidden_Deprecated = b.Deprecated
+ x.xxx_hidden_Security = &b.Security
+ x.xxx_hidden_Extensions = b.Extensions
+ x.xxx_hidden_Parameters = b.Parameters
+ return m0
+}
+
+// `Parameters` is a representation of OpenAPI v2 specification's parameters object.
+// Note: This technically breaks compatibility with the OpenAPI 2 definition structure as we only
+// allow header parameters to be set here since we do not want users specifying custom non-header
+// parameters beyond those inferred from the Protobuf schema.
+// See: https://swagger.io/specification/v2/#parameter-object
+type Parameters struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Headers *[]*HeaderParameter `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Parameters) Reset() {
+ *x = Parameters{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Parameters) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Parameters) ProtoMessage() {}
+
+func (x *Parameters) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Parameters) GetHeaders() []*HeaderParameter {
+ if x != nil {
+ if x.xxx_hidden_Headers != nil {
+ return *x.xxx_hidden_Headers
+ }
+ }
+ return nil
+}
+
+func (x *Parameters) SetHeaders(v []*HeaderParameter) {
+ x.xxx_hidden_Headers = &v
+}
+
+type Parameters_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Headers` is one or more HTTP header parameter.
+ // See: https://swagger.io/docs/specification/2-0/describing-parameters/#header-parameters
+ Headers []*HeaderParameter
+}
+
+func (b0 Parameters_builder) Build() *Parameters {
+ m0 := &Parameters{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Headers = &b.Headers
+ return m0
+}
+
+// `HeaderParameter` a HTTP header parameter.
+// See: https://swagger.io/specification/v2/#parameter-object
+type HeaderParameter struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Type HeaderParameter_Type `protobuf:"varint,3,opt,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter_Type" json:"type,omitempty"`
+ xxx_hidden_Format string `protobuf:"bytes,4,opt,name=format,proto3" json:"format,omitempty"`
+ xxx_hidden_Required bool `protobuf:"varint,5,opt,name=required,proto3" json:"required,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *HeaderParameter) Reset() {
+ *x = HeaderParameter{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *HeaderParameter) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*HeaderParameter) ProtoMessage() {}
+
+func (x *HeaderParameter) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[3]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *HeaderParameter) GetName() string {
+ if x != nil {
+ return x.xxx_hidden_Name
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetType() HeaderParameter_Type {
+ if x != nil {
+ return x.xxx_hidden_Type
+ }
+ return HeaderParameter_UNKNOWN
+}
+
+func (x *HeaderParameter) GetFormat() string {
+ if x != nil {
+ return x.xxx_hidden_Format
+ }
+ return ""
+}
+
+func (x *HeaderParameter) GetRequired() bool {
+ if x != nil {
+ return x.xxx_hidden_Required
+ }
+ return false
+}
+
+func (x *HeaderParameter) SetName(v string) {
+ x.xxx_hidden_Name = v
+}
+
+func (x *HeaderParameter) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *HeaderParameter) SetType(v HeaderParameter_Type) {
+ x.xxx_hidden_Type = v
+}
+
+func (x *HeaderParameter) SetFormat(v string) {
+ x.xxx_hidden_Format = v
+}
+
+func (x *HeaderParameter) SetRequired(v bool) {
+ x.xxx_hidden_Required = v
+}
+
+type HeaderParameter_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Name` is the header name.
+ Name string
+ // `Description` is a short description of the header.
+ Description string
+ // `Type` is the type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ // See: https://swagger.io/specification/v2/#parameterType.
+ Type HeaderParameter_Type
+ // `Format` The extending format for the previously mentioned type.
+ Format string
+ // `Required` indicates if the header is optional
+ Required bool
+}
+
+func (b0 HeaderParameter_builder) Build() *HeaderParameter {
+ m0 := &HeaderParameter{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Name = b.Name
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Type = b.Type
+ x.xxx_hidden_Format = b.Format
+ x.xxx_hidden_Required = b.Required
+ return m0
+}
+
+// `Header` is a representation of OpenAPI v2 specification's Header object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject
+type Header struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
+ xxx_hidden_Format string `protobuf:"bytes,3,opt,name=format,proto3" json:"format,omitempty"`
+ xxx_hidden_Default string `protobuf:"bytes,6,opt,name=default,proto3" json:"default,omitempty"`
+ xxx_hidden_Pattern string `protobuf:"bytes,13,opt,name=pattern,proto3" json:"pattern,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Header) Reset() {
+ *x = Header{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Header) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Header) ProtoMessage() {}
+
+func (x *Header) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[4]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Header) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *Header) GetType() string {
+ if x != nil {
+ return x.xxx_hidden_Type
+ }
+ return ""
+}
+
+func (x *Header) GetFormat() string {
+ if x != nil {
+ return x.xxx_hidden_Format
+ }
+ return ""
+}
+
+func (x *Header) GetDefault() string {
+ if x != nil {
+ return x.xxx_hidden_Default
+ }
+ return ""
+}
+
+func (x *Header) GetPattern() string {
+ if x != nil {
+ return x.xxx_hidden_Pattern
+ }
+ return ""
+}
+
+func (x *Header) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *Header) SetType(v string) {
+ x.xxx_hidden_Type = v
+}
+
+func (x *Header) SetFormat(v string) {
+ x.xxx_hidden_Format = v
+}
+
+func (x *Header) SetDefault(v string) {
+ x.xxx_hidden_Default = v
+}
+
+func (x *Header) SetPattern(v string) {
+ x.xxx_hidden_Pattern = v
+}
+
+type Header_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Description` is a short description of the header.
+ Description string
+ // The type of the object. The value MUST be one of "string", "number", "integer", or "boolean". The "array" type is not supported.
+ Type string
+ // `Format` The extending format for the previously mentioned type.
+ Format string
+ // `Default` Declares the value of the header that the server will use if none is provided.
+ // See: https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-6.2.
+ // Unlike JSON Schema this value MUST conform to the defined type for the header.
+ Default string
+ // 'Pattern' See https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.2.3.
+ Pattern string
+}
+
+func (b0 Header_builder) Build() *Header {
+ m0 := &Header{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Type = b.Type
+ x.xxx_hidden_Format = b.Format
+ x.xxx_hidden_Default = b.Default
+ x.xxx_hidden_Pattern = b.Pattern
+ return m0
+}
+
+// `Response` is a representation of OpenAPI v2 specification's Response object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responseObject
+type Response struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Schema *Schema `protobuf:"bytes,2,opt,name=schema,proto3" json:"schema,omitempty"`
+ xxx_hidden_Headers map[string]*Header `protobuf:"bytes,3,rep,name=headers,proto3" json:"headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ xxx_hidden_Examples map[string]string `protobuf:"bytes,4,rep,name=examples,proto3" json:"examples,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,5,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Response) Reset() {
+ *x = Response{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Response) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Response) ProtoMessage() {}
+
+func (x *Response) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[5]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Response) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *Response) GetSchema() *Schema {
+ if x != nil {
+ return x.xxx_hidden_Schema
+ }
+ return nil
+}
+
+func (x *Response) GetHeaders() map[string]*Header {
+ if x != nil {
+ return x.xxx_hidden_Headers
+ }
+ return nil
+}
+
+func (x *Response) GetExamples() map[string]string {
+ if x != nil {
+ return x.xxx_hidden_Examples
+ }
+ return nil
+}
+
+func (x *Response) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *Response) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *Response) SetSchema(v *Schema) {
+ x.xxx_hidden_Schema = v
+}
+
+func (x *Response) SetHeaders(v map[string]*Header) {
+ x.xxx_hidden_Headers = v
+}
+
+func (x *Response) SetExamples(v map[string]string) {
+ x.xxx_hidden_Examples = v
+}
+
+func (x *Response) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *Response) HasSchema() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_Schema != nil
+}
+
+func (x *Response) ClearSchema() {
+ x.xxx_hidden_Schema = nil
+}
+
+type Response_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // `Description` is a short description of the response.
+ // GFM syntax can be used for rich text representation.
+ Description string
+ // `Schema` optionally defines the structure of the response.
+ // If `Schema` is not provided, it means there is no content to the response.
+ Schema *Schema
+ // `Headers` A list of headers that are sent with the response.
+ // `Header` name is expected to be a string in the canonical format of the MIME header key
+ // See: https://golang.org/pkg/net/textproto/#CanonicalMIMEHeaderKey
+ Headers map[string]*Header
+ // `Examples` gives per-mimetype response examples.
+ // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#example-object
+ Examples map[string]string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Response_builder) Build() *Response {
+ m0 := &Response{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Schema = b.Schema
+ x.xxx_hidden_Headers = b.Headers
+ x.xxx_hidden_Examples = b.Examples
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `Info` is a representation of OpenAPI v2 specification's Info object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#infoObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// title: "Echo API";
+// version: "1.0";
+// description: "";
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// };
+// ...
+// };
+type Info struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Title string `protobuf:"bytes,1,opt,name=title,proto3" json:"title,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_TermsOfService string `protobuf:"bytes,3,opt,name=terms_of_service,json=termsOfService,proto3" json:"terms_of_service,omitempty"`
+ xxx_hidden_Contact *Contact `protobuf:"bytes,4,opt,name=contact,proto3" json:"contact,omitempty"`
+ xxx_hidden_License *License `protobuf:"bytes,5,opt,name=license,proto3" json:"license,omitempty"`
+ xxx_hidden_Version string `protobuf:"bytes,6,opt,name=version,proto3" json:"version,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,7,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Info) Reset() {
+ *x = Info{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Info) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Info) ProtoMessage() {}
+
+func (x *Info) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[6]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Info) GetTitle() string {
+ if x != nil {
+ return x.xxx_hidden_Title
+ }
+ return ""
+}
+
+func (x *Info) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *Info) GetTermsOfService() string {
+ if x != nil {
+ return x.xxx_hidden_TermsOfService
+ }
+ return ""
+}
+
+func (x *Info) GetContact() *Contact {
+ if x != nil {
+ return x.xxx_hidden_Contact
+ }
+ return nil
+}
+
+func (x *Info) GetLicense() *License {
+ if x != nil {
+ return x.xxx_hidden_License
+ }
+ return nil
+}
+
+func (x *Info) GetVersion() string {
+ if x != nil {
+ return x.xxx_hidden_Version
+ }
+ return ""
+}
+
+func (x *Info) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *Info) SetTitle(v string) {
+ x.xxx_hidden_Title = v
+}
+
+func (x *Info) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *Info) SetTermsOfService(v string) {
+ x.xxx_hidden_TermsOfService = v
+}
+
+func (x *Info) SetContact(v *Contact) {
+ x.xxx_hidden_Contact = v
+}
+
+func (x *Info) SetLicense(v *License) {
+ x.xxx_hidden_License = v
+}
+
+func (x *Info) SetVersion(v string) {
+ x.xxx_hidden_Version = v
+}
+
+func (x *Info) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *Info) HasContact() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_Contact != nil
+}
+
+func (x *Info) HasLicense() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_License != nil
+}
+
+func (x *Info) ClearContact() {
+ x.xxx_hidden_Contact = nil
+}
+
+func (x *Info) ClearLicense() {
+ x.xxx_hidden_License = nil
+}
+
+type Info_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The title of the application.
+ Title string
+ // A short description of the application. GFM syntax can be used for rich
+ // text representation.
+ Description string
+ // The Terms of Service for the API.
+ TermsOfService string
+ // The contact information for the exposed API.
+ Contact *Contact
+ // The license information for the exposed API.
+ License *License
+ // Provides the version of the application API (not to be confused
+ // with the specification version).
+ Version string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Info_builder) Build() *Info {
+ m0 := &Info{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Title = b.Title
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_TermsOfService = b.TermsOfService
+ x.xxx_hidden_Contact = b.Contact
+ x.xxx_hidden_License = b.License
+ x.xxx_hidden_Version = b.Version
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `Contact` is a representation of OpenAPI v2 specification's Contact object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#contactObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// contact: {
+// name: "gRPC-Gateway project";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// email: "none@example.com";
+// };
+// ...
+// };
+// ...
+// };
+type Contact struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ xxx_hidden_Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ xxx_hidden_Email string `protobuf:"bytes,3,opt,name=email,proto3" json:"email,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Contact) Reset() {
+ *x = Contact{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Contact) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Contact) ProtoMessage() {}
+
+func (x *Contact) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[7]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Contact) GetName() string {
+ if x != nil {
+ return x.xxx_hidden_Name
+ }
+ return ""
+}
+
+func (x *Contact) GetUrl() string {
+ if x != nil {
+ return x.xxx_hidden_Url
+ }
+ return ""
+}
+
+func (x *Contact) GetEmail() string {
+ if x != nil {
+ return x.xxx_hidden_Email
+ }
+ return ""
+}
+
+func (x *Contact) SetName(v string) {
+ x.xxx_hidden_Name = v
+}
+
+func (x *Contact) SetUrl(v string) {
+ x.xxx_hidden_Url = v
+}
+
+func (x *Contact) SetEmail(v string) {
+ x.xxx_hidden_Email = v
+}
+
+type Contact_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The identifying name of the contact person/organization.
+ Name string
+ // The URL pointing to the contact information. MUST be in the format of a
+ // URL.
+ Url string
+ // The email address of the contact person/organization. MUST be in the format
+ // of an email address.
+ Email string
+}
+
+func (b0 Contact_builder) Build() *Contact {
+ m0 := &Contact{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Name = b.Name
+ x.xxx_hidden_Url = b.Url
+ x.xxx_hidden_Email = b.Email
+ return m0
+}
+
+// `License` is a representation of OpenAPI v2 specification's License object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#licenseObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// info: {
+// ...
+// license: {
+// name: "BSD 3-Clause License";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway/blob/main/LICENSE";
+// };
+// ...
+// };
+// ...
+// };
+type License struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ xxx_hidden_Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *License) Reset() {
+ *x = License{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *License) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*License) ProtoMessage() {}
+
+func (x *License) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[8]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *License) GetName() string {
+ if x != nil {
+ return x.xxx_hidden_Name
+ }
+ return ""
+}
+
+func (x *License) GetUrl() string {
+ if x != nil {
+ return x.xxx_hidden_Url
+ }
+ return ""
+}
+
+func (x *License) SetName(v string) {
+ x.xxx_hidden_Name = v
+}
+
+func (x *License) SetUrl(v string) {
+ x.xxx_hidden_Url = v
+}
+
+type License_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The license name used for the API.
+ Name string
+ // A URL to the license used for the API. MUST be in the format of a URL.
+ Url string
+}
+
+func (b0 License_builder) Build() *License {
+ m0 := &License{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Name = b.Name
+ x.xxx_hidden_Url = b.Url
+ return m0
+}
+
+// `ExternalDocumentation` is a representation of OpenAPI v2 specification's
+// ExternalDocumentation object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#externalDocumentationObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_swagger) = {
+// ...
+// external_docs: {
+// description: "More about gRPC-Gateway";
+// url: "https://github.com/grpc-ecosystem/grpc-gateway";
+// }
+// ...
+// };
+type ExternalDocumentation struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ExternalDocumentation) Reset() {
+ *x = ExternalDocumentation{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ExternalDocumentation) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ExternalDocumentation) ProtoMessage() {}
+
+func (x *ExternalDocumentation) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[9]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *ExternalDocumentation) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *ExternalDocumentation) GetUrl() string {
+ if x != nil {
+ return x.xxx_hidden_Url
+ }
+ return ""
+}
+
+func (x *ExternalDocumentation) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *ExternalDocumentation) SetUrl(v string) {
+ x.xxx_hidden_Url = v
+}
+
+type ExternalDocumentation_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A short description of the target documentation. GFM syntax can be used for
+ // rich text representation.
+ Description string
+ // The URL for the target documentation. Value MUST be in the format
+ // of a URL.
+ Url string
+}
+
+func (b0 ExternalDocumentation_builder) Build() *ExternalDocumentation {
+ m0 := &ExternalDocumentation{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Url = b.Url
+ return m0
+}
+
+// `Schema` is a representation of OpenAPI v2 specification's Schema object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+type Schema struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_JsonSchema *JSONSchema `protobuf:"bytes,1,opt,name=json_schema,json=jsonSchema,proto3" json:"json_schema,omitempty"`
+ xxx_hidden_Discriminator string `protobuf:"bytes,2,opt,name=discriminator,proto3" json:"discriminator,omitempty"`
+ xxx_hidden_ReadOnly bool `protobuf:"varint,3,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ xxx_hidden_ExternalDocs *ExternalDocumentation `protobuf:"bytes,5,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ xxx_hidden_Example string `protobuf:"bytes,6,opt,name=example,proto3" json:"example,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Schema) Reset() {
+ *x = Schema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Schema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Schema) ProtoMessage() {}
+
+func (x *Schema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[10]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Schema) GetJsonSchema() *JSONSchema {
+ if x != nil {
+ return x.xxx_hidden_JsonSchema
+ }
+ return nil
+}
+
+func (x *Schema) GetDiscriminator() string {
+ if x != nil {
+ return x.xxx_hidden_Discriminator
+ }
+ return ""
+}
+
+func (x *Schema) GetReadOnly() bool {
+ if x != nil {
+ return x.xxx_hidden_ReadOnly
+ }
+ return false
+}
+
+func (x *Schema) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.xxx_hidden_ExternalDocs
+ }
+ return nil
+}
+
+func (x *Schema) GetExample() string {
+ if x != nil {
+ return x.xxx_hidden_Example
+ }
+ return ""
+}
+
+func (x *Schema) SetJsonSchema(v *JSONSchema) {
+ x.xxx_hidden_JsonSchema = v
+}
+
+func (x *Schema) SetDiscriminator(v string) {
+ x.xxx_hidden_Discriminator = v
+}
+
+func (x *Schema) SetReadOnly(v bool) {
+ x.xxx_hidden_ReadOnly = v
+}
+
+func (x *Schema) SetExternalDocs(v *ExternalDocumentation) {
+ x.xxx_hidden_ExternalDocs = v
+}
+
+func (x *Schema) SetExample(v string) {
+ x.xxx_hidden_Example = v
+}
+
+func (x *Schema) HasJsonSchema() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_JsonSchema != nil
+}
+
+func (x *Schema) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_ExternalDocs != nil
+}
+
+func (x *Schema) ClearJsonSchema() {
+ x.xxx_hidden_JsonSchema = nil
+}
+
+func (x *Schema) ClearExternalDocs() {
+ x.xxx_hidden_ExternalDocs = nil
+}
+
+type Schema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ JsonSchema *JSONSchema
+ // Adds support for polymorphism. The discriminator is the schema property
+ // name that is used to differentiate between other schema that inherit this
+ // schema. The property name used MUST be defined at this schema and it MUST
+ // be in the required property list. When used, the value MUST be the name of
+ // this schema or any schema that inherits it.
+ Discriminator string
+ // Relevant only for Schema "properties" definitions. Declares the property as
+ // "read only". This means that it MAY be sent as part of a response but MUST
+ // NOT be sent as part of the request. Properties marked as readOnly being
+ // true SHOULD NOT be in the required list of the defined schema. Default
+ // value is false.
+ ReadOnly bool
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation
+ // A free-form property to include an example of an instance for this schema in JSON.
+ // This is copied verbatim to the output.
+ Example string
+}
+
+func (b0 Schema_builder) Build() *Schema {
+ m0 := &Schema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_JsonSchema = b.JsonSchema
+ x.xxx_hidden_Discriminator = b.Discriminator
+ x.xxx_hidden_ReadOnly = b.ReadOnly
+ x.xxx_hidden_ExternalDocs = b.ExternalDocs
+ x.xxx_hidden_Example = b.Example
+ return m0
+}
+
+// `EnumSchema` is subset of fields from the OpenAPI v2 specification's Schema object.
+// Only fields that are applicable to Enums are included
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// Example:
+//
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_enum) = {
+// ...
+// title: "MyEnum";
+// description:"This is my nice enum";
+// example: "ZERO";
+// required: true;
+// ...
+// };
+type EnumSchema struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Description string `protobuf:"bytes,1,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Default string `protobuf:"bytes,2,opt,name=default,proto3" json:"default,omitempty"`
+ xxx_hidden_Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
+ xxx_hidden_Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"`
+ xxx_hidden_ReadOnly bool `protobuf:"varint,5,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ xxx_hidden_ExternalDocs *ExternalDocumentation `protobuf:"bytes,6,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ xxx_hidden_Example string `protobuf:"bytes,7,opt,name=example,proto3" json:"example,omitempty"`
+ xxx_hidden_Ref string `protobuf:"bytes,8,opt,name=ref,proto3" json:"ref,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,9,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *EnumSchema) Reset() {
+ *x = EnumSchema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *EnumSchema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*EnumSchema) ProtoMessage() {}
+
+func (x *EnumSchema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[11]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *EnumSchema) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetDefault() string {
+ if x != nil {
+ return x.xxx_hidden_Default
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetTitle() string {
+ if x != nil {
+ return x.xxx_hidden_Title
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetRequired() bool {
+ if x != nil {
+ return x.xxx_hidden_Required
+ }
+ return false
+}
+
+func (x *EnumSchema) GetReadOnly() bool {
+ if x != nil {
+ return x.xxx_hidden_ReadOnly
+ }
+ return false
+}
+
+func (x *EnumSchema) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.xxx_hidden_ExternalDocs
+ }
+ return nil
+}
+
+func (x *EnumSchema) GetExample() string {
+ if x != nil {
+ return x.xxx_hidden_Example
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetRef() string {
+ if x != nil {
+ return x.xxx_hidden_Ref
+ }
+ return ""
+}
+
+func (x *EnumSchema) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *EnumSchema) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *EnumSchema) SetDefault(v string) {
+ x.xxx_hidden_Default = v
+}
+
+func (x *EnumSchema) SetTitle(v string) {
+ x.xxx_hidden_Title = v
+}
+
+func (x *EnumSchema) SetRequired(v bool) {
+ x.xxx_hidden_Required = v
+}
+
+func (x *EnumSchema) SetReadOnly(v bool) {
+ x.xxx_hidden_ReadOnly = v
+}
+
+func (x *EnumSchema) SetExternalDocs(v *ExternalDocumentation) {
+ x.xxx_hidden_ExternalDocs = v
+}
+
+func (x *EnumSchema) SetExample(v string) {
+ x.xxx_hidden_Example = v
+}
+
+func (x *EnumSchema) SetRef(v string) {
+ x.xxx_hidden_Ref = v
+}
+
+func (x *EnumSchema) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *EnumSchema) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_ExternalDocs != nil
+}
+
+func (x *EnumSchema) ClearExternalDocs() {
+ x.xxx_hidden_ExternalDocs = nil
+}
+
+type EnumSchema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A short description of the schema.
+ Description string
+ Default string
+ // The title of the schema.
+ Title string
+ Required bool
+ ReadOnly bool
+ // Additional external documentation for this schema.
+ ExternalDocs *ExternalDocumentation
+ Example string
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 EnumSchema_builder) Build() *EnumSchema {
+ m0 := &EnumSchema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Default = b.Default
+ x.xxx_hidden_Title = b.Title
+ x.xxx_hidden_Required = b.Required
+ x.xxx_hidden_ReadOnly = b.ReadOnly
+ x.xxx_hidden_ExternalDocs = b.ExternalDocs
+ x.xxx_hidden_Example = b.Example
+ x.xxx_hidden_Ref = b.Ref
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `JSONSchema` represents properties from JSON Schema taken, and as used, in
+// the OpenAPI v2 spec.
+//
+// This includes changes made by OpenAPI v2.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+//
+// See also: https://cswr.github.io/JsonSchema/spec/basic_types/,
+// https://github.com/json-schema-org/json-schema-spec/blob/master/schema.json
+//
+// Example:
+//
+// message SimpleMessage {
+// option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {
+// json_schema: {
+// title: "SimpleMessage"
+// description: "A simple message."
+// required: ["id"]
+// }
+// };
+//
+// // Id represents the message identifier.
+// string id = 1; [
+// (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
+// description: "The unique identifier of the simple message."
+// }];
+// }
+type JSONSchema struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Ref string `protobuf:"bytes,3,opt,name=ref,proto3" json:"ref,omitempty"`
+ xxx_hidden_Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Default string `protobuf:"bytes,7,opt,name=default,proto3" json:"default,omitempty"`
+ xxx_hidden_ReadOnly bool `protobuf:"varint,8,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"`
+ xxx_hidden_Example string `protobuf:"bytes,9,opt,name=example,proto3" json:"example,omitempty"`
+ xxx_hidden_MultipleOf float64 `protobuf:"fixed64,10,opt,name=multiple_of,json=multipleOf,proto3" json:"multiple_of,omitempty"`
+ xxx_hidden_Maximum float64 `protobuf:"fixed64,11,opt,name=maximum,proto3" json:"maximum,omitempty"`
+ xxx_hidden_ExclusiveMaximum bool `protobuf:"varint,12,opt,name=exclusive_maximum,json=exclusiveMaximum,proto3" json:"exclusive_maximum,omitempty"`
+ xxx_hidden_Minimum float64 `protobuf:"fixed64,13,opt,name=minimum,proto3" json:"minimum,omitempty"`
+ xxx_hidden_ExclusiveMinimum bool `protobuf:"varint,14,opt,name=exclusive_minimum,json=exclusiveMinimum,proto3" json:"exclusive_minimum,omitempty"`
+ xxx_hidden_MaxLength uint64 `protobuf:"varint,15,opt,name=max_length,json=maxLength,proto3" json:"max_length,omitempty"`
+ xxx_hidden_MinLength uint64 `protobuf:"varint,16,opt,name=min_length,json=minLength,proto3" json:"min_length,omitempty"`
+ xxx_hidden_Pattern string `protobuf:"bytes,17,opt,name=pattern,proto3" json:"pattern,omitempty"`
+ xxx_hidden_MaxItems uint64 `protobuf:"varint,20,opt,name=max_items,json=maxItems,proto3" json:"max_items,omitempty"`
+ xxx_hidden_MinItems uint64 `protobuf:"varint,21,opt,name=min_items,json=minItems,proto3" json:"min_items,omitempty"`
+ xxx_hidden_UniqueItems bool `protobuf:"varint,22,opt,name=unique_items,json=uniqueItems,proto3" json:"unique_items,omitempty"`
+ xxx_hidden_MaxProperties uint64 `protobuf:"varint,24,opt,name=max_properties,json=maxProperties,proto3" json:"max_properties,omitempty"`
+ xxx_hidden_MinProperties uint64 `protobuf:"varint,25,opt,name=min_properties,json=minProperties,proto3" json:"min_properties,omitempty"`
+ xxx_hidden_Required []string `protobuf:"bytes,26,rep,name=required,proto3" json:"required,omitempty"`
+ xxx_hidden_Array []string `protobuf:"bytes,34,rep,name=array,proto3" json:"array,omitempty"`
+ xxx_hidden_Type []JSONSchema_JSONSchemaSimpleTypes `protobuf:"varint,35,rep,packed,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.JSONSchema_JSONSchemaSimpleTypes" json:"type,omitempty"`
+ xxx_hidden_Format string `protobuf:"bytes,36,opt,name=format,proto3" json:"format,omitempty"`
+ xxx_hidden_Enum []string `protobuf:"bytes,46,rep,name=enum,proto3" json:"enum,omitempty"`
+ xxx_hidden_FieldConfiguration *JSONSchema_FieldConfiguration `protobuf:"bytes,1001,opt,name=field_configuration,json=fieldConfiguration,proto3" json:"field_configuration,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,48,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *JSONSchema) Reset() {
+ *x = JSONSchema{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *JSONSchema) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JSONSchema) ProtoMessage() {}
+
+func (x *JSONSchema) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[12]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *JSONSchema) GetRef() string {
+ if x != nil {
+ return x.xxx_hidden_Ref
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetTitle() string {
+ if x != nil {
+ return x.xxx_hidden_Title
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetDefault() string {
+ if x != nil {
+ return x.xxx_hidden_Default
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetReadOnly() bool {
+ if x != nil {
+ return x.xxx_hidden_ReadOnly
+ }
+ return false
+}
+
+func (x *JSONSchema) GetExample() string {
+ if x != nil {
+ return x.xxx_hidden_Example
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetMultipleOf() float64 {
+ if x != nil {
+ return x.xxx_hidden_MultipleOf
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMaximum() float64 {
+ if x != nil {
+ return x.xxx_hidden_Maximum
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetExclusiveMaximum() bool {
+ if x != nil {
+ return x.xxx_hidden_ExclusiveMaximum
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMinimum() float64 {
+ if x != nil {
+ return x.xxx_hidden_Minimum
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetExclusiveMinimum() bool {
+ if x != nil {
+ return x.xxx_hidden_ExclusiveMinimum
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMaxLength() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MaxLength
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinLength() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MinLength
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetPattern() string {
+ if x != nil {
+ return x.xxx_hidden_Pattern
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetMaxItems() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MaxItems
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinItems() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MinItems
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetUniqueItems() bool {
+ if x != nil {
+ return x.xxx_hidden_UniqueItems
+ }
+ return false
+}
+
+func (x *JSONSchema) GetMaxProperties() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MaxProperties
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetMinProperties() uint64 {
+ if x != nil {
+ return x.xxx_hidden_MinProperties
+ }
+ return 0
+}
+
+func (x *JSONSchema) GetRequired() []string {
+ if x != nil {
+ return x.xxx_hidden_Required
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetArray() []string {
+ if x != nil {
+ return x.xxx_hidden_Array
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetType() []JSONSchema_JSONSchemaSimpleTypes {
+ if x != nil {
+ return x.xxx_hidden_Type
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetFormat() string {
+ if x != nil {
+ return x.xxx_hidden_Format
+ }
+ return ""
+}
+
+func (x *JSONSchema) GetEnum() []string {
+ if x != nil {
+ return x.xxx_hidden_Enum
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetFieldConfiguration() *JSONSchema_FieldConfiguration {
+ if x != nil {
+ return x.xxx_hidden_FieldConfiguration
+ }
+ return nil
+}
+
+func (x *JSONSchema) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *JSONSchema) SetRef(v string) {
+ x.xxx_hidden_Ref = v
+}
+
+func (x *JSONSchema) SetTitle(v string) {
+ x.xxx_hidden_Title = v
+}
+
+func (x *JSONSchema) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *JSONSchema) SetDefault(v string) {
+ x.xxx_hidden_Default = v
+}
+
+func (x *JSONSchema) SetReadOnly(v bool) {
+ x.xxx_hidden_ReadOnly = v
+}
+
+func (x *JSONSchema) SetExample(v string) {
+ x.xxx_hidden_Example = v
+}
+
+func (x *JSONSchema) SetMultipleOf(v float64) {
+ x.xxx_hidden_MultipleOf = v
+}
+
+func (x *JSONSchema) SetMaximum(v float64) {
+ x.xxx_hidden_Maximum = v
+}
+
+func (x *JSONSchema) SetExclusiveMaximum(v bool) {
+ x.xxx_hidden_ExclusiveMaximum = v
+}
+
+func (x *JSONSchema) SetMinimum(v float64) {
+ x.xxx_hidden_Minimum = v
+}
+
+func (x *JSONSchema) SetExclusiveMinimum(v bool) {
+ x.xxx_hidden_ExclusiveMinimum = v
+}
+
+func (x *JSONSchema) SetMaxLength(v uint64) {
+ x.xxx_hidden_MaxLength = v
+}
+
+func (x *JSONSchema) SetMinLength(v uint64) {
+ x.xxx_hidden_MinLength = v
+}
+
+func (x *JSONSchema) SetPattern(v string) {
+ x.xxx_hidden_Pattern = v
+}
+
+func (x *JSONSchema) SetMaxItems(v uint64) {
+ x.xxx_hidden_MaxItems = v
+}
+
+func (x *JSONSchema) SetMinItems(v uint64) {
+ x.xxx_hidden_MinItems = v
+}
+
+func (x *JSONSchema) SetUniqueItems(v bool) {
+ x.xxx_hidden_UniqueItems = v
+}
+
+func (x *JSONSchema) SetMaxProperties(v uint64) {
+ x.xxx_hidden_MaxProperties = v
+}
+
+func (x *JSONSchema) SetMinProperties(v uint64) {
+ x.xxx_hidden_MinProperties = v
+}
+
+func (x *JSONSchema) SetRequired(v []string) {
+ x.xxx_hidden_Required = v
+}
+
+func (x *JSONSchema) SetArray(v []string) {
+ x.xxx_hidden_Array = v
+}
+
+func (x *JSONSchema) SetType(v []JSONSchema_JSONSchemaSimpleTypes) {
+ x.xxx_hidden_Type = v
+}
+
+func (x *JSONSchema) SetFormat(v string) {
+ x.xxx_hidden_Format = v
+}
+
+func (x *JSONSchema) SetEnum(v []string) {
+ x.xxx_hidden_Enum = v
+}
+
+func (x *JSONSchema) SetFieldConfiguration(v *JSONSchema_FieldConfiguration) {
+ x.xxx_hidden_FieldConfiguration = v
+}
+
+func (x *JSONSchema) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *JSONSchema) HasFieldConfiguration() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_FieldConfiguration != nil
+}
+
+func (x *JSONSchema) ClearFieldConfiguration() {
+ x.xxx_hidden_FieldConfiguration = nil
+}
+
+type JSONSchema_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Ref is used to define an external reference to include in the message.
+ // This could be a fully qualified proto message reference, and that type must
+ // be imported into the protofile. If no message is identified, the Ref will
+ // be used verbatim in the output.
+ // For example:
+ //
+ // `ref: ".google.protobuf.Timestamp"`.
+ Ref string
+ // The title of the schema.
+ Title string
+ // A short description of the schema.
+ Description string
+ Default string
+ ReadOnly bool
+ // A free-form property to include a JSON example of this field. This is copied
+ // verbatim to the output swagger.json. Quotes must be escaped.
+ // This property is the same for 2.0 and 3.0.0 https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#schemaObject https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#schemaObject
+ Example string
+ MultipleOf float64
+ // Maximum represents an inclusive upper limit for a numeric instance. The
+ // value of MUST be a number,
+ Maximum float64
+ ExclusiveMaximum bool
+ // minimum represents an inclusive lower limit for a numeric instance. The
+ // value of MUST be a number,
+ Minimum float64
+ ExclusiveMinimum bool
+ MaxLength uint64
+ MinLength uint64
+ Pattern string
+ MaxItems uint64
+ MinItems uint64
+ UniqueItems bool
+ MaxProperties uint64
+ MinProperties uint64
+ Required []string
+ // Items in 'array' must be unique.
+ Array []string
+ Type []JSONSchema_JSONSchemaSimpleTypes
+ // `Format`
+ Format string
+ // Items in `enum` must be unique https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.1
+ Enum []string
+ // Additional field level properties used when generating the OpenAPI v2 file.
+ FieldConfiguration *JSONSchema_FieldConfiguration
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 JSONSchema_builder) Build() *JSONSchema {
+ m0 := &JSONSchema{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Ref = b.Ref
+ x.xxx_hidden_Title = b.Title
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Default = b.Default
+ x.xxx_hidden_ReadOnly = b.ReadOnly
+ x.xxx_hidden_Example = b.Example
+ x.xxx_hidden_MultipleOf = b.MultipleOf
+ x.xxx_hidden_Maximum = b.Maximum
+ x.xxx_hidden_ExclusiveMaximum = b.ExclusiveMaximum
+ x.xxx_hidden_Minimum = b.Minimum
+ x.xxx_hidden_ExclusiveMinimum = b.ExclusiveMinimum
+ x.xxx_hidden_MaxLength = b.MaxLength
+ x.xxx_hidden_MinLength = b.MinLength
+ x.xxx_hidden_Pattern = b.Pattern
+ x.xxx_hidden_MaxItems = b.MaxItems
+ x.xxx_hidden_MinItems = b.MinItems
+ x.xxx_hidden_UniqueItems = b.UniqueItems
+ x.xxx_hidden_MaxProperties = b.MaxProperties
+ x.xxx_hidden_MinProperties = b.MinProperties
+ x.xxx_hidden_Required = b.Required
+ x.xxx_hidden_Array = b.Array
+ x.xxx_hidden_Type = b.Type
+ x.xxx_hidden_Format = b.Format
+ x.xxx_hidden_Enum = b.Enum
+ x.xxx_hidden_FieldConfiguration = b.FieldConfiguration
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `Tag` is a representation of OpenAPI v2 specification's Tag object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#tagObject
+type Tag struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_ExternalDocs *ExternalDocumentation `protobuf:"bytes,3,opt,name=external_docs,json=externalDocs,proto3" json:"external_docs,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,4,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Tag) Reset() {
+ *x = Tag{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Tag) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Tag) ProtoMessage() {}
+
+func (x *Tag) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[13]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Tag) GetName() string {
+ if x != nil {
+ return x.xxx_hidden_Name
+ }
+ return ""
+}
+
+func (x *Tag) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *Tag) GetExternalDocs() *ExternalDocumentation {
+ if x != nil {
+ return x.xxx_hidden_ExternalDocs
+ }
+ return nil
+}
+
+func (x *Tag) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *Tag) SetName(v string) {
+ x.xxx_hidden_Name = v
+}
+
+func (x *Tag) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *Tag) SetExternalDocs(v *ExternalDocumentation) {
+ x.xxx_hidden_ExternalDocs = v
+}
+
+func (x *Tag) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *Tag) HasExternalDocs() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_ExternalDocs != nil
+}
+
+func (x *Tag) ClearExternalDocs() {
+ x.xxx_hidden_ExternalDocs = nil
+}
+
+type Tag_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The name of the tag. Use it to allow override of the name of a
+ // global Tag object, then use that name to reference the tag throughout the
+ // OpenAPI file.
+ Name string
+ // A short description for the tag. GFM syntax can be used for rich text
+ // representation.
+ Description string
+ // Additional external documentation for this tag.
+ ExternalDocs *ExternalDocumentation
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 Tag_builder) Build() *Tag {
+ m0 := &Tag{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Name = b.Name
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_ExternalDocs = b.ExternalDocs
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `SecurityDefinitions` is a representation of OpenAPI v2 specification's
+// Security Definitions object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityDefinitionsObject
+//
+// A declaration of the security schemes available to be used in the
+// specification. This does not enforce the security schemes on the operations
+// and only serves to provide the relevant details for each scheme.
+type SecurityDefinitions struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Security map[string]*SecurityScheme `protobuf:"bytes,1,rep,name=security,proto3" json:"security,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityDefinitions) Reset() {
+ *x = SecurityDefinitions{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityDefinitions) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityDefinitions) ProtoMessage() {}
+
+func (x *SecurityDefinitions) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[14]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityDefinitions) GetSecurity() map[string]*SecurityScheme {
+ if x != nil {
+ return x.xxx_hidden_Security
+ }
+ return nil
+}
+
+func (x *SecurityDefinitions) SetSecurity(v map[string]*SecurityScheme) {
+ x.xxx_hidden_Security = v
+}
+
+type SecurityDefinitions_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // A single security scheme definition, mapping a "name" to the scheme it
+ // defines.
+ Security map[string]*SecurityScheme
+}
+
+func (b0 SecurityDefinitions_builder) Build() *SecurityDefinitions {
+ m0 := &SecurityDefinitions{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Security = b.Security
+ return m0
+}
+
+// `SecurityScheme` is a representation of OpenAPI v2 specification's
+// Security Scheme object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securitySchemeObject
+//
+// Allows the definition of a security scheme that can be used by the
+// operations. Supported schemes are basic authentication, an API key (either as
+// a header or as a query parameter) and OAuth2's common flows (implicit,
+// password, application and access code).
+type SecurityScheme struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Type SecurityScheme_Type `protobuf:"varint,1,opt,name=type,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_Type" json:"type,omitempty"`
+ xxx_hidden_Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"`
+ xxx_hidden_Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+ xxx_hidden_In SecurityScheme_In `protobuf:"varint,4,opt,name=in,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_In" json:"in,omitempty"`
+ xxx_hidden_Flow SecurityScheme_Flow `protobuf:"varint,5,opt,name=flow,proto3,enum=grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme_Flow" json:"flow,omitempty"`
+ xxx_hidden_AuthorizationUrl string `protobuf:"bytes,6,opt,name=authorization_url,json=authorizationUrl,proto3" json:"authorization_url,omitempty"`
+ xxx_hidden_TokenUrl string `protobuf:"bytes,7,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"`
+ xxx_hidden_Scopes *Scopes `protobuf:"bytes,8,opt,name=scopes,proto3" json:"scopes,omitempty"`
+ xxx_hidden_Extensions map[string]*structpb.Value `protobuf:"bytes,9,rep,name=extensions,proto3" json:"extensions,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityScheme) Reset() {
+ *x = SecurityScheme{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityScheme) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityScheme) ProtoMessage() {}
+
+func (x *SecurityScheme) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[15]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityScheme) GetType() SecurityScheme_Type {
+ if x != nil {
+ return x.xxx_hidden_Type
+ }
+ return SecurityScheme_TYPE_INVALID
+}
+
+func (x *SecurityScheme) GetDescription() string {
+ if x != nil {
+ return x.xxx_hidden_Description
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetName() string {
+ if x != nil {
+ return x.xxx_hidden_Name
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetIn() SecurityScheme_In {
+ if x != nil {
+ return x.xxx_hidden_In
+ }
+ return SecurityScheme_IN_INVALID
+}
+
+func (x *SecurityScheme) GetFlow() SecurityScheme_Flow {
+ if x != nil {
+ return x.xxx_hidden_Flow
+ }
+ return SecurityScheme_FLOW_INVALID
+}
+
+func (x *SecurityScheme) GetAuthorizationUrl() string {
+ if x != nil {
+ return x.xxx_hidden_AuthorizationUrl
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetTokenUrl() string {
+ if x != nil {
+ return x.xxx_hidden_TokenUrl
+ }
+ return ""
+}
+
+func (x *SecurityScheme) GetScopes() *Scopes {
+ if x != nil {
+ return x.xxx_hidden_Scopes
+ }
+ return nil
+}
+
+func (x *SecurityScheme) GetExtensions() map[string]*structpb.Value {
+ if x != nil {
+ return x.xxx_hidden_Extensions
+ }
+ return nil
+}
+
+func (x *SecurityScheme) SetType(v SecurityScheme_Type) {
+ x.xxx_hidden_Type = v
+}
+
+func (x *SecurityScheme) SetDescription(v string) {
+ x.xxx_hidden_Description = v
+}
+
+func (x *SecurityScheme) SetName(v string) {
+ x.xxx_hidden_Name = v
+}
+
+func (x *SecurityScheme) SetIn(v SecurityScheme_In) {
+ x.xxx_hidden_In = v
+}
+
+func (x *SecurityScheme) SetFlow(v SecurityScheme_Flow) {
+ x.xxx_hidden_Flow = v
+}
+
+func (x *SecurityScheme) SetAuthorizationUrl(v string) {
+ x.xxx_hidden_AuthorizationUrl = v
+}
+
+func (x *SecurityScheme) SetTokenUrl(v string) {
+ x.xxx_hidden_TokenUrl = v
+}
+
+func (x *SecurityScheme) SetScopes(v *Scopes) {
+ x.xxx_hidden_Scopes = v
+}
+
+func (x *SecurityScheme) SetExtensions(v map[string]*structpb.Value) {
+ x.xxx_hidden_Extensions = v
+}
+
+func (x *SecurityScheme) HasScopes() bool {
+ if x == nil {
+ return false
+ }
+ return x.xxx_hidden_Scopes != nil
+}
+
+func (x *SecurityScheme) ClearScopes() {
+ x.xxx_hidden_Scopes = nil
+}
+
+type SecurityScheme_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // The type of the security scheme. Valid values are "basic",
+ // "apiKey" or "oauth2".
+ Type SecurityScheme_Type
+ // A short description for security scheme.
+ Description string
+ // The name of the header or query parameter to be used.
+ // Valid for apiKey.
+ Name string
+ // The location of the API key. Valid values are "query" or
+ // "header".
+ // Valid for apiKey.
+ In SecurityScheme_In
+ // The flow used by the OAuth2 security scheme. Valid values are
+ // "implicit", "password", "application" or "accessCode".
+ // Valid for oauth2.
+ Flow SecurityScheme_Flow
+ // The authorization URL to be used for this flow. This SHOULD be in
+ // the form of a URL.
+ // Valid for oauth2/implicit and oauth2/accessCode.
+ AuthorizationUrl string
+ // The token URL to be used for this flow. This SHOULD be in the
+ // form of a URL.
+ // Valid for oauth2/password, oauth2/application and oauth2/accessCode.
+ TokenUrl string
+ // The available scopes for the OAuth2 security scheme.
+ // Valid for oauth2.
+ Scopes *Scopes
+ // Custom properties that start with "x-" such as "x-foo" used to describe
+ // extra functionality that is not covered by the standard OpenAPI Specification.
+ // See: https://swagger.io/docs/specification/2-0/swagger-extensions/
+ Extensions map[string]*structpb.Value
+}
+
+func (b0 SecurityScheme_builder) Build() *SecurityScheme {
+ m0 := &SecurityScheme{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Type = b.Type
+ x.xxx_hidden_Description = b.Description
+ x.xxx_hidden_Name = b.Name
+ x.xxx_hidden_In = b.In
+ x.xxx_hidden_Flow = b.Flow
+ x.xxx_hidden_AuthorizationUrl = b.AuthorizationUrl
+ x.xxx_hidden_TokenUrl = b.TokenUrl
+ x.xxx_hidden_Scopes = b.Scopes
+ x.xxx_hidden_Extensions = b.Extensions
+ return m0
+}
+
+// `SecurityRequirement` is a representation of OpenAPI v2 specification's
+// Security Requirement object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#securityRequirementObject
+//
+// Lists the required security schemes to execute this operation. The object can
+// have multiple security schemes declared in it which are all required (that
+// is, there is a logical AND between the schemes).
+//
+// The name used for each property MUST correspond to a security scheme
+// declared in the Security Definitions.
+type SecurityRequirement struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_SecurityRequirement map[string]*SecurityRequirement_SecurityRequirementValue `protobuf:"bytes,1,rep,name=security_requirement,json=securityRequirement,proto3" json:"security_requirement,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityRequirement) Reset() {
+ *x = SecurityRequirement{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityRequirement) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityRequirement) ProtoMessage() {}
+
+func (x *SecurityRequirement) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[16]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityRequirement) GetSecurityRequirement() map[string]*SecurityRequirement_SecurityRequirementValue {
+ if x != nil {
+ return x.xxx_hidden_SecurityRequirement
+ }
+ return nil
+}
+
+func (x *SecurityRequirement) SetSecurityRequirement(v map[string]*SecurityRequirement_SecurityRequirementValue) {
+ x.xxx_hidden_SecurityRequirement = v
+}
+
+type SecurityRequirement_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Each name must correspond to a security scheme which is declared in
+ // the Security Definitions. If the security scheme is of type "oauth2",
+ // then the value is a list of scope names required for the execution.
+ // For other security scheme types, the array MUST be empty.
+ SecurityRequirement map[string]*SecurityRequirement_SecurityRequirementValue
+}
+
+func (b0 SecurityRequirement_builder) Build() *SecurityRequirement {
+ m0 := &SecurityRequirement{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_SecurityRequirement = b.SecurityRequirement
+ return m0
+}
+
+// `Scopes` is a representation of OpenAPI v2 specification's Scopes object.
+//
+// See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#scopesObject
+//
+// Lists the available scopes for an OAuth2 security scheme.
+type Scopes struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Scope map[string]string `protobuf:"bytes,1,rep,name=scope,proto3" json:"scope,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Scopes) Reset() {
+ *x = Scopes{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Scopes) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Scopes) ProtoMessage() {}
+
+func (x *Scopes) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[17]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *Scopes) GetScope() map[string]string {
+ if x != nil {
+ return x.xxx_hidden_Scope
+ }
+ return nil
+}
+
+func (x *Scopes) SetScope(v map[string]string) {
+ x.xxx_hidden_Scope = v
+}
+
+type Scopes_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Maps between a name of a scope to a short description of it (as the value
+ // of the property).
+ Scope map[string]string
+}
+
+func (b0 Scopes_builder) Build() *Scopes {
+ m0 := &Scopes{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Scope = b.Scope
+ return m0
+}
+
+// 'FieldConfiguration' provides additional field level properties used when generating the OpenAPI v2 file.
+// These properties are not defined by OpenAPIv2, but they are used to control the generation.
+type JSONSchema_FieldConfiguration struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_PathParamName string `protobuf:"bytes,47,opt,name=path_param_name,json=pathParamName,proto3" json:"path_param_name,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *JSONSchema_FieldConfiguration) Reset() {
+ *x = JSONSchema_FieldConfiguration{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *JSONSchema_FieldConfiguration) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*JSONSchema_FieldConfiguration) ProtoMessage() {}
+
+func (x *JSONSchema_FieldConfiguration) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[27]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *JSONSchema_FieldConfiguration) GetPathParamName() string {
+ if x != nil {
+ return x.xxx_hidden_PathParamName
+ }
+ return ""
+}
+
+func (x *JSONSchema_FieldConfiguration) SetPathParamName(v string) {
+ x.xxx_hidden_PathParamName = v
+}
+
+type JSONSchema_FieldConfiguration_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ // Alternative parameter name when used as path parameter. If set, this will
+ // be used as the complete parameter name when this field is used as a path
+ // parameter. Use this to avoid having auto generated path parameter names
+ // for overlapping paths.
+ PathParamName string
+}
+
+func (b0 JSONSchema_FieldConfiguration_builder) Build() *JSONSchema_FieldConfiguration {
+ m0 := &JSONSchema_FieldConfiguration{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_PathParamName = b.PathParamName
+ return m0
+}
+
+// If the security scheme is of type "oauth2", then the value is a list of
+// scope names required for the execution. For other security scheme types,
+// the array MUST be empty.
+type SecurityRequirement_SecurityRequirementValue struct {
+ state protoimpl.MessageState `protogen:"opaque.v1"`
+ xxx_hidden_Scope []string `protobuf:"bytes,1,rep,name=scope,proto3" json:"scope,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) Reset() {
+ *x = SecurityRequirement_SecurityRequirementValue{}
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[32]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SecurityRequirement_SecurityRequirementValue) ProtoMessage() {}
+
+func (x *SecurityRequirement_SecurityRequirementValue) ProtoReflect() protoreflect.Message {
+ mi := &file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes[32]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) GetScope() []string {
+ if x != nil {
+ return x.xxx_hidden_Scope
+ }
+ return nil
+}
+
+func (x *SecurityRequirement_SecurityRequirementValue) SetScope(v []string) {
+ x.xxx_hidden_Scope = v
+}
+
+type SecurityRequirement_SecurityRequirementValue_builder struct {
+ _ [0]func() // Prevents comparability and use of unkeyed literals for the builder.
+
+ Scope []string
+}
+
+func (b0 SecurityRequirement_SecurityRequirementValue_builder) Build() *SecurityRequirement_SecurityRequirementValue {
+ m0 := &SecurityRequirement_SecurityRequirementValue{}
+ b, x := &b0, m0
+ _, _ = b, x
+ x.xxx_hidden_Scope = b.Scope
+ return m0
+}
+
+var File_protoc_gen_openapiv3_options_openapiv3_proto protoreflect.FileDescriptor
+
+var file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc = []byte{
+ 0x0a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
+ 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63,
+ 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb3, 0x08, 0x0a, 0x07, 0x53, 0x77, 0x61, 0x67,
+ 0x67, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, 0x43, 0x0a,
+ 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e,
+ 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70,
+ 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x61, 0x73, 0x65, 0x50,
+ 0x61, 0x74, 0x68, 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x05,
+ 0x20, 0x03, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73,
+ 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03,
+ 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08,
+ 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08,
+ 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x5f, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70,
+ 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x72,
+ 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e,
+ 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09,
+ 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x12, 0x71, 0x0a, 0x14, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69,
+ 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x13, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x5a, 0x0a, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3e,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69,
+ 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72,
+ 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x42, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73,
+ 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x54, 0x61, 0x67, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x65, 0x0a, 0x0d,
+ 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44,
+ 0x6f, 0x63, 0x73, 0x12, 0x62, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x42, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x71, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+ 0x6e, 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52,
+ 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0xd6, 0x07,
+ 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74,
+ 0x61, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12,
+ 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73,
+ 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+ 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0d, 0x65,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45,
+ 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f,
+ 0x63, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+ 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
+ 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
+ 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x73, 0x12, 0x61, 0x0a,
+ 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x43, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e,
+ 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61,
+ 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65,
+ 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73,
+ 0x12, 0x4b, 0x0a, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28,
+ 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63,
+ 0x68, 0x65, 0x6d, 0x65, 0x52, 0x07, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x73, 0x12, 0x1e, 0x0a,
+ 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x5a, 0x0a,
+ 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52,
+ 0x08, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x12, 0x64, 0x0a, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x44, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e,
+ 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+ 0x55, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61,
+ 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x71, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x49, 0x0a, 0x05, 0x76, 0x61,
+ 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f,
+ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
+ 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c,
+ 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
+ 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+ 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
+ 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x22, 0x62, 0x0a, 0x0a, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65,
+ 0x74, 0x65, 0x72, 0x73, 0x12, 0x54, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
+ 0x72, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0xa3, 0x02, 0x0a, 0x0f, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x12, 0x12,
+ 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x53, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x0e, 0x32, 0x3f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x48,
+ 0x65, 0x61, 0x64, 0x65, 0x72, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x2e, 0x54,
+ 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72,
+ 0x6d, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61,
+ 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x45, 0x0a,
+ 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e,
+ 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0a,
+ 0x0a, 0x06, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e,
+ 0x54, 0x45, 0x47, 0x45, 0x52, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45,
+ 0x41, 0x4e, 0x10, 0x04, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08,
+ 0x22, 0xd8, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a,
+ 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
+ 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66,
+ 0x61, 0x75, 0x6c, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61,
+ 0x75, 0x6c, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x18, 0x0d,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x4a, 0x04, 0x08,
+ 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x4a,
+ 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x4a, 0x04, 0x08, 0x0a, 0x10,
+ 0x0b, 0x4a, 0x04, 0x08, 0x0b, 0x10, 0x0c, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x0d, 0x4a, 0x04, 0x08,
+ 0x0e, 0x10, 0x0f, 0x4a, 0x04, 0x08, 0x0f, 0x10, 0x10, 0x4a, 0x04, 0x08, 0x10, 0x10, 0x11, 0x4a,
+ 0x04, 0x08, 0x11, 0x10, 0x12, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x22, 0x9a, 0x05, 0x0a, 0x08,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64,
+ 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x63,
+ 0x68, 0x65, 0x6d, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73,
+ 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x5a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
+ 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x65, 0x61, 0x64,
+ 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72,
+ 0x73, 0x12, 0x5d, 0x0a, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x41, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
+ 0x12, 0x63, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x05,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73,
+ 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x6d, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x47, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3b, 0x0a, 0x0d, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73,
+ 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76,
+ 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd6, 0x03, 0x0a, 0x04, 0x49, 0x6e, 0x66,
+ 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65,
+ 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x65, 0x72,
+ 0x6d, 0x73, 0x5f, 0x6f, 0x66, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0e, 0x74, 0x65, 0x72, 0x6d, 0x73, 0x4f, 0x66, 0x53, 0x65, 0x72, 0x76,
+ 0x69, 0x63, 0x65, 0x12, 0x4c, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x63,
+ 0x74, 0x12, 0x4c, 0x0a, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
+ 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65,
+ 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4c,
+ 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x52, 0x07, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12,
+ 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x5f, 0x0a, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3f, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x45,
+ 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a,
+ 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x22, 0x45, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+ 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x2f, 0x0a, 0x07, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x4b, 0x0a, 0x15, 0x45, 0x78, 0x74,
+ 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xaa, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x12, 0x56, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0a, 0x6a,
+ 0x73, 0x6f, 0x6e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0d, 0x64, 0x69, 0x73,
+ 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0d, 0x64, 0x69, 0x73, 0x63, 0x72, 0x69, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x12,
+ 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x65, 0x0a, 0x0d,
+ 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44,
+ 0x6f, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x4a, 0x04, 0x08,
+ 0x04, 0x10, 0x05, 0x22, 0xe8, 0x03, 0x0a, 0x0a, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x61, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x14,
+ 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74,
+ 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
+ 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x05, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x65, 0x0a,
+ 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x06,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
+ 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c,
+ 0x44, 0x6f, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18,
+ 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x10,
+ 0x0a, 0x03, 0x72, 0x65, 0x66, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66,
+ 0x12, 0x65, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x09,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65,
+ 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f,
+ 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x45, 0x78, 0x74, 0x65,
+ 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74,
+ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e,
+ 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
+ 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f,
+ 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61,
+ 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7,
+ 0x0a, 0x0a, 0x0a, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x10, 0x0a,
+ 0x03, 0x72, 0x65, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x72, 0x65, 0x66, 0x12,
+ 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
+ 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75,
+ 0x6c, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
+ 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x08,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x12, 0x18,
+ 0x0a, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x75, 0x6c, 0x74,
+ 0x69, 0x70, 0x6c, 0x65, 0x5f, 0x6f, 0x66, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0a, 0x6d,
+ 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x4f, 0x66, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78,
+ 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x69,
+ 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65,
+ 0x5f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
+ 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x4d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d,
+ 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18, 0x0d, 0x20, 0x01, 0x28,
+ 0x01, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x2b, 0x0a, 0x11, 0x65, 0x78,
+ 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x18,
+ 0x0e, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x76, 0x65,
+ 0x4d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x6c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x78,
+ 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x5f, 0x6c, 0x65,
+ 0x6e, 0x67, 0x74, 0x68, 0x18, 0x10, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x4c,
+ 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e,
+ 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6e, 0x12,
+ 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x14, 0x20, 0x01,
+ 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x1b, 0x0a, 0x09,
+ 0x6d, 0x69, 0x6e, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x04, 0x52,
+ 0x08, 0x6d, 0x69, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x6e, 0x69,
+ 0x71, 0x75, 0x65, 0x5f, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x08, 0x52,
+ 0x0b, 0x75, 0x6e, 0x69, 0x71, 0x75, 0x65, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x25, 0x0a, 0x0e,
+ 0x6d, 0x61, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x18,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x61, 0x78, 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74,
+ 0x69, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x69, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x65,
+ 0x72, 0x74, 0x69, 0x65, 0x73, 0x18, 0x19, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6d, 0x69, 0x6e,
+ 0x50, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x18,
+ 0x22, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x61, 0x72, 0x72, 0x61, 0x79, 0x12, 0x5f, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x18, 0x23, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x4b, 0x2e, 0x67, 0x72, 0x70,
+ 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+ 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f,
+ 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x61, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x69, 0x6d, 0x70,
+ 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a,
+ 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x24, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66,
+ 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x2e, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x04, 0x65, 0x6e, 0x75, 0x6d, 0x12, 0x7a, 0x0a, 0x13, 0x66, 0x69, 0x65,
+ 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x18, 0xe9, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x48, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x46,
+ 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
+ 0x6e, 0x52, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+ 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
+ 0x6f, 0x6e, 0x73, 0x18, 0x30, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x45, 0x2e, 0x67, 0x72, 0x70, 0x63,
+ 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f,
+ 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61,
+ 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x3c, 0x0a, 0x12,
+ 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
+ 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x2f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x74,
+ 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x1a, 0x55, 0x0a, 0x0f, 0x45, 0x78,
+ 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
+ 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+ 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+ 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
+ 0x01, 0x22, 0x77, 0x0a, 0x15, 0x4a, 0x53, 0x4f, 0x4e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53,
+ 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e,
+ 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x52, 0x41, 0x59,
+ 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x4f, 0x4f, 0x4c, 0x45, 0x41, 0x4e, 0x10, 0x02, 0x12,
+ 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x54, 0x45, 0x47, 0x45, 0x52, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04,
+ 0x4e, 0x55, 0x4c, 0x4c, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52,
+ 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x4f, 0x42, 0x4a, 0x45, 0x43, 0x54, 0x10, 0x06, 0x12, 0x0a,
+ 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02,
+ 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x12,
+ 0x10, 0x13, 0x4a, 0x04, 0x08, 0x13, 0x10, 0x14, 0x4a, 0x04, 0x08, 0x17, 0x10, 0x18, 0x4a, 0x04,
+ 0x08, 0x1b, 0x10, 0x1c, 0x4a, 0x04, 0x08, 0x1c, 0x10, 0x1d, 0x4a, 0x04, 0x08, 0x1d, 0x10, 0x1e,
+ 0x4a, 0x04, 0x08, 0x1e, 0x10, 0x22, 0x4a, 0x04, 0x08, 0x25, 0x10, 0x2a, 0x4a, 0x04, 0x08, 0x2a,
+ 0x10, 0x2b, 0x4a, 0x04, 0x08, 0x2b, 0x10, 0x2e, 0x22, 0xd9, 0x02, 0x0a, 0x03, 0x54, 0x61, 0x67,
+ 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72,
+ 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x65, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e,
+ 0x61, 0x6c, 0x5f, 0x64, 0x6f, 0x63, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e,
+ 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e,
+ 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
+ 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x73, 0x12, 0x5e, 0x0a,
+ 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
+ 0x0b, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e,
+ 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x54, 0x61,
+ 0x67, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a,
+ 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+ 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf7, 0x01, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x68, 0x0a, 0x08,
+ 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4c,
+ 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69,
+ 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72,
+ 0x69, 0x74, 0x79, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53,
+ 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x73, 0x65,
+ 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x1a, 0x76, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69,
+ 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4f, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e,
+ 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67,
+ 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74,
+ 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68,
+ 0x65, 0x6d, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xff,
+ 0x06, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x65, 0x12, 0x52, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32,
+ 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70,
+ 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70,
+ 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52,
+ 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
+ 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63,
+ 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+ 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4c, 0x0a, 0x02, 0x69,
+ 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x65, 0x2e, 0x49, 0x6e, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x52, 0x0a, 0x04, 0x66, 0x6c, 0x6f,
+ 0x77, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3e, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65,
+ 0x6d, 0x65, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x52, 0x04, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x2b, 0x0a,
+ 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75,
+ 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+ 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f,
+ 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74,
+ 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65,
+ 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70,
+ 0x65, 0x73, 0x12, 0x69, 0x0a, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
+ 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61,
+ 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e,
+ 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+ 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x63, 0x68, 0x65, 0x6d,
+ 0x65, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0a, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x55, 0x0a,
+ 0x0f, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+ 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+ 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c,
+ 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0e,
+ 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x42, 0x41, 0x53, 0x49, 0x43, 0x10, 0x01, 0x12, 0x10,
+ 0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x41, 0x50, 0x49, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02,
+ 0x12, 0x0f, 0x0a, 0x0b, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x41, 0x55, 0x54, 0x48, 0x32, 0x10,
+ 0x03, 0x22, 0x31, 0x0a, 0x02, 0x49, 0x6e, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x4e, 0x5f, 0x49, 0x4e,
+ 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4e, 0x5f, 0x51, 0x55,
+ 0x45, 0x52, 0x59, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x49, 0x4e, 0x5f, 0x48, 0x45, 0x41, 0x44,
+ 0x45, 0x52, 0x10, 0x02, 0x22, 0x6a, 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x12, 0x10, 0x0a, 0x0c,
+ 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x11,
+ 0x0a, 0x0d, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10,
+ 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f,
+ 0x52, 0x44, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c, 0x4f, 0x57, 0x5f, 0x41, 0x50, 0x50,
+ 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x03, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x4c,
+ 0x4f, 0x57, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x44, 0x45, 0x10, 0x04,
+ 0x22, 0xf6, 0x02, 0x0a, 0x13, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71,
+ 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x8a, 0x01, 0x0a, 0x14, 0x73, 0x65, 0x63,
+ 0x75, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e,
+ 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67,
+ 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65,
+ 0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69,
+ 0x6f, 0x6e, 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75,
+ 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79,
+ 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79,
+ 0x52, 0x13, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x1a, 0x30, 0x0a, 0x18, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74,
+ 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75,
+ 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
+ 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x1a, 0x9f, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x63, 0x75,
+ 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x45,
+ 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x6d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x57, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74,
+ 0x65, 0x77, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f,
+ 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+ 0x73, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72,
+ 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65,
+ 0x71, 0x75, 0x69, 0x72, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x05,
+ 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x96, 0x01, 0x0a, 0x06, 0x53, 0x63,
+ 0x6f, 0x70, 0x65, 0x73, 0x12, 0x52, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x77,
+ 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x5f, 0x67, 0x65, 0x6e, 0x5f, 0x6f, 0x70,
+ 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x33, 0x2e, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
+ 0x53, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x63, 0x6f, 0x70, 0x65, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x1a, 0x38, 0x0a, 0x0a, 0x53, 0x63, 0x6f, 0x70,
+ 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x2a, 0x3b, 0x0a, 0x06, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x65, 0x12, 0x0b, 0x0a, 0x07,
+ 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54,
+ 0x50, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x48, 0x54, 0x54, 0x50, 0x53, 0x10, 0x02, 0x12, 0x06,
+ 0x0a, 0x02, 0x57, 0x53, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x57, 0x53, 0x53, 0x10, 0x04, 0x42,
+ 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72,
+ 0x70, 0x63, 0x2d, 0x65, 0x63, 0x6f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x67, 0x72, 0x70,
+ 0x63, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f,
+ 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76,
+ 0x33, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
+}
+
+var file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes = make([]protoimpl.EnumInfo, 6)
+var file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes = make([]protoimpl.MessageInfo, 35)
+var file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes = []any{
+ (Scheme)(0), // 0: grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ (HeaderParameter_Type)(0), // 1: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.Type
+ (JSONSchema_JSONSchemaSimpleTypes)(0), // 2: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.JSONSchemaSimpleTypes
+ (SecurityScheme_Type)(0), // 3: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Type
+ (SecurityScheme_In)(0), // 4: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.In
+ (SecurityScheme_Flow)(0), // 5: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Flow
+ (*Swagger)(nil), // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger
+ (*Operation)(nil), // 7: grpc.gateway.protoc_gen_openapiv3.options.Operation
+ (*Parameters)(nil), // 8: grpc.gateway.protoc_gen_openapiv3.options.Parameters
+ (*HeaderParameter)(nil), // 9: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter
+ (*Header)(nil), // 10: grpc.gateway.protoc_gen_openapiv3.options.Header
+ (*Response)(nil), // 11: grpc.gateway.protoc_gen_openapiv3.options.Response
+ (*Info)(nil), // 12: grpc.gateway.protoc_gen_openapiv3.options.Info
+ (*Contact)(nil), // 13: grpc.gateway.protoc_gen_openapiv3.options.Contact
+ (*License)(nil), // 14: grpc.gateway.protoc_gen_openapiv3.options.License
+ (*ExternalDocumentation)(nil), // 15: grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ (*Schema)(nil), // 16: grpc.gateway.protoc_gen_openapiv3.options.Schema
+ (*EnumSchema)(nil), // 17: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema
+ (*JSONSchema)(nil), // 18: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ (*Tag)(nil), // 19: grpc.gateway.protoc_gen_openapiv3.options.Tag
+ (*SecurityDefinitions)(nil), // 20: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions
+ (*SecurityScheme)(nil), // 21: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme
+ (*SecurityRequirement)(nil), // 22: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ (*Scopes)(nil), // 23: grpc.gateway.protoc_gen_openapiv3.options.Scopes
+ nil, // 24: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry
+ nil, // 25: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry
+ nil, // 26: grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry
+ nil, // 27: grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry
+ nil, // 28: grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry
+ nil, // 29: grpc.gateway.protoc_gen_openapiv3.options.Response.ExamplesEntry
+ nil, // 30: grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry
+ nil, // 31: grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry
+ nil, // 32: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry
+ (*JSONSchema_FieldConfiguration)(nil), // 33: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.FieldConfiguration
+ nil, // 34: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry
+ nil, // 35: grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry
+ nil, // 36: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry
+ nil, // 37: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry
+ (*SecurityRequirement_SecurityRequirementValue)(nil), // 38: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementValue
+ nil, // 39: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry
+ nil, // 40: grpc.gateway.protoc_gen_openapiv3.options.Scopes.ScopeEntry
+ (*structpb.Value)(nil), // 41: google.protobuf.Value
+}
+var file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs = []int32{
+ 12, // 0: grpc.gateway.protoc_gen_openapiv3.options.Swagger.info:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Info
+ 0, // 1: grpc.gateway.protoc_gen_openapiv3.options.Swagger.schemes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ 24, // 2: grpc.gateway.protoc_gen_openapiv3.options.Swagger.responses:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry
+ 20, // 3: grpc.gateway.protoc_gen_openapiv3.options.Swagger.security_definitions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions
+ 22, // 4: grpc.gateway.protoc_gen_openapiv3.options.Swagger.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ 19, // 5: grpc.gateway.protoc_gen_openapiv3.options.Swagger.tags:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag
+ 15, // 6: grpc.gateway.protoc_gen_openapiv3.options.Swagger.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 25, // 7: grpc.gateway.protoc_gen_openapiv3.options.Swagger.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry
+ 15, // 8: grpc.gateway.protoc_gen_openapiv3.options.Operation.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 26, // 9: grpc.gateway.protoc_gen_openapiv3.options.Operation.responses:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry
+ 0, // 10: grpc.gateway.protoc_gen_openapiv3.options.Operation.schemes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scheme
+ 22, // 11: grpc.gateway.protoc_gen_openapiv3.options.Operation.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement
+ 27, // 12: grpc.gateway.protoc_gen_openapiv3.options.Operation.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry
+ 8, // 13: grpc.gateway.protoc_gen_openapiv3.options.Operation.parameters:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Parameters
+ 9, // 14: grpc.gateway.protoc_gen_openapiv3.options.Parameters.headers:type_name -> grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter
+ 1, // 15: grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.HeaderParameter.Type
+ 16, // 16: grpc.gateway.protoc_gen_openapiv3.options.Response.schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Schema
+ 28, // 17: grpc.gateway.protoc_gen_openapiv3.options.Response.headers:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry
+ 29, // 18: grpc.gateway.protoc_gen_openapiv3.options.Response.examples:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.ExamplesEntry
+ 30, // 19: grpc.gateway.protoc_gen_openapiv3.options.Response.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry
+ 13, // 20: grpc.gateway.protoc_gen_openapiv3.options.Info.contact:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Contact
+ 14, // 21: grpc.gateway.protoc_gen_openapiv3.options.Info.license:type_name -> grpc.gateway.protoc_gen_openapiv3.options.License
+ 31, // 22: grpc.gateway.protoc_gen_openapiv3.options.Info.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry
+ 18, // 23: grpc.gateway.protoc_gen_openapiv3.options.Schema.json_schema:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema
+ 15, // 24: grpc.gateway.protoc_gen_openapiv3.options.Schema.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 15, // 25: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 32, // 26: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry
+ 2, // 27: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.JSONSchemaSimpleTypes
+ 33, // 28: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.field_configuration:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.FieldConfiguration
+ 34, // 29: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry
+ 15, // 30: grpc.gateway.protoc_gen_openapiv3.options.Tag.external_docs:type_name -> grpc.gateway.protoc_gen_openapiv3.options.ExternalDocumentation
+ 35, // 31: grpc.gateway.protoc_gen_openapiv3.options.Tag.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry
+ 36, // 32: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.security:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry
+ 3, // 33: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.type:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Type
+ 4, // 34: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.in:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.In
+ 5, // 35: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.flow:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.Flow
+ 23, // 36: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.scopes:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scopes
+ 37, // 37: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.extensions:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry
+ 39, // 38: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.security_requirement:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry
+ 40, // 39: grpc.gateway.protoc_gen_openapiv3.options.Scopes.scope:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Scopes.ScopeEntry
+ 11, // 40: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ResponsesEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response
+ 41, // 41: grpc.gateway.protoc_gen_openapiv3.options.Swagger.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 11, // 42: grpc.gateway.protoc_gen_openapiv3.options.Operation.ResponsesEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Response
+ 41, // 43: grpc.gateway.protoc_gen_openapiv3.options.Operation.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 10, // 44: grpc.gateway.protoc_gen_openapiv3.options.Response.HeadersEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.Header
+ 41, // 45: grpc.gateway.protoc_gen_openapiv3.options.Response.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 46: grpc.gateway.protoc_gen_openapiv3.options.Info.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 47: grpc.gateway.protoc_gen_openapiv3.options.EnumSchema.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 48: grpc.gateway.protoc_gen_openapiv3.options.JSONSchema.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 41, // 49: grpc.gateway.protoc_gen_openapiv3.options.Tag.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 21, // 50: grpc.gateway.protoc_gen_openapiv3.options.SecurityDefinitions.SecurityEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme
+ 41, // 51: grpc.gateway.protoc_gen_openapiv3.options.SecurityScheme.ExtensionsEntry.value:type_name -> google.protobuf.Value
+ 38, // 52: grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementEntry.value:type_name -> grpc.gateway.protoc_gen_openapiv3.options.SecurityRequirement.SecurityRequirementValue
+ 53, // [53:53] is the sub-list for method output_type
+ 53, // [53:53] is the sub-list for method input_type
+ 53, // [53:53] is the sub-list for extension type_name
+ 53, // [53:53] is the sub-list for extension extendee
+ 0, // [0:53] is the sub-list for field type_name
+}
+
+func init() { file_protoc_gen_openapiv3_options_openapiv3_proto_init() }
+func file_protoc_gen_openapiv3_options_openapiv3_proto_init() {
+ if File_protoc_gen_openapiv3_options_openapiv3_proto != nil {
+ return
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc,
+ NumEnums: 6,
+ NumMessages: 35,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes,
+ DependencyIndexes: file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs,
+ EnumInfos: file_protoc_gen_openapiv3_options_openapiv3_proto_enumTypes,
+ MessageInfos: file_protoc_gen_openapiv3_options_openapiv3_proto_msgTypes,
+ }.Build()
+ File_protoc_gen_openapiv3_options_openapiv3_proto = out.File
+ file_protoc_gen_openapiv3_options_openapiv3_proto_rawDesc = nil
+ file_protoc_gen_openapiv3_options_openapiv3_proto_goTypes = nil
+ file_protoc_gen_openapiv3_options_openapiv3_proto_depIdxs = nil
+}