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 +}