From 70157c7548c3dad8312e83be0b3ea6b36cc6dded Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 00:23:20 -0700
Subject: [PATCH 01/17] Add support for OpenAPI YAML annotations

---
 internal/descriptor/apiconfig/apiconfig.pb.go | 634 +++++++++++++-
 internal/descriptor/apiconfig/apiconfig.proto |  45 +
 internal/descriptor/grpc_api_configuration.go |  48 ++
 .../descriptor/grpc_api_configuration_test.go | 108 +++
 internal/descriptor/registry.go               | 131 +++
 internal/descriptor/registry_test.go          | 166 ++++
 internal/descriptor/types.go                  |   5 +
 .../internal/genopenapi/template.go           |  68 +-
 .../internal/genopenapi/template_test.go      | 795 ++++++++++++------
 protoc-gen-openapiv2/main.go                  |   8 +
 10 files changed, 1735 insertions(+), 273 deletions(-)

diff --git a/internal/descriptor/apiconfig/apiconfig.pb.go b/internal/descriptor/apiconfig/apiconfig.pb.go
index b4271c1e3a2..1df7389863a 100644
--- a/internal/descriptor/apiconfig/apiconfig.pb.go
+++ b/internal/descriptor/apiconfig/apiconfig.pb.go
@@ -1,13 +1,14 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.12.0
+// 	protoc        v3.13.0
 // source: internal/descriptor/apiconfig/apiconfig.proto
 
 package apiconfig
 
 import (
 	proto "github.com/golang/protobuf/proto"
+	options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	annotations "google.golang.org/genproto/googleapis/api/annotations"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -83,6 +84,414 @@ func (x *GrpcAPIService) GetHttp() *annotations.Http {
 	return nil
 }
 
+// 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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[1]
+		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_apiconfig_apiconfig_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{1}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[2]
+		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_apiconfig_apiconfig_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{2}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[3]
+		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_apiconfig_apiconfig_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{3}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[4]
+		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_apiconfig_apiconfig_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{4}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[5]
+		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_apiconfig_apiconfig_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{5}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[6]
+		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_apiconfig_apiconfig_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{6}
+}
+
+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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[7]
+		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_apiconfig_apiconfig_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{7}
+}
+
+func (x *OpenAPIConfig) GetOpenapiOptions() *OpenAPIOptions {
+	if x != nil {
+		return x.OpenapiOptions
+	}
+	return nil
+}
+
 var File_internal_descriptor_apiconfig_apiconfig_proto protoreflect.FileDescriptor
 
 var file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc = []byte{
@@ -93,15 +502,93 @@ var file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc = []byte{
 	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
 	0x72, 0x2e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x15, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0e, 0x47, 0x72, 0x70, 0x63, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e,
-	0x48, 0x74, 0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x42, 0x49, 0x5a, 0x47, 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, 0x61, 0x70, 0x69, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x74, 0x6f, 0x1a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x6f,
+	0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x22, 0x36, 0x0a, 0x0e, 0x47, 0x72, 0x70, 0x63, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x10, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74,
+	0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 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, 0x32, 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, 0x32,
+	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, 0x32,
+	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, 0x32, 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,
+	0x32, 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, 0xca, 0x03, 0x0a,
+	0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
+	0x51, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x57, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x3f, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x5a, 0x0a, 0x07, 0x6d,
+	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x03, 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,
+	0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x5a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 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, 0x61, 0x70, 0x69, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x54, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x3e, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x74, 0x0a, 0x0d, 0x4f, 0x70, 0x65,
+	0x6e, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0f, 0x6f, 0x70,
+	0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x3a, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	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,
+	0x49, 0x5a, 0x47, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
 }
 
 var (
@@ -116,18 +603,41 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_rawDescGZIP() []byte {
 	return file_internal_descriptor_apiconfig_apiconfig_proto_rawDescData
 }
 
-var file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
 var file_internal_descriptor_apiconfig_apiconfig_proto_goTypes = []interface{}{
-	(*GrpcAPIService)(nil),   // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService
-	(*annotations.Http)(nil), // 1: google.api.Http
+	(*GrpcAPIService)(nil),       // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService
+	(*OpenAPIFileOption)(nil),    // 1: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption
+	(*OpenAPIMethodOption)(nil),  // 2: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption
+	(*OpenAPIMessageOption)(nil), // 3: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption
+	(*OpenAPIServiceOption)(nil), // 4: grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption
+	(*OpenAPIFieldOption)(nil),   // 5: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption
+	(*OpenAPIOptions)(nil),       // 6: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions
+	(*OpenAPIConfig)(nil),        // 7: grpc.gateway.internal.descriptor.apiconfig.OpenAPIConfig
+	(*annotations.Http)(nil),     // 8: google.api.Http
+	(*options.Swagger)(nil),      // 9: grpc.gateway.protoc_gen_openapiv2.options.Swagger
+	(*options.Operation)(nil),    // 10: grpc.gateway.protoc_gen_openapiv2.options.Operation
+	(*options.Schema)(nil),       // 11: grpc.gateway.protoc_gen_openapiv2.options.Schema
+	(*options.Tag)(nil),          // 12: grpc.gateway.protoc_gen_openapiv2.options.Tag
+	(*options.JSONSchema)(nil),   // 13: grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
 }
 var file_internal_descriptor_apiconfig_apiconfig_proto_depIdxs = []int32{
-	1, // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService.http:type_name -> google.api.Http
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
+	8,  // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService.http:type_name -> google.api.Http
+	9,  // 1: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Swagger
+	10, // 2: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Operation
+	11, // 3: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Schema
+	12, // 4: grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Tag
+	13, // 5: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
+	1,  // 6: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.file:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption
+	2,  // 7: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.method:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption
+	3,  // 8: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.message:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption
+	4,  // 9: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.service:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption
+	5,  // 10: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.field:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption
+	6,  // 11: grpc.gateway.internal.descriptor.apiconfig.OpenAPIConfig.openapi_options:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions
+	12, // [12:12] is the sub-list for method output_type
+	12, // [12:12] is the sub-list for method input_type
+	12, // [12:12] is the sub-list for extension type_name
+	12, // [12:12] is the sub-list for extension extendee
+	0,  // [0:12] is the sub-list for field type_name
 }
 
 func init() { file_internal_descriptor_apiconfig_apiconfig_proto_init() }
@@ -148,6 +658,90 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_init() {
 				return nil
 			}
 		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIFileOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIMethodOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIMessageOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIServiceOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIFieldOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIOptions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -155,7 +749,7 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   1,
+			NumMessages:   8,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/internal/descriptor/apiconfig/apiconfig.proto b/internal/descriptor/apiconfig/apiconfig.proto
index aeecd777384..d8ab71d0396 100644
--- a/internal/descriptor/apiconfig/apiconfig.proto
+++ b/internal/descriptor/apiconfig/apiconfig.proto
@@ -5,6 +5,7 @@ package grpc.gateway.internal.descriptor.apiconfig;
 option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig";
 
 import "google/api/http.proto";
+import "protoc-gen-openapiv2/options/openapiv2.proto";
 
 // GrpcAPIService represents a stripped down version of google.api.Service .
 // Compare to https://github.com/googleapis/googleapis/blob/master/google/api/service.proto
@@ -19,3 +20,47 @@ message GrpcAPIService {
 	// Http Rule.
     google.api.Http http = 1;
 }
+
+// OpenAPIFileOption represents OpenAPI options on a file
+message OpenAPIFileOption {
+    string file = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Swagger option = 2;
+}
+
+// OpenAPIMethodOption represents OpenAPI options on a method
+message OpenAPIMethodOption {
+    string method = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Operation option = 2;
+}
+
+// OpenAPIMessageOption represents OpenAPI options on a message
+message OpenAPIMessageOption {
+    string message = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Schema option = 2;
+}
+
+// OpenAPIServiceOption represents OpenAPI options on a service
+message OpenAPIServiceOption {
+    string service = 1; // ex: Service
+    grpc.gateway.protoc_gen_openapiv2.options.Tag option = 2;
+}
+
+// OpenAPIFieldOption represents OpenAPI options on a field
+message OpenAPIFieldOption {
+    string field = 1;
+    grpc.gateway.protoc_gen_openapiv2.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/grpc_api_configuration.go b/internal/descriptor/grpc_api_configuration.go
index c369f93406f..8e13af97eca 100644
--- a/internal/descriptor/grpc_api_configuration.go
+++ b/internal/descriptor/grpc_api_configuration.go
@@ -69,3 +69,51 @@ func (r *Registry) LoadGrpcAPIServiceFromYAML(yamlFile string) error {
 
 	return registerHTTPRulesFromGrpcAPIService(r, service, yamlFile)
 }
+
+func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*apiconfig.OpenAPIConfig, error) {
+	jsonContents, err := yaml.YAMLToJSON(yamlFileContents)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert OpenAPI Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err)
+	}
+
+	// Reject unknown fields because OpenAPIConfig is only used here
+	unmarshaler := protojson.UnmarshalOptions{
+		DiscardUnknown: false,
+	}
+
+	openapiConfiguration := apiconfig.OpenAPIConfig{}
+	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
+		return nil, fmt.Errorf("failed to parse gRPC API Configuration from YAML in '%v': %v", yamlSourceLogName, err)
+	}
+
+	return &openapiConfiguration, nil
+}
+
+func registerOpenAPIOptions(registry *Registry, openAPIConfig *apiconfig.OpenAPIConfig, yamlSourceLogName string) error {
+	if openAPIConfig.OpenapiOptions == nil {
+		// Nothing to do
+		return nil
+	}
+
+	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
+		return fmt.Errorf("failed to register option in %s: %s", yamlSourceLogName, err)
+	}
+	return nil
+}
+
+// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file
+// and registers the OpenAPI options the given registry.
+// This must be done after loading the proto file.
+func (r *Registry) LoadOpenAPIConfigFromYAML(yamlFile string) error {
+	yamlFileContents, err := ioutil.ReadFile(yamlFile)
+	if err != nil {
+		return fmt.Errorf("failed to read gRPC API Configuration description from '%v': %v", yamlFile, err)
+	}
+
+	config, err := loadOpenAPIConfigFromYAML(yamlFileContents, yamlFile)
+	if err != nil {
+		return err
+	}
+
+	return registerOpenAPIOptions(r, config, yamlFile)
+}
diff --git a/internal/descriptor/grpc_api_configuration_test.go b/internal/descriptor/grpc_api_configuration_test.go
index e6ac3d10aa3..b5aad2f37a9 100644
--- a/internal/descriptor/grpc_api_configuration_test.go
+++ b/internal/descriptor/grpc_api_configuration_test.go
@@ -3,6 +3,8 @@ package descriptor
 import (
 	"strings"
 	"testing"
+
+	"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 )
 
 func TestLoadGrpcAPIServiceFromYAMLInvalidType(t *testing.T) {
@@ -147,3 +149,109 @@ http:
 		t.Errorf("some.other.service has %v additional bindings when it should not have any. Got: %v", len(second.GetAdditionalBindings()), second.GetAdditionalBindings())
 	}
 }
+
+func TestLoadOpenAPIConfigFromYAMLRejectInvalidYAML(t *testing.T) {
+	config, err := loadOpenAPIConfigFromYAML([]byte(`
+openapiOptions:
+file:
+- file: test.proto
+  - option:
+      schemes:
+        - HTTP
+        - HTTPS
+        - WSS
+      securityDefinitions:
+        security:
+          ApiKeyAuth:
+            type: TYPE_API_KEY
+            in: IN_HEADER
+            name: "X-API-Key"
+`), "invalidyaml")
+	if err == nil {
+		t.Fatal(err)
+	}
+
+	if !strings.Contains(err.Error(), "line 4") {
+		t.Errorf("Expected yaml error to be detected in line 4. Got other error: %v", err)
+	}
+
+	if config != nil {
+		t.Fatal("Config returned")
+	}
+}
+
+func TestLoadOpenAPIConfigFromYAML(t *testing.T) {
+	config, err := loadOpenAPIConfigFromYAML([]byte(`
+openapiOptions:
+  file:
+  - file: test.proto
+    option:
+      schemes:
+      - HTTP
+      - HTTPS
+      - WSS
+      securityDefinitions:
+        security:
+          ApiKeyAuth:
+            type: TYPE_API_KEY
+            in: IN_HEADER
+            name: "X-API-Key"
+`), "openapi_options")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if config.OpenapiOptions == nil {
+		t.Fatal("OpenAPIOptions is empty")
+	}
+
+	opts := config.OpenapiOptions
+	if numFileOpts := len(opts.File); numFileOpts != 1 {
+		t.Fatalf("expected 1 file option but got %d", numFileOpts)
+	}
+
+	fileOpt := opts.File[0]
+
+	if fileOpt.File != "test.proto" {
+		t.Fatalf("file option has unexpected binding %s", fileOpt.File)
+	}
+
+	swaggerOpt := fileOpt.Option
+
+	if swaggerOpt == nil {
+		t.Fatal("expected option to be set")
+	}
+
+	if numSchemes := len(swaggerOpt.Schemes); numSchemes != 3 {
+		t.Fatalf("expected 3 schemes but got %d", numSchemes)
+	}
+	if swaggerOpt.Schemes[0] != options.Scheme_HTTP {
+		t.Fatalf("expected first scheme to be HTTP but got %s", swaggerOpt.Schemes[0])
+	}
+	if swaggerOpt.Schemes[1] != options.Scheme_HTTPS {
+		t.Fatalf("expected second scheme to be HTTPS but got %s", swaggerOpt.Schemes[1])
+	}
+	if swaggerOpt.Schemes[2] != options.Scheme_WSS {
+		t.Fatalf("expected third scheme to be WSS but got %s", swaggerOpt.Schemes[2])
+	}
+
+	if swaggerOpt.SecurityDefinitions == nil {
+		t.Fatal("expected securityDefinitions to be set")
+	}
+	if numSecOpts := len(swaggerOpt.SecurityDefinitions.Security); numSecOpts != 1 {
+		t.Fatalf("expected 1 security option but got %d", numSecOpts)
+	}
+	secOpt, ok := swaggerOpt.SecurityDefinitions.Security["ApiKeyAuth"]
+	if !ok {
+		t.Fatal("no SecurityScheme for key \"ApiKeyAuth\"")
+	}
+	if secOpt.Type != options.SecurityScheme_TYPE_API_KEY {
+		t.Fatalf("expected scheme type to be TYPE_API_KEY but got %s", secOpt.Type)
+	}
+	if secOpt.In != options.SecurityScheme_IN_HEADER {
+		t.Fatalf("expected scheme  in to be IN_HEADER but got %s", secOpt.In)
+	}
+	if secOpt.Name != "X-API-Key" {
+		t.Fatalf("expected name to be X-API-Key but got %s", secOpt.Name)
+	}
+}
diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go
index 060ef74bfbf..f1518f9e84f 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -7,6 +7,8 @@ import (
 	"strings"
 
 	"github.com/golang/glog"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	"google.golang.org/genproto/googleapis/api/annotations"
 	"google.golang.org/protobuf/types/descriptorpb"
 	"google.golang.org/protobuf/types/pluginpb"
@@ -87,6 +89,22 @@ type Registry struct {
 	// warnOnUnboundMethods causes the registry to emit warning logs if an RPC method
 	// has no HttpRule annotation.
 	warnOnUnboundMethods bool
+
+	// fileOptions is a mapping of file name to additional OpenAPI file options
+	fileOptions map[string]*options.Swagger
+
+	// methodOptions is a mapping of fully-qualified method name to additional OpenAPI method options
+	methodOptions map[string]*options.Operation
+
+	// messageOptions is a mapping of fully-qualified message name to additional OpenAPI message options
+	messageOptions map[string]*options.Schema
+
+	//serviceOptions is a mapping of fully-qualified service name to additional OpenAPI service options
+	serviceOptions map[string]*options.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
+	fieldOptions map[string]*options.JSONSchema
 }
 
 type repeatedFieldSeparator struct {
@@ -107,6 +125,11 @@ func NewRegistry() *Registry {
 			name: "csv",
 			sep:  ',',
 		},
+		fileOptions:    make(map[string]*options.Swagger),
+		methodOptions:  make(map[string]*options.Operation),
+		messageOptions: make(map[string]*options.Schema),
+		serviceOptions: make(map[string]*options.Tag),
+		fieldOptions:   make(map[string]*options.JSONSchema),
 	}
 }
 
@@ -582,3 +605,111 @@ func (r *Registry) packageIdentityName(f *descriptorpb.FileDescriptorProto) stri
 	}
 	return f.GetPackage()
 }
+
+// RegisterOpenAPIOptions registers OpenAPI options
+func (r *Registry) RegisterOpenAPIOptions(opts *apiconfig.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.fileOptions[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 !strings.HasPrefix(qualifiedMethod, ".") {
+			qualifiedMethod = "." + qualifiedMethod
+		}
+		if _, ok := methods[qualifiedMethod]; !ok {
+			return fmt.Errorf("no method %s found", opt.Method)
+		}
+		r.methodOptions[qualifiedMethod] = opt.Option
+	}
+
+	for _, opt := range opts.Message {
+		qualifiedMessage := opt.Message
+		if !strings.HasPrefix(qualifiedMessage, ".") {
+			qualifiedMessage = "." + qualifiedMessage
+		}
+		if _, ok := r.msgs[qualifiedMessage]; !ok {
+			return fmt.Errorf("no message %s found", opt.Message)
+		}
+		r.messageOptions[qualifiedMessage] = opt.Option
+	}
+
+	for _, opt := range opts.Service {
+		qualifiedService := opt.Service
+		if !strings.HasPrefix(qualifiedService, ".") {
+			qualifiedService = "." + qualifiedService
+		}
+		if _, ok := services[qualifiedService]; !ok {
+			return fmt.Errorf("no service %s found", opt.Service)
+		}
+		r.serviceOptions[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 !strings.HasPrefix(qualifiedField, ".") {
+			qualifiedField = "." + qualifiedField
+		}
+		if _, ok := fields[qualifiedField]; !ok {
+			return fmt.Errorf("no field %s found", opt.Field)
+		}
+		r.fieldOptions[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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
+
+// 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
+}
diff --git a/internal/descriptor/registry_test.go b/internal/descriptor/registry_test.go
index c96ad253916..e9f554a613d 100644
--- a/internal/descriptor/registry_test.go
+++ b/internal/descriptor/registry_test.go
@@ -3,6 +3,7 @@ package descriptor
 import (
 	"testing"
 
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
 	"google.golang.org/protobuf/encoding/prototext"
 	"google.golang.org/protobuf/types/descriptorpb"
 	"google.golang.org/protobuf/types/pluginpb"
@@ -635,6 +636,171 @@ func TestUnboundExternalHTTPRules(t *testing.T) {
 	assertStringSlice(t, "unbound external HTTP rules", reg.UnboundExternalHTTPRules(), []string{})
 }
 
+func TestRegisterOpenAPIOptions(t *testing.T) {
+	codeReq := `file_to_generate: 'a.proto'
+	proto_file <
+		name: 'a.proto'
+		package: 'example.foo'
+		options < go_package: 'foo' >
+		message_type <
+			name: 'ExampleMessage'
+			field <
+				name: 'str'
+				label: LABEL_OPTIONAL
+				type: TYPE_STRING
+				number: 1
+			>
+		>
+		service <
+			name: "AService"
+			method <
+				name: "Meth"
+				input_type: "ExampleMessage"
+				output_type: "ExampleMessage"
+				options <
+					[google.api.http] < post: "/v1/a" body: "*" >
+				>
+			>
+		>
+	>
+	`
+	for _, tcase := range []struct {
+		options   *apiconfig.OpenAPIOptions
+		shouldErr bool
+		desc      string
+	}{
+		{
+			desc: "handle nil options",
+		},
+		{
+			desc: "successfully add options if referenced entity exists",
+			options: &apiconfig.OpenAPIOptions{
+				File: []*apiconfig.OpenAPIFileOption{
+					{
+						File: "a.proto",
+					},
+				},
+				Method: []*apiconfig.OpenAPIMethodOption{
+					{
+						Method: "example.foo.AService.Meth",
+					},
+				},
+				Message: []*apiconfig.OpenAPIMessageOption{
+					{
+						Message: "example.foo.ExampleMessage",
+					},
+				},
+				Service: []*apiconfig.OpenAPIServiceOption{
+					{
+						Service: "example.foo.AService",
+					},
+				},
+				Field: []*apiconfig.OpenAPIFieldOption{
+					{
+						Field: "example.foo.ExampleMessage.str",
+					},
+				},
+			},
+		},
+		{
+			desc: "successfully accept fully qualified names",
+			options: &apiconfig.OpenAPIOptions{
+				File: []*apiconfig.OpenAPIFileOption{
+					{
+						File: "a.proto",
+					},
+				},
+				Method: []*apiconfig.OpenAPIMethodOption{
+					{
+						Method: ".example.foo.AService.Meth",
+					},
+				},
+				Message: []*apiconfig.OpenAPIMessageOption{
+					{
+						Message: ".example.foo.ExampleMessage",
+					},
+				},
+				Service: []*apiconfig.OpenAPIServiceOption{
+					{
+						Service: ".example.foo.AService",
+					},
+				},
+				Field: []*apiconfig.OpenAPIFieldOption{
+					{
+						Field: ".example.foo.ExampleMessage.str",
+					},
+				},
+			},
+		},
+		{
+			desc: "error if file does not exist",
+			options: &apiconfig.OpenAPIOptions{
+				File: []*apiconfig.OpenAPIFileOption{
+					{
+						File: "b.proto",
+					},
+				},
+			},
+			shouldErr: true,
+		},
+		{
+			desc: "error if method does not exist",
+			options: &apiconfig.OpenAPIOptions{
+				Method: []*apiconfig.OpenAPIMethodOption{
+					{
+						Method: "example.foo.AService.Meth2",
+					},
+				},
+			},
+			shouldErr: true,
+		},
+		{
+			desc: "error if message does not exist",
+			options: &apiconfig.OpenAPIOptions{
+				Message: []*apiconfig.OpenAPIMessageOption{
+					{
+						Message: "example.foo.NonexistentMessage",
+					},
+				},
+			},
+			shouldErr: true,
+		},
+		{
+			desc: "error if service does not exist",
+			options: &apiconfig.OpenAPIOptions{
+				Service: []*apiconfig.OpenAPIServiceOption{
+					{
+						Service: "example.foo.AService1",
+					},
+				},
+			},
+			shouldErr: true,
+		},
+		{
+			desc: "error if field does not exist",
+			options: &apiconfig.OpenAPIOptions{
+				Field: []*apiconfig.OpenAPIFieldOption{
+					{
+						Field: "example.foo.ExampleMessage.str1",
+					},
+				},
+			},
+			shouldErr: true,
+		},
+	} {
+		t.Run(tcase.desc, func(t *testing.T) {
+			reg := NewRegistry()
+			if err := load(t, reg, codeReq); err != nil {
+				t.Fatalf("got unexpected error when loading request: %s", err)
+			}
+			err := reg.RegisterOpenAPIOptions(tcase.options)
+			if (err != nil) != tcase.shouldErr {
+				t.Fatalf("got unexpected error: %s", err)
+			}
+		})
+	}
+}
+
 func assertStringSlice(t *testing.T, message string, got, want []string) {
 	if len(got) != len(want) {
 		t.Errorf("%s = %#v len(%d); want %#v len(%d)", message, got, len(got), want, len(want))
diff --git a/internal/descriptor/types.go b/internal/descriptor/types.go
index ab3e3036ce9..1fd716a915f 100644
--- a/internal/descriptor/types.go
+++ b/internal/descriptor/types.go
@@ -255,6 +255,11 @@ type Field struct {
 	ForcePrefixedName bool
 }
 
+// FQFN returns a fully qualified field name of this field.
+func (f *Field) FQFN() string {
+	return strings.Join([]string{f.Message.FQMN(), f.GetName()}, ".")
+}
+
 // Parameter is a parameter provided in http requests
 type Parameter struct {
 	// FieldPath is a path to a proto field which this parameter is mapped to.
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template.go b/protoc-gen-openapiv2/internal/genopenapi/template.go
index 856cdd45fd7..299f5fa626c 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template.go
@@ -362,7 +362,7 @@ func renderMessagesAsDefinition(messages messageMap, d openapiDefinitionsObject,
 		if err := updateOpenAPIDataFromComments(reg, &schema, msg, msgComments, false); err != nil {
 			panic(err)
 		}
-		opts, err := extractSchemaOptionFromMessageDescriptor(msg.DescriptorProto)
+		opts, err := getMessageOpenAPIOption(reg, msg)
 		if err != nil {
 			panic(err)
 		}
@@ -501,7 +501,7 @@ func schemaOfField(f *descriptor.Field, reg *descriptor.Registry, refs refMap) o
 		}
 	}
 
-	if j, err := extractJSONSchemaFromFieldDescriptor(f.FieldDescriptorProto); err == nil {
+	if j, err := getFieldOpenAPIOption(reg, f); err == nil {
 		updateswaggerObjectFromJSONSchema(&ret, j, reg, f)
 	}
 
@@ -1042,7 +1042,7 @@ func renderServices(services []*descriptor.Service, paths openapiPathsObject, re
 					panic(err)
 				}
 
-				opts, err := extractOperationOptionFromMethodDescriptor(meth.MethodDescriptorProto)
+				opts, err := getMethodOpenAPIOption(reg, meth)
 				if opts != nil {
 					if err != nil {
 						panic(err)
@@ -1202,7 +1202,7 @@ func applyTemplate(p param) (*openapiSwaggerObject, error) {
 	}
 
 	// There may be additional options in the OpenAPI option in the proto.
-	spb, err := extractOpenAPIOptionFromFileDescriptor(p.FileDescriptorProto)
+	spb, err := getFileOpenAPIOption(p.reg, p.File)
 	if err != nil {
 		panic(err)
 	}
@@ -1791,6 +1791,66 @@ func extractJSONSchemaFromFieldDescriptor(fd *descriptorpb.FieldDescriptorProto)
 	return opts, nil
 }
 
+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.GetOpenAPIMethodOption(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.GetOpenAPIMessageOption(msg.FQMN())
+	if !ok {
+		return nil, nil
+	}
+	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.GetOpenAPIFileOption(*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.GetOpenAPIFieldOption(fd.FQFN())
+	if !ok {
+		return nil, nil
+	}
+	return opts, nil
+}
+
 func protoJSONSchemaToOpenAPISchemaCore(j *openapi_options.JSONSchema, reg *descriptor.Registry, refs refMap) schemaCore {
 	ret := schemaCore{}
 
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template_test.go b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
index 4f032dc4d64..9291afac12d 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template_test.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
@@ -10,6 +10,7 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
 	openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
@@ -1078,147 +1079,180 @@ func TestApplyTemplateMultiService(t *testing.T) {
 }
 
 func TestApplyTemplateOverrideOperationID(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:    &descriptorpb.MethodOptions{},
-	}
-	openapiOperation := openapi_options.Operation{
-		OperationId: "MyExample",
-	}
-	proto.SetExtension(proto.Message(meth.Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
-	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"),
-			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
-			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
-			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
-		},
-		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
+	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"),
+				Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
+				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
+				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+			},
+			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 := "MyExample", result.Paths["/v1/echo"].Get.OperationID; !reflect.DeepEqual(is, want) {
-		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
+	verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *apiconfig.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.RegisterOpenAPIOptions(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.Paths["/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 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))
+		}
 	}
 
-	// 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",
 	}
+
+	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 := &apiconfig.OpenAPIOptions{
+			Method: []*apiconfig.OpenAPIMethodOption{
+				{
+					Method: "example.ExampleService.Example",
+					Option: &openapiOperation,
+				},
+			},
+		}
+		verifyTemplateFromReq(t, reg, file, opts)
+	})
 }
 
 func TestApplyTemplateExtensions(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:    &descriptorpb.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"),
-			Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
-			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
-			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
-			Options:        &descriptorpb.FileOptions{},
-		},
-		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
+	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"),
+				Dependency:     []string{"a.example/b/c.proto", "a.example/d/e.proto"},
+				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
+				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
+				Options:        &descriptorpb.FileOptions{},
+			},
+			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{
@@ -1243,7 +1277,6 @@ func TestApplyTemplateExtensions(t *testing.T) {
 			},
 		},
 	}
-	proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
 	openapiOperation := openapi_options.Operation{
 		Responses: map[string]*openapi_options.Response{
 			"200": {
@@ -1256,90 +1289,122 @@ func TestApplyTemplateExtensions(t *testing.T) {
 			"x-op-foo": {Kind: &structpb.Value_StringValue{StringValue: "baz"}},
 		},
 	}
-	proto.SetExtension(proto.Message(meth.Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
-	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 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)
+	verifyTemplateExtensions := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
+		opts *apiconfig.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.Fatalf("marshaler.Unmarshal failed: %v", err)
+			t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
+			return
 		}
-		want := []string{"baz"}
-		if diff := cmp.Diff(got, want); diff != "" {
-			t.Errorf(diff)
+		if opts != nil {
+			if err := reg.RegisterOpenAPIOptions(opts); err != nil {
+				t.Fatalf("failed to register OpenAPI annotations: %s", err)
+			}
 		}
-	}
-	{
-		var got string
-		err = marshaler.Unmarshal(result.extensions[1].value, &got)
+		result, err := applyTemplate(param{File: fileCL, reg: reg})
 		if err != nil {
-			t.Fatalf("marshaler.Unmarshal failed: %v", err)
+			t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
+			return
 		}
-		want := "bar"
-		if diff := cmp.Diff(got, want); diff != "" {
-			t.Errorf(diff)
+		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.Errorf(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.Errorf(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)
-	}
+		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)
-	}
+		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.Get
-		response = v.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)
+		var operation *openapiOperationObject
+		var response openapiResponseObject
+		for _, v := range result.Paths {
+			operation = v.Get
+			response = v.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)
+		}
 	}
+	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 := &apiconfig.OpenAPIOptions{
+			File: []*apiconfig.OpenAPIFileOption{
+				{
+					File:   "example.proto",
+					Option: &swagger,
+				},
+			},
+			Method: []*apiconfig.OpenAPIMethodOption{
+				{
+					Method: "example.ExampleService.Example",
+					Option: &openapiOperation,
+				},
+			},
+		}
+		reg := descriptor.NewRegistry()
+		verifyTemplateExtensions(t, reg, file, opts)
+	})
 }
 
 func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
@@ -2229,16 +2294,19 @@ func TestFQMNtoOpenAPIName(t *testing.T) {
 
 func TestSchemaOfField(t *testing.T) {
 	type test struct {
-		field    *descriptor.Field
-		refs     refMap
-		expected openapiSchemaObject
+		field          *descriptor.Field
+		refs           refMap
+		expected       openapiSchemaObject
+		openAPIOptions *apiconfig.OpenAPIOptions
 	}
 
-	var fieldOptions = new(descriptorpb.FieldOptions)
-	proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, &openapi_options.JSONSchema{
+	jsonSchema := &openapi_options.JSONSchema{
 		Title:       "field title",
 		Description: "field description",
-	})
+	}
+
+	var fieldOptions = new(descriptorpb.FieldOptions)
+	proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
 
 	tests := []test{
 		{
@@ -2640,55 +2708,177 @@ func TestSchemaOfField(t *testing.T) {
 				Description: "field description",
 			},
 		},
-	}
-
-	reg := descriptor.NewRegistry()
-	reg.Load(&pluginpb.CodeGeneratorRequest{
-		ProtoFile: []*descriptorpb.FileDescriptorProto{
-			{
-				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
-				Name:           proto.String("example.proto"),
-				Package:        proto.String("example"),
-				Dependency:     []string{},
-				MessageType: []*descriptorpb.DescriptorProto{
+		{
+			field: &descriptor.Field{
+				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
+					Name:     proto.String("map_field_option"),
+					Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
+					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+					TypeName: proto.String(".example.Message.MapFieldEntry"),
+				},
+			},
+			openAPIOptions: &apiconfig.OpenAPIOptions{
+				Field: []*apiconfig.OpenAPIFieldOption{
 					{
-						Name: proto.String("Message"),
-						Field: []*descriptorpb.FieldDescriptorProto{
-							{
-								Name: proto.String("value"),
-								Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
-							},
-						},
-						NestedType: []*descriptorpb.DescriptorProto{
-							{
-								Name:    proto.String("MapFieldEntry"),
-								Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)},
-								Field: []*descriptorpb.FieldDescriptorProto{
-									{},
-									{
-										Name:  proto.String("value"),
-										Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
-										Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
-									},
-								},
-							},
-						},
+						Field:  "example.Message.map_field_option",
+						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: &apiconfig.OpenAPIOptions{
+				Field: []*apiconfig.OpenAPIFieldOption{
 					{
-						Name: proto.String("Empty"),
+						Field:  "example.Message.array_field_option",
+						Option: jsonSchema,
 					},
 				},
-				EnumType: []*descriptorpb.EnumDescriptorProto{
+			},
+			refs: make(refMap),
+			expected: openapiSchemaObject{
+				schemaCore: schemaCore{
+					Type:  "array",
+					Items: (*openapiItemsObject)(&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: &apiconfig.OpenAPIOptions{
+				Field: []*apiconfig.OpenAPIFieldOption{
 					{
-						Name: proto.String("Message"),
+						Field:  "example.Message.primitive_field_option",
+						Option: jsonSchema,
 					},
 				},
-				Service: []*descriptorpb.ServiceDescriptorProto{},
+			},
+			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_option"),
+					Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
+					TypeName: proto.String(".example.Empty"),
+				},
+			},
+			openAPIOptions: &apiconfig.OpenAPIOptions{
+				Field: []*apiconfig.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",
+			},
+		},
+	}
 	for _, test := range tests {
+		reg := descriptor.NewRegistry()
+		req := &pluginpb.CodeGeneratorRequest{
+			ProtoFile: []*descriptorpb.FileDescriptorProto{
+				{
+					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
+					Name:           proto.String("example.proto"),
+					Package:        proto.String("example"),
+					Dependency:     []string{},
+					MessageType: []*descriptorpb.DescriptorProto{
+						{
+							Name: proto.String("Message"),
+							Field: []*descriptorpb.FieldDescriptorProto{
+								{
+									Name: proto.String("value"),
+									Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+								},
+								test.field.FieldDescriptorProto,
+							},
+							NestedType: []*descriptorpb.DescriptorProto{
+								{
+									Name:    proto.String("MapFieldEntry"),
+									Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)},
+									Field: []*descriptorpb.FieldDescriptorProto{
+										{},
+										{
+											Name:  proto.String("value"),
+											Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
+											Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
+										},
+									},
+								},
+							},
+						},
+						{
+							Name: proto.String("Empty"),
+						},
+					},
+					EnumType: []*descriptorpb.EnumDescriptorProto{
+						{
+							Name: proto.String("Message"),
+						},
+					},
+					Service: []*descriptorpb.ServiceDescriptorProto{},
+				},
+			},
+		}
+		reg.Load(req)
+
+		// set field's parent message pointer to message so field can resolve its FQFN
+		test.field.Message = &descriptor.Message{
+			DescriptorProto: req.ProtoFile[0].MessageType[0],
+			File: &descriptor.File{
+				FileDescriptorProto: req.ProtoFile[0],
+			},
+		}
+
+		if test.openAPIOptions != nil {
+			if err := reg.RegisterOpenAPIOptions(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
@@ -2704,10 +2894,11 @@ func TestSchemaOfField(t *testing.T) {
 func TestRenderMessagesAsDefinition(t *testing.T) {
 
 	tests := []struct {
-		descr    string
-		msgDescs []*descriptorpb.DescriptorProto
-		schema   map[string]openapi_options.Schema // per-message schema to add
-		defs     openapiDefinitionsObject
+		descr          string
+		msgDescs       []*descriptorpb.DescriptorProto
+		schema         map[string]openapi_options.Schema // per-message schema to add
+		defs           openapiDefinitionsObject
+		openAPIOptions *apiconfig.OpenAPIOptions
 	}{
 		{
 			descr: "no OpenAPI options",
@@ -2831,6 +3022,64 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
 				},
 			},
 		},
+		{
+			descr: "JSONSchema options from registry",
+			msgDescs: []*descriptorpb.DescriptorProto{
+				{Name: proto.String("Message")},
+			},
+			openAPIOptions: &apiconfig.OpenAPIOptions{
+				Message: []*apiconfig.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,
+				},
+			},
+		},
 	}
 
 	for _, test := range tests {
@@ -2873,6 +3122,12 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
 				}
 			}
 
+			if test.openAPIOptions != nil {
+				if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
+					t.Fatalf("failed to register OpenAPI options: %s", err)
+				}
+			}
+
 			refs := make(refMap)
 			actual := make(openapiDefinitionsObject)
 			renderMessagesAsDefinition(msgMap, actual, reg, refs)
@@ -3053,11 +3308,12 @@ func TestUpdateOpenAPIDataFromComments(t *testing.T) {
 
 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
-		useGoTemplate bool
+		descr          string
+		msgDescs       []*descriptorpb.DescriptorProto
+		schema         map[string]openapi_options.Schema // per-message schema to add
+		defs           openapiDefinitionsObject
+		openAPIOptions *apiconfig.OpenAPIOptions
+		useGoTemplate  bool
 	}{
 		{
 			descr: "external docs option",
@@ -3119,6 +3375,41 @@ func TestMessageOptionsWithGoTemplate(t *testing.T) {
 			},
 			useGoTemplate: false,
 		},
+		{
+			descr: "registered OpenAPIOption",
+			msgDescs: []*descriptorpb.DescriptorProto{
+				{Name: proto.String("Message")},
+			},
+			openAPIOptions: &apiconfig.OpenAPIOptions{
+				Message: []*apiconfig.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,
+		},
 	}
 
 	for _, test := range tests {
@@ -3162,6 +3453,12 @@ func TestMessageOptionsWithGoTemplate(t *testing.T) {
 				}
 			}
 
+			if test.openAPIOptions != nil {
+				if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
+					t.Fatalf("failed to register OpenAPI options: %s", err)
+				}
+			}
+
 			refs := make(refMap)
 			actual := make(openapiDefinitionsObject)
 			renderMessagesAsDefinition(msgMap, actual, reg, refs)
diff --git a/protoc-gen-openapiv2/main.go b/protoc-gen-openapiv2/main.go
index 10c5b21393f..c3eb25b9413 100644
--- a/protoc-gen-openapiv2/main.go
+++ b/protoc-gen-openapiv2/main.go
@@ -31,6 +31,7 @@ var (
 	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.")
+	openAPIConfiguration       = flag.String("openapi_configuration", "", "path to OpenAPI Configuration in YAML format")
 )
 
 // Variables set by goreleaser at build time
@@ -113,6 +114,13 @@ func main() {
 		return
 	}
 
+	if *openAPIConfiguration != "" {
+		if err := reg.LoadOpenAPIConfigFromYAML(*openAPIConfiguration); err != nil {
+			emitError(err)
+			return
+		}
+	}
+
 	var targets []*descriptor.File
 	for _, target := range req.FileToGenerate {
 		f, err := reg.LookupFile(target)

From 6f7127e0197aa52ac1724543f0688a12c11d343f Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 00:44:49 -0700
Subject: [PATCH 02/17] Fix Bazel rules

---
 internal/descriptor/BUILD.bazel                      | 5 +++++
 internal/descriptor/apiconfig/BUILD.bazel            | 6 +++++-
 protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel | 1 +
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index 9b798b16005..2719b37feb6 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -15,6 +15,7 @@ go_library(
         "//internal/casing:go_default_library",
         "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
+        "//protoc-gen-openapiv2/options:go_default_library",
         "@com_github_ghodss_yaml//:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@go_googleapis//google/api:annotations_go_proto",
@@ -36,7 +37,11 @@ go_test(
     ],
     embed = [":go_default_library"],
     deps = [
+        "//internal/descriptor/apiconfig:go_default_library"
         "//internal/httprule:go_default_library",
+        "//protoc-gen-openapiv2/options:go_default_library"
+        "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
+        "@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
         "@org_golang_google_protobuf//encoding/prototext:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
         "@org_golang_google_protobuf//types/descriptorpb:go_default_library",
diff --git a/internal/descriptor/apiconfig/BUILD.bazel b/internal/descriptor/apiconfig/BUILD.bazel
index 80dc57a2e3c..29089e351ad 100644
--- a/internal/descriptor/apiconfig/BUILD.bazel
+++ b/internal/descriptor/apiconfig/BUILD.bazel
@@ -10,6 +10,7 @@ proto_library(
         "apiconfig.proto",
     ],
     deps = [
+        "//protoc-gen-openapiv2/options:options_proto",
         "@go_googleapis//google/api:annotations_proto",
     ],
 )
@@ -19,7 +20,10 @@ go_proto_library(
     compilers = ["//:go_apiv2"],
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig",
     proto = ":apiconfig_proto",
-    deps = ["@go_googleapis//google/api:annotations_go_proto"],
+    deps = [
+        "//protoc-gen-openapiv2/options:go_default_library",
+        "@go_googleapis//google/api:annotations_go_proto",
+    ],
 )
 
 go_library(
diff --git a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
index 2da8588b62f..edec6baf010 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
+++ b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
@@ -37,6 +37,7 @@ go_test(
     embed = [":go_default_library"],
     deps = [
         "//internal/descriptor:go_default_library",
+        "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "//runtime:go_default_library",

From a265263a50f130d9607404d8276eea3d44ea37e8 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 00:52:44 -0700
Subject: [PATCH 03/17] Fix more Bazel rules

---
 protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel | 2 --
 protoc-gen-openapiv2/BUILD.bazel                        | 1 -
 protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel    | 3 +--
 3 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel b/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
index 4c9445ab4ce..b8438804017 100644
--- a/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
+++ b/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
@@ -12,7 +12,6 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway/internal/gengateway",
     deps = [
         "//internal/casing:go_default_library",
-        "//internal/descriptor:go_default_library",
         "//internal/generator:go_default_library",
         "//utilities:go_default_library",
         "@com_github_golang_glog//:go_default_library",
@@ -30,7 +29,6 @@ go_test(
     ],
     embed = [":go_default_library"],
     deps = [
-        "//internal/descriptor:go_default_library",
         "//internal/httprule:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
         "@org_golang_google_protobuf//types/descriptorpb:go_default_library",
diff --git a/protoc-gen-openapiv2/BUILD.bazel b/protoc-gen-openapiv2/BUILD.bazel
index 90e711b18d9..6d0bb2cc9b0 100644
--- a/protoc-gen-openapiv2/BUILD.bazel
+++ b/protoc-gen-openapiv2/BUILD.bazel
@@ -8,7 +8,6 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2",
     deps = [
         "//internal/codegenerator:go_default_library",
-        "//internal/descriptor:go_default_library",
         "//protoc-gen-openapiv2/internal/genopenapi:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
diff --git a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
index edec6baf010..02a4408ab20 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
+++ b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
@@ -15,11 +15,11 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/internal/genopenapi",
     deps = [
         "//internal/casing:go_default_library",
-        "//internal/descriptor:go_default_library",
         "//internal/generator:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@com_github_golang_protobuf//descriptor:go_default_library_gen",
+        "@com_github_grpc_ecosystem_grpc_gateway_v2//internal/descriptor:go_default_library",
         "@go_googleapis//google/rpc:status_go_proto",
         "@io_bazel_rules_go//proto/wkt:any_go_proto",
         "@io_bazel_rules_go//proto/wkt:struct_go_proto",
@@ -36,7 +36,6 @@ go_test(
     srcs = ["template_test.go"],
     embed = [":go_default_library"],
     deps = [
-        "//internal/descriptor:go_default_library",
         "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",

From e11bc4b3d90ec3b0ea8eb68498b1b7eaad388583 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 01:07:04 -0700
Subject: [PATCH 04/17] Fix Bazel rule syntax

---
 internal/descriptor/BUILD.bazel | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index 2719b37feb6..beb47b81653 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -37,7 +37,7 @@ go_test(
     ],
     embed = [":go_default_library"],
     deps = [
-        "//internal/descriptor/apiconfig:go_default_library"
+        "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library"
         "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",

From 623bfbdf43e63f1ba9fde6711a1b2ba7809ef07a Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 01:26:15 -0700
Subject: [PATCH 05/17] Fix Bazel with changes from docker run

---
 internal/descriptor/BUILD.bazel                         | 2 +-
 protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel | 2 ++
 protoc-gen-openapiv2/BUILD.bazel                        | 1 +
 protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel    | 3 ++-
 4 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index beb47b81653..e35fb6dfd31 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -39,7 +39,7 @@ go_test(
     deps = [
         "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
-        "//protoc-gen-openapiv2/options:go_default_library"
+        "//protoc-gen-openapiv2/options:go_default_library",
         "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
         "@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
         "@org_golang_google_protobuf//encoding/prototext:go_default_library",
diff --git a/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel b/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
index b8438804017..4c9445ab4ce 100644
--- a/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
+++ b/protoc-gen-grpc-gateway/internal/gengateway/BUILD.bazel
@@ -12,6 +12,7 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway/internal/gengateway",
     deps = [
         "//internal/casing:go_default_library",
+        "//internal/descriptor:go_default_library",
         "//internal/generator:go_default_library",
         "//utilities:go_default_library",
         "@com_github_golang_glog//:go_default_library",
@@ -29,6 +30,7 @@ go_test(
     ],
     embed = [":go_default_library"],
     deps = [
+        "//internal/descriptor:go_default_library",
         "//internal/httprule:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
         "@org_golang_google_protobuf//types/descriptorpb:go_default_library",
diff --git a/protoc-gen-openapiv2/BUILD.bazel b/protoc-gen-openapiv2/BUILD.bazel
index 6d0bb2cc9b0..90e711b18d9 100644
--- a/protoc-gen-openapiv2/BUILD.bazel
+++ b/protoc-gen-openapiv2/BUILD.bazel
@@ -8,6 +8,7 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2",
     deps = [
         "//internal/codegenerator:go_default_library",
+        "//internal/descriptor:go_default_library",
         "//protoc-gen-openapiv2/internal/genopenapi:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
diff --git a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
index 02a4408ab20..edec6baf010 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
+++ b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
@@ -15,11 +15,11 @@ go_library(
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/internal/genopenapi",
     deps = [
         "//internal/casing:go_default_library",
+        "//internal/descriptor:go_default_library",
         "//internal/generator:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "@com_github_golang_glog//:go_default_library",
         "@com_github_golang_protobuf//descriptor:go_default_library_gen",
-        "@com_github_grpc_ecosystem_grpc_gateway_v2//internal/descriptor:go_default_library",
         "@go_googleapis//google/rpc:status_go_proto",
         "@io_bazel_rules_go//proto/wkt:any_go_proto",
         "@io_bazel_rules_go//proto/wkt:struct_go_proto",
@@ -36,6 +36,7 @@ go_test(
     srcs = ["template_test.go"],
     embed = [":go_default_library"],
     deps = [
+        "//internal/descriptor:go_default_library",
         "//internal/descriptor/apiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",

From 852867b009c7bf31cba528bd792c70f7717a39e2 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 11:47:49 -0700
Subject: [PATCH 06/17] Split OpenAPI config from apiconfig

---
 Makefile                                      |   3 +-
 internal/descriptor/apiconfig/BUILD.bazel     |   6 +-
 internal/descriptor/apiconfig/apiconfig.pb.go | 634 +---------------
 internal/descriptor/apiconfig/apiconfig.proto |  45 --
 internal/descriptor/grpc_api_configuration.go |  48 --
 .../descriptor/grpc_api_configuration_test.go | 108 ---
 internal/descriptor/openapi_configuration.go  |  58 ++
 .../descriptor/openapi_configuration_test.go  | 114 +++
 internal/descriptor/openapiconfig/BUILD.bazel |  26 +
 .../openapiconfig/openapiconfig.pb.go         | 688 ++++++++++++++++++
 .../openapiconfig/openapiconfig.proto         |  51 ++
 internal/descriptor/registry.go               |   4 +-
 internal/descriptor/registry_test.go          |  48 +-
 .../internal/genopenapi/template_test.go      |  46 +-
 14 files changed, 1009 insertions(+), 870 deletions(-)
 create mode 100644 internal/descriptor/openapi_configuration.go
 create mode 100644 internal/descriptor/openapi_configuration_test.go
 create mode 100644 internal/descriptor/openapiconfig/BUILD.bazel
 create mode 100644 internal/descriptor/openapiconfig/openapiconfig.pb.go
 create mode 100644 internal/descriptor/openapiconfig/openapiconfig.proto

diff --git a/Makefile b/Makefile
index d25de4a959c..427d7f57de1 100644
--- a/Makefile
+++ b/Makefile
@@ -94,7 +94,8 @@ RUNTIME_TEST_PROTO=runtime/internal/examplepb/example.proto \
 	runtime/internal/examplepb/non_standard_names.proto
 RUNTIME_TEST_SRCS=$(RUNTIME_TEST_PROTO:.proto=.pb.go)
 
-APICONFIG_PROTO=internal/descriptor/apiconfig/apiconfig.proto
+APICONFIG_PROTO=internal/descriptor/apiconfig/apiconfig.proto \
+	internal/descriptor/openapiconfig/openapiconfig.proto
 APICONFIG_SRCS=$(APICONFIG_PROTO:.proto=.pb.go)
 
 EXAMPLE_CLIENT_DIR=examples/internal/clients
diff --git a/internal/descriptor/apiconfig/BUILD.bazel b/internal/descriptor/apiconfig/BUILD.bazel
index 29089e351ad..80dc57a2e3c 100644
--- a/internal/descriptor/apiconfig/BUILD.bazel
+++ b/internal/descriptor/apiconfig/BUILD.bazel
@@ -10,7 +10,6 @@ proto_library(
         "apiconfig.proto",
     ],
     deps = [
-        "//protoc-gen-openapiv2/options:options_proto",
         "@go_googleapis//google/api:annotations_proto",
     ],
 )
@@ -20,10 +19,7 @@ go_proto_library(
     compilers = ["//:go_apiv2"],
     importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig",
     proto = ":apiconfig_proto",
-    deps = [
-        "//protoc-gen-openapiv2/options:go_default_library",
-        "@go_googleapis//google/api:annotations_go_proto",
-    ],
+    deps = ["@go_googleapis//google/api:annotations_go_proto"],
 )
 
 go_library(
diff --git a/internal/descriptor/apiconfig/apiconfig.pb.go b/internal/descriptor/apiconfig/apiconfig.pb.go
index 1df7389863a..b4271c1e3a2 100644
--- a/internal/descriptor/apiconfig/apiconfig.pb.go
+++ b/internal/descriptor/apiconfig/apiconfig.pb.go
@@ -1,14 +1,13 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.25.0
-// 	protoc        v3.13.0
+// 	protoc        v3.12.0
 // source: internal/descriptor/apiconfig/apiconfig.proto
 
 package apiconfig
 
 import (
 	proto "github.com/golang/protobuf/proto"
-	options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	annotations "google.golang.org/genproto/googleapis/api/annotations"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
@@ -84,414 +83,6 @@ func (x *GrpcAPIService) GetHttp() *annotations.Http {
 	return nil
 }
 
-// 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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[1]
-		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_apiconfig_apiconfig_proto_msgTypes[1]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{1}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[2]
-		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_apiconfig_apiconfig_proto_msgTypes[2]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{2}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[3]
-		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_apiconfig_apiconfig_proto_msgTypes[3]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{3}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[4]
-		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_apiconfig_apiconfig_proto_msgTypes[4]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{4}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[5]
-		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_apiconfig_apiconfig_proto_msgTypes[5]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{5}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[6]
-		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_apiconfig_apiconfig_proto_msgTypes[6]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{6}
-}
-
-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{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[7]
-		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_apiconfig_apiconfig_proto_msgTypes[7]
-	if protoimpl.UnsafeEnabled && 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_apiconfig_apiconfig_proto_rawDescGZIP(), []int{7}
-}
-
-func (x *OpenAPIConfig) GetOpenapiOptions() *OpenAPIOptions {
-	if x != nil {
-		return x.OpenapiOptions
-	}
-	return nil
-}
-
 var File_internal_descriptor_apiconfig_apiconfig_proto protoreflect.FileDescriptor
 
 var file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc = []byte{
@@ -502,93 +93,15 @@ var file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc = []byte{
 	0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2e, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f,
 	0x72, 0x2e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x15, 0x67, 0x6f, 0x6f,
 	0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x68, 0x74, 0x74, 0x70, 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, 0x32, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x22, 0x36, 0x0a, 0x0e, 0x47, 0x72, 0x70, 0x63, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x10, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x48, 0x74,
-	0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 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, 0x32, 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, 0x32,
-	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, 0x32,
-	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, 0x32, 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,
-	0x32, 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, 0xca, 0x03, 0x0a,
-	0x0e, 0x4f, 0x70, 0x65, 0x6e, 0x41, 0x50, 0x49, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
-	0x51, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3d, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x57, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x3f, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x5a, 0x0a, 0x07, 0x6d,
-	0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x03, 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,
-	0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x5a, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x18, 0x04, 0x20, 0x03, 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, 0x61, 0x70, 0x69, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x54, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x3e, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x74, 0x0a, 0x0d, 0x4f, 0x70, 0x65,
-	0x6e, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x63, 0x0a, 0x0f, 0x6f, 0x70,
-	0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x3a, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	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,
-	0x49, 0x5a, 0x47, 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, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x74, 0x6f, 0x22, 0x36, 0x0a, 0x0e, 0x47, 0x72, 0x70, 0x63, 0x41, 0x50, 0x49, 0x53, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x10, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x61, 0x70, 0x69, 0x2e,
+	0x48, 0x74, 0x74, 0x70, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x42, 0x49, 0x5a, 0x47, 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, 0x61, 0x70, 0x69, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -603,41 +116,18 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_rawDescGZIP() []byte {
 	return file_internal_descriptor_apiconfig_apiconfig_proto_rawDescData
 }
 
-var file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
+var file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
 var file_internal_descriptor_apiconfig_apiconfig_proto_goTypes = []interface{}{
-	(*GrpcAPIService)(nil),       // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService
-	(*OpenAPIFileOption)(nil),    // 1: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption
-	(*OpenAPIMethodOption)(nil),  // 2: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption
-	(*OpenAPIMessageOption)(nil), // 3: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption
-	(*OpenAPIServiceOption)(nil), // 4: grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption
-	(*OpenAPIFieldOption)(nil),   // 5: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption
-	(*OpenAPIOptions)(nil),       // 6: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions
-	(*OpenAPIConfig)(nil),        // 7: grpc.gateway.internal.descriptor.apiconfig.OpenAPIConfig
-	(*annotations.Http)(nil),     // 8: google.api.Http
-	(*options.Swagger)(nil),      // 9: grpc.gateway.protoc_gen_openapiv2.options.Swagger
-	(*options.Operation)(nil),    // 10: grpc.gateway.protoc_gen_openapiv2.options.Operation
-	(*options.Schema)(nil),       // 11: grpc.gateway.protoc_gen_openapiv2.options.Schema
-	(*options.Tag)(nil),          // 12: grpc.gateway.protoc_gen_openapiv2.options.Tag
-	(*options.JSONSchema)(nil),   // 13: grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
+	(*GrpcAPIService)(nil),   // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService
+	(*annotations.Http)(nil), // 1: google.api.Http
 }
 var file_internal_descriptor_apiconfig_apiconfig_proto_depIdxs = []int32{
-	8,  // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService.http:type_name -> google.api.Http
-	9,  // 1: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Swagger
-	10, // 2: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Operation
-	11, // 3: grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Schema
-	12, // 4: grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Tag
-	13, // 5: grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
-	1,  // 6: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.file:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIFileOption
-	2,  // 7: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.method:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIMethodOption
-	3,  // 8: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.message:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIMessageOption
-	4,  // 9: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.service:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIServiceOption
-	5,  // 10: grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions.field:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIFieldOption
-	6,  // 11: grpc.gateway.internal.descriptor.apiconfig.OpenAPIConfig.openapi_options:type_name -> grpc.gateway.internal.descriptor.apiconfig.OpenAPIOptions
-	12, // [12:12] is the sub-list for method output_type
-	12, // [12:12] is the sub-list for method input_type
-	12, // [12:12] is the sub-list for extension type_name
-	12, // [12:12] is the sub-list for extension extendee
-	0,  // [0:12] is the sub-list for field type_name
+	1, // 0: grpc.gateway.internal.descriptor.apiconfig.GrpcAPIService.http:type_name -> google.api.Http
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
 }
 
 func init() { file_internal_descriptor_apiconfig_apiconfig_proto_init() }
@@ -658,90 +148,6 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_init() {
 				return nil
 			}
 		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIFileOption); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIMethodOption); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIMessageOption); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIServiceOption); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIFieldOption); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIOptions); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-		file_internal_descriptor_apiconfig_apiconfig_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*OpenAPIConfig); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
 	}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
@@ -749,7 +155,7 @@ func file_internal_descriptor_apiconfig_apiconfig_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_internal_descriptor_apiconfig_apiconfig_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   8,
+			NumMessages:   1,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/internal/descriptor/apiconfig/apiconfig.proto b/internal/descriptor/apiconfig/apiconfig.proto
index d8ab71d0396..aeecd777384 100644
--- a/internal/descriptor/apiconfig/apiconfig.proto
+++ b/internal/descriptor/apiconfig/apiconfig.proto
@@ -5,7 +5,6 @@ package grpc.gateway.internal.descriptor.apiconfig;
 option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig";
 
 import "google/api/http.proto";
-import "protoc-gen-openapiv2/options/openapiv2.proto";
 
 // GrpcAPIService represents a stripped down version of google.api.Service .
 // Compare to https://github.com/googleapis/googleapis/blob/master/google/api/service.proto
@@ -20,47 +19,3 @@ message GrpcAPIService {
 	// Http Rule.
     google.api.Http http = 1;
 }
-
-// OpenAPIFileOption represents OpenAPI options on a file
-message OpenAPIFileOption {
-    string file = 1;
-    grpc.gateway.protoc_gen_openapiv2.options.Swagger option = 2;
-}
-
-// OpenAPIMethodOption represents OpenAPI options on a method
-message OpenAPIMethodOption {
-    string method = 1;
-    grpc.gateway.protoc_gen_openapiv2.options.Operation option = 2;
-}
-
-// OpenAPIMessageOption represents OpenAPI options on a message
-message OpenAPIMessageOption {
-    string message = 1;
-    grpc.gateway.protoc_gen_openapiv2.options.Schema option = 2;
-}
-
-// OpenAPIServiceOption represents OpenAPI options on a service
-message OpenAPIServiceOption {
-    string service = 1; // ex: Service
-    grpc.gateway.protoc_gen_openapiv2.options.Tag option = 2;
-}
-
-// OpenAPIFieldOption represents OpenAPI options on a field
-message OpenAPIFieldOption {
-    string field = 1;
-    grpc.gateway.protoc_gen_openapiv2.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/grpc_api_configuration.go b/internal/descriptor/grpc_api_configuration.go
index 8e13af97eca..c369f93406f 100644
--- a/internal/descriptor/grpc_api_configuration.go
+++ b/internal/descriptor/grpc_api_configuration.go
@@ -69,51 +69,3 @@ func (r *Registry) LoadGrpcAPIServiceFromYAML(yamlFile string) error {
 
 	return registerHTTPRulesFromGrpcAPIService(r, service, yamlFile)
 }
-
-func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*apiconfig.OpenAPIConfig, error) {
-	jsonContents, err := yaml.YAMLToJSON(yamlFileContents)
-	if err != nil {
-		return nil, fmt.Errorf("failed to convert OpenAPI Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err)
-	}
-
-	// Reject unknown fields because OpenAPIConfig is only used here
-	unmarshaler := protojson.UnmarshalOptions{
-		DiscardUnknown: false,
-	}
-
-	openapiConfiguration := apiconfig.OpenAPIConfig{}
-	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
-		return nil, fmt.Errorf("failed to parse gRPC API Configuration from YAML in '%v': %v", yamlSourceLogName, err)
-	}
-
-	return &openapiConfiguration, nil
-}
-
-func registerOpenAPIOptions(registry *Registry, openAPIConfig *apiconfig.OpenAPIConfig, yamlSourceLogName string) error {
-	if openAPIConfig.OpenapiOptions == nil {
-		// Nothing to do
-		return nil
-	}
-
-	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
-		return fmt.Errorf("failed to register option in %s: %s", yamlSourceLogName, err)
-	}
-	return nil
-}
-
-// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file
-// and registers the OpenAPI options the given registry.
-// This must be done after loading the proto file.
-func (r *Registry) LoadOpenAPIConfigFromYAML(yamlFile string) error {
-	yamlFileContents, err := ioutil.ReadFile(yamlFile)
-	if err != nil {
-		return fmt.Errorf("failed to read gRPC API Configuration description from '%v': %v", yamlFile, err)
-	}
-
-	config, err := loadOpenAPIConfigFromYAML(yamlFileContents, yamlFile)
-	if err != nil {
-		return err
-	}
-
-	return registerOpenAPIOptions(r, config, yamlFile)
-}
diff --git a/internal/descriptor/grpc_api_configuration_test.go b/internal/descriptor/grpc_api_configuration_test.go
index b5aad2f37a9..e6ac3d10aa3 100644
--- a/internal/descriptor/grpc_api_configuration_test.go
+++ b/internal/descriptor/grpc_api_configuration_test.go
@@ -3,8 +3,6 @@ package descriptor
 import (
 	"strings"
 	"testing"
-
-	"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 )
 
 func TestLoadGrpcAPIServiceFromYAMLInvalidType(t *testing.T) {
@@ -149,109 +147,3 @@ http:
 		t.Errorf("some.other.service has %v additional bindings when it should not have any. Got: %v", len(second.GetAdditionalBindings()), second.GetAdditionalBindings())
 	}
 }
-
-func TestLoadOpenAPIConfigFromYAMLRejectInvalidYAML(t *testing.T) {
-	config, err := loadOpenAPIConfigFromYAML([]byte(`
-openapiOptions:
-file:
-- file: test.proto
-  - option:
-      schemes:
-        - HTTP
-        - HTTPS
-        - WSS
-      securityDefinitions:
-        security:
-          ApiKeyAuth:
-            type: TYPE_API_KEY
-            in: IN_HEADER
-            name: "X-API-Key"
-`), "invalidyaml")
-	if err == nil {
-		t.Fatal(err)
-	}
-
-	if !strings.Contains(err.Error(), "line 4") {
-		t.Errorf("Expected yaml error to be detected in line 4. Got other error: %v", err)
-	}
-
-	if config != nil {
-		t.Fatal("Config returned")
-	}
-}
-
-func TestLoadOpenAPIConfigFromYAML(t *testing.T) {
-	config, err := loadOpenAPIConfigFromYAML([]byte(`
-openapiOptions:
-  file:
-  - file: test.proto
-    option:
-      schemes:
-      - HTTP
-      - HTTPS
-      - WSS
-      securityDefinitions:
-        security:
-          ApiKeyAuth:
-            type: TYPE_API_KEY
-            in: IN_HEADER
-            name: "X-API-Key"
-`), "openapi_options")
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if config.OpenapiOptions == nil {
-		t.Fatal("OpenAPIOptions is empty")
-	}
-
-	opts := config.OpenapiOptions
-	if numFileOpts := len(opts.File); numFileOpts != 1 {
-		t.Fatalf("expected 1 file option but got %d", numFileOpts)
-	}
-
-	fileOpt := opts.File[0]
-
-	if fileOpt.File != "test.proto" {
-		t.Fatalf("file option has unexpected binding %s", fileOpt.File)
-	}
-
-	swaggerOpt := fileOpt.Option
-
-	if swaggerOpt == nil {
-		t.Fatal("expected option to be set")
-	}
-
-	if numSchemes := len(swaggerOpt.Schemes); numSchemes != 3 {
-		t.Fatalf("expected 3 schemes but got %d", numSchemes)
-	}
-	if swaggerOpt.Schemes[0] != options.Scheme_HTTP {
-		t.Fatalf("expected first scheme to be HTTP but got %s", swaggerOpt.Schemes[0])
-	}
-	if swaggerOpt.Schemes[1] != options.Scheme_HTTPS {
-		t.Fatalf("expected second scheme to be HTTPS but got %s", swaggerOpt.Schemes[1])
-	}
-	if swaggerOpt.Schemes[2] != options.Scheme_WSS {
-		t.Fatalf("expected third scheme to be WSS but got %s", swaggerOpt.Schemes[2])
-	}
-
-	if swaggerOpt.SecurityDefinitions == nil {
-		t.Fatal("expected securityDefinitions to be set")
-	}
-	if numSecOpts := len(swaggerOpt.SecurityDefinitions.Security); numSecOpts != 1 {
-		t.Fatalf("expected 1 security option but got %d", numSecOpts)
-	}
-	secOpt, ok := swaggerOpt.SecurityDefinitions.Security["ApiKeyAuth"]
-	if !ok {
-		t.Fatal("no SecurityScheme for key \"ApiKeyAuth\"")
-	}
-	if secOpt.Type != options.SecurityScheme_TYPE_API_KEY {
-		t.Fatalf("expected scheme type to be TYPE_API_KEY but got %s", secOpt.Type)
-	}
-	if secOpt.In != options.SecurityScheme_IN_HEADER {
-		t.Fatalf("expected scheme  in to be IN_HEADER but got %s", secOpt.In)
-	}
-	if secOpt.Name != "X-API-Key" {
-		t.Fatalf("expected name to be X-API-Key but got %s", secOpt.Name)
-	}
-}
diff --git a/internal/descriptor/openapi_configuration.go b/internal/descriptor/openapi_configuration.go
new file mode 100644
index 00000000000..3b5f74965a3
--- /dev/null
+++ b/internal/descriptor/openapi_configuration.go
@@ -0,0 +1,58 @@
+package descriptor
+
+import (
+	"fmt"
+	"io/ioutil"
+
+	"github.com/ghodss/yaml"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
+	"google.golang.org/protobuf/encoding/protojson"
+)
+
+func loadOpenAPIConfigFromYAML(yamlFileContents []byte, yamlSourceLogName string) (*openapiconfig.OpenAPIConfig, error) {
+	jsonContents, err := yaml.YAMLToJSON(yamlFileContents)
+	if err != nil {
+		return nil, fmt.Errorf("failed to convert OpenAPI Configuration from YAML in '%v' to JSON: %v", yamlSourceLogName, err)
+	}
+
+	// Reject unknown fields because OpenAPIConfig is only used here
+	unmarshaler := protojson.UnmarshalOptions{
+		DiscardUnknown: false,
+	}
+
+	openapiConfiguration := openapiconfig.OpenAPIConfig{}
+	if err := unmarshaler.Unmarshal(jsonContents, &openapiConfiguration); err != nil {
+		return nil, fmt.Errorf("failed to parse gRPC API Configuration from YAML in '%v': %v", yamlSourceLogName, err)
+	}
+
+	return &openapiConfiguration, nil
+}
+
+func registerOpenAPIOptions(registry *Registry, openAPIConfig *openapiconfig.OpenAPIConfig, yamlSourceLogName string) error {
+	if openAPIConfig.OpenapiOptions == nil {
+		// Nothing to do
+		return nil
+	}
+
+	if err := registry.RegisterOpenAPIOptions(openAPIConfig.OpenapiOptions); err != nil {
+		return fmt.Errorf("failed to register option in %s: %s", yamlSourceLogName, err)
+	}
+	return nil
+}
+
+// LoadOpenAPIConfigFromYAML loads an  OpenAPI Configuration from the given YAML file
+// and registers the OpenAPI options the given registry.
+// This must be done after loading the proto file.
+func (r *Registry) LoadOpenAPIConfigFromYAML(yamlFile string) error {
+	yamlFileContents, err := ioutil.ReadFile(yamlFile)
+	if err != nil {
+		return fmt.Errorf("failed to read gRPC API Configuration description from '%v': %v", yamlFile, err)
+	}
+
+	config, err := loadOpenAPIConfigFromYAML(yamlFileContents, yamlFile)
+	if err != nil {
+		return err
+	}
+
+	return registerOpenAPIOptions(r, config, yamlFile)
+}
diff --git a/internal/descriptor/openapi_configuration_test.go b/internal/descriptor/openapi_configuration_test.go
new file mode 100644
index 00000000000..e61b0d1ef0b
--- /dev/null
+++ b/internal/descriptor/openapi_configuration_test.go
@@ -0,0 +1,114 @@
+package descriptor
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
+)
+
+func TestLoadOpenAPIConfigFromYAMLRejectInvalidYAML(t *testing.T) {
+	config, err := loadOpenAPIConfigFromYAML([]byte(`
+openapiOptions:
+file:
+- file: test.proto
+  - option:
+      schemes:
+        - HTTP
+        - HTTPS
+        - WSS
+      securityDefinitions:
+        security:
+          ApiKeyAuth:
+            type: TYPE_API_KEY
+            in: IN_HEADER
+            name: "X-API-Key"
+`), "invalidyaml")
+	if err == nil {
+		t.Fatal(err)
+	}
+
+	if !strings.Contains(err.Error(), "line 4") {
+		t.Errorf("Expected yaml error to be detected in line 4. Got other error: %v", err)
+	}
+
+	if config != nil {
+		t.Fatal("Config returned")
+	}
+}
+
+func TestLoadOpenAPIConfigFromYAML(t *testing.T) {
+	config, err := loadOpenAPIConfigFromYAML([]byte(`
+openapiOptions:
+  file:
+  - file: test.proto
+    option:
+      schemes:
+      - HTTP
+      - HTTPS
+      - WSS
+      securityDefinitions:
+        security:
+          ApiKeyAuth:
+            type: TYPE_API_KEY
+            in: IN_HEADER
+            name: "X-API-Key"
+`), "openapi_options")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if config.OpenapiOptions == nil {
+		t.Fatal("OpenAPIOptions is empty")
+	}
+
+	opts := config.OpenapiOptions
+	if numFileOpts := len(opts.File); numFileOpts != 1 {
+		t.Fatalf("expected 1 file option but got %d", numFileOpts)
+	}
+
+	fileOpt := opts.File[0]
+
+	if fileOpt.File != "test.proto" {
+		t.Fatalf("file option has unexpected binding %s", fileOpt.File)
+	}
+
+	swaggerOpt := fileOpt.Option
+
+	if swaggerOpt == nil {
+		t.Fatal("expected option to be set")
+	}
+
+	if numSchemes := len(swaggerOpt.Schemes); numSchemes != 3 {
+		t.Fatalf("expected 3 schemes but got %d", numSchemes)
+	}
+	if swaggerOpt.Schemes[0] != options.Scheme_HTTP {
+		t.Fatalf("expected first scheme to be HTTP but got %s", swaggerOpt.Schemes[0])
+	}
+	if swaggerOpt.Schemes[1] != options.Scheme_HTTPS {
+		t.Fatalf("expected second scheme to be HTTPS but got %s", swaggerOpt.Schemes[1])
+	}
+	if swaggerOpt.Schemes[2] != options.Scheme_WSS {
+		t.Fatalf("expected third scheme to be WSS but got %s", swaggerOpt.Schemes[2])
+	}
+
+	if swaggerOpt.SecurityDefinitions == nil {
+		t.Fatal("expected securityDefinitions to be set")
+	}
+	if numSecOpts := len(swaggerOpt.SecurityDefinitions.Security); numSecOpts != 1 {
+		t.Fatalf("expected 1 security option but got %d", numSecOpts)
+	}
+	secOpt, ok := swaggerOpt.SecurityDefinitions.Security["ApiKeyAuth"]
+	if !ok {
+		t.Fatal("no SecurityScheme for key \"ApiKeyAuth\"")
+	}
+	if secOpt.Type != options.SecurityScheme_TYPE_API_KEY {
+		t.Fatalf("expected scheme type to be TYPE_API_KEY but got %s", secOpt.Type)
+	}
+	if secOpt.In != options.SecurityScheme_IN_HEADER {
+		t.Fatalf("expected scheme  in to be IN_HEADER but got %s", secOpt.In)
+	}
+	if secOpt.Name != "X-API-Key" {
+		t.Fatalf("expected name to be X-API-Key but got %s", secOpt.Name)
+	}
+}
diff --git a/internal/descriptor/openapiconfig/BUILD.bazel b/internal/descriptor/openapiconfig/BUILD.bazel
new file mode 100644
index 00000000000..29156c84612
--- /dev/null
+++ b/internal/descriptor/openapiconfig/BUILD.bazel
@@ -0,0 +1,26 @@
+load("@rules_proto//proto:defs.bzl", "proto_library")
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
+
+proto_library(
+    name = "openapiconfig_proto",
+    srcs = ["openapiconfig.proto"],
+    visibility = ["//:__subpackages__"],
+    deps = ["//protoc-gen-openapiv2/options:options_proto"],
+)
+
+go_proto_library(
+    name = "openapiconfig_go_proto",
+    compilers = ["//:go_apiv2"],
+    importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig",
+    proto = ":openapiconfig_proto",
+    visibility = ["//:__subpackages__"],
+    deps = ["//protoc-gen-openapiv2/options:go_default_library"],
+)
+
+go_library(
+    name = "go_default_library",
+    embed = [":openapiconfig_go_proto"],
+    importpath = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig",
+    visibility = ["//:__subpackages__"],
+)
diff --git a/internal/descriptor/openapiconfig/openapiconfig.pb.go b/internal/descriptor/openapiconfig/openapiconfig.pb.go
new file mode 100644
index 00000000000..887d429fd3e
--- /dev/null
+++ b/internal/descriptor/openapiconfig/openapiconfig.pb.go
@@ -0,0 +1,688 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.12.0
+// source: internal/descriptor/openapiconfig/openapiconfig.proto
+
+package openapiconfig
+
+import (
+	proto "github.com/golang/protobuf/proto"
+	options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/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)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+// 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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_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{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_internal_descriptor_openapiconfig_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_openapiconfig_openapiconfig_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && 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_openapiconfig_openapiconfig_proto_rawDescGZIP(), []int{6}
+}
+
+func (x *OpenAPIConfig) GetOpenapiOptions() *OpenAPIOptions {
+	if x != nil {
+		return x.OpenapiOptions
+	}
+	return nil
+}
+
+var File_internal_descriptor_openapiconfig_openapiconfig_proto protoreflect.FileDescriptor
+
+var file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDesc = []byte{
+	0x0a, 0x35, 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, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x63, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 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, 0x1a, 0x2c, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x2d,
+	0x67, 0x65, 0x6e, 0x2d, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 0x2f, 0x6f, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x76, 0x32, 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, 0x32, 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, 0x32, 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, 0x32, 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, 0x32, 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, 0x32, 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, 0xde, 0x03, 0x0a, 0x0e, 0x4f, 0x70,
+	0x65, 0x6e, 0x41, 0x50, 0x49, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x55, 0x0a, 0x04,
+	0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x41, 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, 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, 0x5b, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x02, 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, 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, 0x5e, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 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, 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, 0x5e, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x04, 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, 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, 0x58, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x42, 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,
+	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, 0x78, 0x0a, 0x0d, 0x4f, 0x70,
+	0x65, 0x6e, 0x41, 0x50, 0x49, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x67, 0x0a, 0x0f, 0x6f,
+	0x70, 0x65, 0x6e, 0x61, 0x70, 0x69, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x3e, 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, 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, 0x4d, 0x5a, 0x4b, 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, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescOnce sync.Once
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescData = file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDesc
+)
+
+func file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescGZIP() []byte {
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescOnce.Do(func() {
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescData)
+	})
+	return file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDescData
+}
+
+var file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_internal_descriptor_openapiconfig_openapiconfig_proto_goTypes = []interface{}{
+	(*OpenAPIFileOption)(nil),    // 0: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFileOption
+	(*OpenAPIMethodOption)(nil),  // 1: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMethodOption
+	(*OpenAPIMessageOption)(nil), // 2: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMessageOption
+	(*OpenAPIServiceOption)(nil), // 3: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIServiceOption
+	(*OpenAPIFieldOption)(nil),   // 4: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFieldOption
+	(*OpenAPIOptions)(nil),       // 5: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions
+	(*OpenAPIConfig)(nil),        // 6: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIConfig
+	(*options.Swagger)(nil),      // 7: grpc.gateway.protoc_gen_openapiv2.options.Swagger
+	(*options.Operation)(nil),    // 8: grpc.gateway.protoc_gen_openapiv2.options.Operation
+	(*options.Schema)(nil),       // 9: grpc.gateway.protoc_gen_openapiv2.options.Schema
+	(*options.Tag)(nil),          // 10: grpc.gateway.protoc_gen_openapiv2.options.Tag
+	(*options.JSONSchema)(nil),   // 11: grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
+}
+var file_internal_descriptor_openapiconfig_openapiconfig_proto_depIdxs = []int32{
+	7,  // 0: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFileOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Swagger
+	8,  // 1: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMethodOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Operation
+	9,  // 2: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMessageOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Schema
+	10, // 3: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIServiceOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.Tag
+	11, // 4: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFieldOption.option:type_name -> grpc.gateway.protoc_gen_openapiv2.options.JSONSchema
+	0,  // 5: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions.file:type_name -> grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFileOption
+	1,  // 6: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions.method:type_name -> grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMethodOption
+	2,  // 7: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions.message:type_name -> grpc.gateway.internal.descriptor.openapiconfig.OpenAPIMessageOption
+	3,  // 8: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions.service:type_name -> grpc.gateway.internal.descriptor.openapiconfig.OpenAPIServiceOption
+	4,  // 9: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIOptions.field:type_name -> grpc.gateway.internal.descriptor.openapiconfig.OpenAPIFieldOption
+	5,  // 10: grpc.gateway.internal.descriptor.openapiconfig.OpenAPIConfig.openapi_options:type_name -> grpc.gateway.internal.descriptor.openapiconfig.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_openapiconfig_openapiconfig_proto_init() }
+func file_internal_descriptor_openapiconfig_openapiconfig_proto_init() {
+	if File_internal_descriptor_openapiconfig_openapiconfig_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIFileOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIMethodOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIMessageOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIServiceOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIFieldOption); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIOptions); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenAPIConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   7,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_internal_descriptor_openapiconfig_openapiconfig_proto_goTypes,
+		DependencyIndexes: file_internal_descriptor_openapiconfig_openapiconfig_proto_depIdxs,
+		MessageInfos:      file_internal_descriptor_openapiconfig_openapiconfig_proto_msgTypes,
+	}.Build()
+	File_internal_descriptor_openapiconfig_openapiconfig_proto = out.File
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_rawDesc = nil
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_goTypes = nil
+	file_internal_descriptor_openapiconfig_openapiconfig_proto_depIdxs = nil
+}
diff --git a/internal/descriptor/openapiconfig/openapiconfig.proto b/internal/descriptor/openapiconfig/openapiconfig.proto
new file mode 100644
index 00000000000..f0aa69ee295
--- /dev/null
+++ b/internal/descriptor/openapiconfig/openapiconfig.proto
@@ -0,0 +1,51 @@
+syntax = "proto3";
+
+package grpc.gateway.internal.descriptor.openapiconfig;
+
+option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig";
+
+import "protoc-gen-openapiv2/options/openapiv2.proto";
+
+// OpenAPIFileOption represents OpenAPI options on a file
+message OpenAPIFileOption {
+    string file = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Swagger option = 2;
+}
+
+// OpenAPIMethodOption represents OpenAPI options on a method
+message OpenAPIMethodOption {
+    string method = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Operation option = 2;
+}
+
+// OpenAPIMessageOption represents OpenAPI options on a message
+message OpenAPIMessageOption {
+    string message = 1;
+    grpc.gateway.protoc_gen_openapiv2.options.Schema option = 2;
+}
+
+// OpenAPIServiceOption represents OpenAPI options on a service
+message OpenAPIServiceOption {
+    string service = 1; // ex: Service
+    grpc.gateway.protoc_gen_openapiv2.options.Tag option = 2;
+}
+
+// OpenAPIFieldOption represents OpenAPI options on a field
+message OpenAPIFieldOption {
+    string field = 1;
+    grpc.gateway.protoc_gen_openapiv2.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/registry.go b/internal/descriptor/registry.go
index f1518f9e84f..ad078b369a7 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 
 	"github.com/golang/glog"
-	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	"google.golang.org/genproto/googleapis/api/annotations"
 	"google.golang.org/protobuf/types/descriptorpb"
@@ -607,7 +607,7 @@ func (r *Registry) packageIdentityName(f *descriptorpb.FileDescriptorProto) stri
 }
 
 // RegisterOpenAPIOptions registers OpenAPI options
-func (r *Registry) RegisterOpenAPIOptions(opts *apiconfig.OpenAPIOptions) error {
+func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) error {
 	if opts == nil {
 		return nil
 	}
diff --git a/internal/descriptor/registry_test.go b/internal/descriptor/registry_test.go
index e9f554a613d..90a7fe4e755 100644
--- a/internal/descriptor/registry_test.go
+++ b/internal/descriptor/registry_test.go
@@ -3,7 +3,7 @@ package descriptor
 import (
 	"testing"
 
-	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
 	"google.golang.org/protobuf/encoding/prototext"
 	"google.golang.org/protobuf/types/descriptorpb"
 	"google.golang.org/protobuf/types/pluginpb"
@@ -665,7 +665,7 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 	>
 	`
 	for _, tcase := range []struct {
-		options   *apiconfig.OpenAPIOptions
+		options   *openapiconfig.OpenAPIOptions
 		shouldErr bool
 		desc      string
 	}{
@@ -674,28 +674,28 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "successfully add options if referenced entity exists",
-			options: &apiconfig.OpenAPIOptions{
-				File: []*apiconfig.OpenAPIFileOption{
+			options: &openapiconfig.OpenAPIOptions{
+				File: []*openapiconfig.OpenAPIFileOption{
 					{
 						File: "a.proto",
 					},
 				},
-				Method: []*apiconfig.OpenAPIMethodOption{
+				Method: []*openapiconfig.OpenAPIMethodOption{
 					{
 						Method: "example.foo.AService.Meth",
 					},
 				},
-				Message: []*apiconfig.OpenAPIMessageOption{
+				Message: []*openapiconfig.OpenAPIMessageOption{
 					{
 						Message: "example.foo.ExampleMessage",
 					},
 				},
-				Service: []*apiconfig.OpenAPIServiceOption{
+				Service: []*openapiconfig.OpenAPIServiceOption{
 					{
 						Service: "example.foo.AService",
 					},
 				},
-				Field: []*apiconfig.OpenAPIFieldOption{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field: "example.foo.ExampleMessage.str",
 					},
@@ -704,28 +704,28 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "successfully accept fully qualified names",
-			options: &apiconfig.OpenAPIOptions{
-				File: []*apiconfig.OpenAPIFileOption{
+			options: &openapiconfig.OpenAPIOptions{
+				File: []*openapiconfig.OpenAPIFileOption{
 					{
 						File: "a.proto",
 					},
 				},
-				Method: []*apiconfig.OpenAPIMethodOption{
+				Method: []*openapiconfig.OpenAPIMethodOption{
 					{
 						Method: ".example.foo.AService.Meth",
 					},
 				},
-				Message: []*apiconfig.OpenAPIMessageOption{
+				Message: []*openapiconfig.OpenAPIMessageOption{
 					{
 						Message: ".example.foo.ExampleMessage",
 					},
 				},
-				Service: []*apiconfig.OpenAPIServiceOption{
+				Service: []*openapiconfig.OpenAPIServiceOption{
 					{
 						Service: ".example.foo.AService",
 					},
 				},
-				Field: []*apiconfig.OpenAPIFieldOption{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field: ".example.foo.ExampleMessage.str",
 					},
@@ -734,8 +734,8 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "error if file does not exist",
-			options: &apiconfig.OpenAPIOptions{
-				File: []*apiconfig.OpenAPIFileOption{
+			options: &openapiconfig.OpenAPIOptions{
+				File: []*openapiconfig.OpenAPIFileOption{
 					{
 						File: "b.proto",
 					},
@@ -745,8 +745,8 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "error if method does not exist",
-			options: &apiconfig.OpenAPIOptions{
-				Method: []*apiconfig.OpenAPIMethodOption{
+			options: &openapiconfig.OpenAPIOptions{
+				Method: []*openapiconfig.OpenAPIMethodOption{
 					{
 						Method: "example.foo.AService.Meth2",
 					},
@@ -756,8 +756,8 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "error if message does not exist",
-			options: &apiconfig.OpenAPIOptions{
-				Message: []*apiconfig.OpenAPIMessageOption{
+			options: &openapiconfig.OpenAPIOptions{
+				Message: []*openapiconfig.OpenAPIMessageOption{
 					{
 						Message: "example.foo.NonexistentMessage",
 					},
@@ -767,8 +767,8 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "error if service does not exist",
-			options: &apiconfig.OpenAPIOptions{
-				Service: []*apiconfig.OpenAPIServiceOption{
+			options: &openapiconfig.OpenAPIOptions{
+				Service: []*openapiconfig.OpenAPIServiceOption{
 					{
 						Service: "example.foo.AService1",
 					},
@@ -778,8 +778,8 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 		},
 		{
 			desc: "error if field does not exist",
-			options: &apiconfig.OpenAPIOptions{
-				Field: []*apiconfig.OpenAPIFieldOption{
+			options: &openapiconfig.OpenAPIOptions{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field: "example.foo.ExampleMessage.str1",
 					},
diff --git a/protoc-gen-openapiv2/internal/genopenapi/template_test.go b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
index 9291afac12d..a4204c8e94f 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/template_test.go
+++ b/protoc-gen-openapiv2/internal/genopenapi/template_test.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/google/go-cmp/cmp"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
-	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/apiconfig"
+	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
 	openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
 	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
@@ -1136,7 +1136,7 @@ func TestApplyTemplateOverrideOperationID(t *testing.T) {
 		}
 	}
 
-	verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *apiconfig.OpenAPIOptions) {
+	verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *openapiconfig.OpenAPIOptions) {
 		if err := AddErrorDefs(reg); err != nil {
 			t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 			return
@@ -1184,8 +1184,8 @@ func TestApplyTemplateOverrideOperationID(t *testing.T) {
 	t.Run("verify override options annotations", func(t *testing.T) {
 		file := newFile()
 		reg := descriptor.NewRegistry()
-		opts := &apiconfig.OpenAPIOptions{
-			Method: []*apiconfig.OpenAPIMethodOption{
+		opts := &openapiconfig.OpenAPIOptions{
+			Method: []*openapiconfig.OpenAPIMethodOption{
 				{
 					Method: "example.ExampleService.Example",
 					Option: &openapiOperation,
@@ -1290,7 +1290,7 @@ func TestApplyTemplateExtensions(t *testing.T) {
 		},
 	}
 	verifyTemplateExtensions := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
-		opts *apiconfig.OpenAPIOptions) {
+		opts *openapiconfig.OpenAPIOptions) {
 		if err := AddErrorDefs(reg); err != nil {
 			t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 			return
@@ -1388,14 +1388,14 @@ func TestApplyTemplateExtensions(t *testing.T) {
 	})
 	t.Run("verify template options set via annotations", func(t *testing.T) {
 		file := newFile()
-		opts := &apiconfig.OpenAPIOptions{
-			File: []*apiconfig.OpenAPIFileOption{
+		opts := &openapiconfig.OpenAPIOptions{
+			File: []*openapiconfig.OpenAPIFileOption{
 				{
 					File:   "example.proto",
 					Option: &swagger,
 				},
 			},
-			Method: []*apiconfig.OpenAPIMethodOption{
+			Method: []*openapiconfig.OpenAPIMethodOption{
 				{
 					Method: "example.ExampleService.Example",
 					Option: &openapiOperation,
@@ -2297,7 +2297,7 @@ func TestSchemaOfField(t *testing.T) {
 		field          *descriptor.Field
 		refs           refMap
 		expected       openapiSchemaObject
-		openAPIOptions *apiconfig.OpenAPIOptions
+		openAPIOptions *openapiconfig.OpenAPIOptions
 	}
 
 	jsonSchema := &openapi_options.JSONSchema{
@@ -2717,8 +2717,8 @@ func TestSchemaOfField(t *testing.T) {
 					TypeName: proto.String(".example.Message.MapFieldEntry"),
 				},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Field: []*apiconfig.OpenAPIFieldOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field:  "example.Message.map_field_option",
 						Option: jsonSchema,
@@ -2745,8 +2745,8 @@ func TestSchemaOfField(t *testing.T) {
 					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
 				},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Field: []*apiconfig.OpenAPIFieldOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field:  "example.Message.array_field_option",
 						Option: jsonSchema,
@@ -2771,8 +2771,8 @@ func TestSchemaOfField(t *testing.T) {
 					Type:  descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
 				},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Field: []*apiconfig.OpenAPIFieldOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field:  "example.Message.primitive_field_option",
 						Option: jsonSchema,
@@ -2798,8 +2798,8 @@ func TestSchemaOfField(t *testing.T) {
 					TypeName: proto.String(".example.Empty"),
 				},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Field: []*apiconfig.OpenAPIFieldOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Field: []*openapiconfig.OpenAPIFieldOption{
 					{
 						Field:  "example.Message.message_field_option",
 						Option: jsonSchema,
@@ -2898,7 +2898,7 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
 		msgDescs       []*descriptorpb.DescriptorProto
 		schema         map[string]openapi_options.Schema // per-message schema to add
 		defs           openapiDefinitionsObject
-		openAPIOptions *apiconfig.OpenAPIOptions
+		openAPIOptions *openapiconfig.OpenAPIOptions
 	}{
 		{
 			descr: "no OpenAPI options",
@@ -3027,8 +3027,8 @@ func TestRenderMessagesAsDefinition(t *testing.T) {
 			msgDescs: []*descriptorpb.DescriptorProto{
 				{Name: proto.String("Message")},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Message: []*apiconfig.OpenAPIMessageOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Message: []*openapiconfig.OpenAPIMessageOption{
 					{
 						Message: "example.Message",
 						Option: &openapi_options.Schema{
@@ -3312,7 +3312,7 @@ func TestMessageOptionsWithGoTemplate(t *testing.T) {
 		msgDescs       []*descriptorpb.DescriptorProto
 		schema         map[string]openapi_options.Schema // per-message schema to add
 		defs           openapiDefinitionsObject
-		openAPIOptions *apiconfig.OpenAPIOptions
+		openAPIOptions *openapiconfig.OpenAPIOptions
 		useGoTemplate  bool
 	}{
 		{
@@ -3380,8 +3380,8 @@ func TestMessageOptionsWithGoTemplate(t *testing.T) {
 			msgDescs: []*descriptorpb.DescriptorProto{
 				{Name: proto.String("Message")},
 			},
-			openAPIOptions: &apiconfig.OpenAPIOptions{
-				Message: []*apiconfig.OpenAPIMessageOption{
+			openAPIOptions: &openapiconfig.OpenAPIOptions{
+				Message: []*openapiconfig.OpenAPIMessageOption{
 					{
 						Message: "example.Message",
 						Option: &openapi_options.Schema{

From b33ec94a885abdac9c41881f0d15cedfca1dc476 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 13:17:23 -0700
Subject: [PATCH 07/17] Add unannotated_echo_service swagger YAML example

---
 Makefile                                      |   2 +-
 .../unannotated_echo_service.swagger.json     | 215 ++++++++++++++++--
 .../unannotated_echo_service.swagger.yaml     | 117 ++++++++++
 3 files changed, 315 insertions(+), 19 deletions(-)
 create mode 100644 examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml

diff --git a/Makefile b/Makefile
index 427d7f57de1..046f5b83740 100644
--- a/Makefile
+++ b/Makefile
@@ -184,7 +184,7 @@ $(EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(EXAMPLES)
 	protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,allow_repeated_fields_in_body=true,paths=source_relative$(ADDITIONAL_GW_FLAGS):. $(EXAMPLES)
 
 
-$(EXAMPLE_OPENAPISRCS): ADDITIONAL_SWG_FLAGS:=$(ADDITIONAL_SWG_FLAGS),grpc_api_configuration=examples/internal/proto/examplepb/unannotated_echo_service.yaml
+$(EXAMPLE_OPENAPISRCS): ADDITIONAL_SWG_FLAGS:=$(ADDITIONAL_SWG_FLAGS),grpc_api_configuration=examples/internal/proto/examplepb/unannotated_echo_service.yaml,openapi_configuration=examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
 $(EXAMPLE_OPENAPISRCS): $(OPENAPI_PLUGIN) $(OPENAPI_EXAMPLES)
 	protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(OPENAPI_PLUGIN) --openapiv2_out=logtostderr=true,allow_repeated_fields_in_body=true,use_go_templates=true$(ADDITIONAL_SWG_FLAGS):. $(OPENAPI_EXAMPLES)
 
diff --git a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
index db30959f7c9..8bafa922d6a 100644
--- a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
+++ b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
@@ -1,29 +1,73 @@
 {
   "swagger": "2.0",
   "info": {
-    "title": "examples/internal/proto/examplepb/unannotated_echo_service.proto",
+    "title": "Unannotated Echo",
     "description": "Unannotated Echo Service\nSimilar to echo_service.proto but without annotations. See\nunannotated_echo_service.yaml for the equivalent of the annotations in\ngRPC API configuration format.\n\nEcho Service API consists of a single service which returns\na message.",
-    "version": "version not set"
+    "version": "1.0",
+    "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/master/LICENSE.txt"
+    },
+    "x-something-something": "yadda"
   },
+  "schemes": [
+    "http",
+    "https",
+    "wss"
+  ],
   "consumes": [
-    "application/json"
+    "application/json",
+    "application/x-foo-mime"
   ],
   "produces": [
-    "application/json"
+    "application/json",
+    "application/x-foo-mime"
   ],
   "paths": {
     "/v1/example/echo/{id}": {
       "post": {
-        "summary": "Echo method receives a simple message and returns it.",
-        "description": "The message posted as the id parameter will also be\nreturned.",
+        "summary": "Summary: Echo rpc",
+        "description": "Description Echo",
         "operationId": "UnannotatedEchoService_Echo",
         "responses": {
           "200": {
             "description": "A successful response.",
             "schema": {
               "$ref": "#/definitions/examplepbUnannotatedSimpleMessage"
+            },
+            "examples": {
+              "application/json": {
+                "value": "the input value"
+              }
+            }
+          },
+          "403": {
+            "description": "Returned when the user does not have permission to access the resource.",
+            "schema": {}
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "integer",
+              "format": "integer"
             }
           },
+          "418": {
+            "description": "I'm a teapot.",
+            "schema": {
+              "$ref": "#/definitions/examplepbNumericEnum"
+            }
+          },
+          "503": {
+            "description": "Returned when the resource is temporarily unavailable.",
+            "schema": {},
+            "x-number": 100
+          },
           "default": {
             "description": "An unexpected error response.",
             "schema": {
@@ -41,22 +85,53 @@
           }
         ],
         "tags": [
-          "UnannotatedEchoService"
-        ]
+          "echo rpc"
+        ],
+        "externalDocs": {
+          "description": "Find out more Echo",
+          "url": "https://github.com/grpc-ecosystem/grpc-gateway"
+        }
       }
     },
     "/v1/example/echo/{id}/{num}": {
       "get": {
-        "summary": "Echo method receives a simple message and returns it.",
-        "description": "The message posted as the id parameter will also be\nreturned.",
+        "summary": "Summary: Echo rpc",
+        "description": "Description Echo",
         "operationId": "UnannotatedEchoService_Echo2",
         "responses": {
           "200": {
             "description": "A successful response.",
             "schema": {
               "$ref": "#/definitions/examplepbUnannotatedSimpleMessage"
+            },
+            "examples": {
+              "application/json": {
+                "value": "the input value"
+              }
             }
           },
+          "403": {
+            "description": "Returned when the user does not have permission to access the resource.",
+            "schema": {}
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "integer",
+              "format": "integer"
+            }
+          },
+          "418": {
+            "description": "I'm a teapot.",
+            "schema": {
+              "$ref": "#/definitions/examplepbNumericEnum"
+            }
+          },
+          "503": {
+            "description": "Returned when the resource is temporarily unavailable.",
+            "schema": {},
+            "x-number": 100
+          },
           "default": {
             "description": "An unexpected error response.",
             "schema": {
@@ -74,10 +149,12 @@
           },
           {
             "name": "num",
+            "description": "Int value field",
             "in": "path",
             "required": true,
             "type": "string",
-            "format": "int64"
+            "format": "int64",
+            "default": "42"
           },
           {
             "name": "duration",
@@ -133,8 +210,12 @@
           }
         ],
         "tags": [
-          "UnannotatedEchoService"
-        ]
+          "echo rpc"
+        ],
+        "externalDocs": {
+          "description": "Find out more Echo",
+          "url": "https://github.com/grpc-ecosystem/grpc-gateway"
+        }
       }
     },
     "/v1/example/echo_body": {
@@ -148,6 +229,23 @@
               "$ref": "#/definitions/examplepbUnannotatedSimpleMessage"
             }
           },
+          "403": {
+            "description": "Returned when the user does not have permission to access the resource.",
+            "schema": {}
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          },
+          "418": {
+            "description": "I'm a teapot.",
+            "schema": {
+              "$ref": "#/definitions/examplepbNumericEnum"
+            }
+          },
           "default": {
             "description": "An unexpected error response.",
             "schema": {
@@ -181,6 +279,23 @@
               "$ref": "#/definitions/examplepbUnannotatedSimpleMessage"
             }
           },
+          "403": {
+            "description": "Returned when the user does not have permission to access the resource.",
+            "schema": {}
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          },
+          "418": {
+            "description": "I'm a teapot.",
+            "schema": {
+              "$ref": "#/definitions/examplepbNumericEnum"
+            }
+          },
           "default": {
             "description": "An unexpected error response.",
             "schema": {
@@ -198,10 +313,12 @@
           },
           {
             "name": "num",
+            "description": "Int value field",
             "in": "query",
-            "required": false,
+            "required": true,
             "type": "string",
-            "format": "int64"
+            "format": "int64",
+            "default": "42"
           },
           {
             "name": "duration",
@@ -263,6 +380,15 @@
     }
   },
   "definitions": {
+    "examplepbNumericEnum": {
+      "type": "string",
+      "enum": [
+        "ZERO",
+        "ONE"
+      ],
+      "default": "ZERO",
+      "description": "NumericEnum is one or zero.\n\n - ZERO: ZERO means 0\n - ONE: ONE means 1"
+    },
     "examplepbUnannotatedEmbedded": {
       "type": "object",
       "properties": {
@@ -278,6 +404,9 @@
     },
     "examplepbUnannotatedSimpleMessage": {
       "type": "object",
+      "example": {
+        "id": "myid"
+      },
       "properties": {
         "id": {
           "type": "string",
@@ -285,7 +414,12 @@
         },
         "num": {
           "type": "string",
-          "format": "int64"
+          "format": "int64",
+          "default": "42",
+          "description": "Int value field",
+          "required": [
+            "num"
+          ]
         },
         "duration": {
           "type": "string"
@@ -308,7 +442,15 @@
           "$ref": "#/definitions/examplepbUnannotatedEmbedded"
         }
       },
-      "description": "UnannotatedSimpleMessage represents a simple message sent to the unannotated Echo service."
+      "description": "A simple message with many types",
+      "title": "A bit of everything",
+      "externalDocs": {
+        "description": "Find out more about UnannotatedSimpleMessage",
+        "url": "https://github.com/grpc-ecosystem/grpc-gateway"
+      },
+      "required": [
+        "id"
+      ]
     },
     "protobufAny": {
       "type": "object",
@@ -343,5 +485,42 @@
         }
       }
     }
-  }
+  },
+  "securityDefinitions": {
+    "ApiKeyAuth": {
+      "type": "apiKey",
+      "name": "X-API-Key",
+      "in": "header",
+      "x-amazon-apigateway-authorizer": {
+        "authorizerResultTtlInSeconds": 60,
+        "type": "token"
+      },
+      "x-amazon-apigateway-authtype": "oauth2"
+    },
+    "BasicAuth": {
+      "type": "basic"
+    }
+  },
+  "security": [
+    {
+      "ApiKeyAuth": [],
+      "BasicAuth": []
+    },
+    {
+      "ApiKeyAuth": [],
+      "OAuth2": [
+        "read",
+        "write"
+      ]
+    }
+  ],
+  "externalDocs": {
+    "description": "More about gRPC-Gateway",
+    "url": "https://github.com/grpc-ecosystem/grpc-gateway"
+  },
+  "x-grpc-gateway-baz-list": [
+    "one",
+    true
+  ],
+  "x-grpc-gateway-foo": "bar"
 }
diff --git a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
new file mode 100644
index 00000000000..91b19169c71
--- /dev/null
+++ b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
@@ -0,0 +1,117 @@
+openapiOptions:
+  file:
+    - file: "examples/internal/proto/examplepb/unannotated_echo_service.proto"
+      option:
+        info:
+          title: Unannotated Echo
+          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/master/LICENSE.txt
+          version: '1.0'
+          extensions:
+            x-something-something: yadda
+        schemes:
+        - HTTP
+        - HTTPS
+        - WSS
+        consumes:
+        - application/json
+        - application/x-foo-mime
+        produces:
+        - application/json
+        - application/x-foo-mime
+        responses:
+          '403':
+            description: Returned when the user does not have permission to access the resource.
+          '404':
+            description: Returned when the resource does not exist.
+            schema:
+              jsonSchema:
+                type:
+                - STRING
+          '418':
+            description: I'm a teapot.
+            schema:
+              jsonSchema:
+                ref: ".grpc.gateway.examples.internal.proto.examplepb.NumericEnum"
+        securityDefinitions:
+          security:
+            ApiKeyAuth:
+              type: TYPE_API_KEY
+              name: X-API-Key
+              in: IN_HEADER
+              extensions:
+                x-amazon-apigateway-authorizer:
+                  authorizerResultTtlInSeconds: 60
+                  type: token
+                x-amazon-apigateway-authtype: oauth2
+            BasicAuth:
+              type: TYPE_BASIC
+        security:
+        - securityRequirement:
+            ApiKeyAuth: {}
+            BasicAuth: {}
+        - securityRequirement:
+            ApiKeyAuth: {}
+            OAuth2:
+              scope:
+              - read
+              - write
+        externalDocs:
+          description: More about gRPC-Gateway
+          url: https://github.com/grpc-ecosystem/grpc-gateway
+        extensions:
+          x-grpc-gateway-baz-list:
+          - one
+          - true
+          x-grpc-gateway-foo: bar
+  service:
+    - service: grpc.gateway.examples.internal.proto.examplepb.UnannotatedEchoService
+      option:
+        description: "UnannotatedEchoService description -- which should not be used in place of the documentation comment!"
+        external_docs:
+          url: "https://github.com/grpc-ecosystem/grpc-gateway"
+          description: "Find out more about UnannotatedEchoService"
+  method:
+    - method: grpc.gateway.examples.internal.proto.examplepb.UnannotatedEchoService.Echo
+      option:
+        description: "Description Echo"
+        summary: "Summary: Echo rpc"
+        tags: ["echo rpc"]
+        external_docs:
+          url: "https://github.com/grpc-ecosystem/grpc-gateway"
+          description: "Find out more Echo"
+        responses:
+          '200':
+            examples:
+              "application/json": '{"value": "the input value"}'
+          '503':
+            description: Returned when the resource is temporarily unavailable.
+            extensions:
+              x-number: 100
+          '404':
+            description: Returned when the resource does not exist.
+            schema:
+              jsonSchema:
+                type: [INTEGER]
+  message:
+    - message: grpc.gateway.examples.internal.proto.examplepb.UnannotatedSimpleMessage
+      option:
+        json_schema:
+          title: "A bit of everything"
+          description: "A simple message with many types"
+          required: ["id"]
+        external_docs:
+          url: "https://github.com/grpc-ecosystem/grpc-gateway"
+          description: "Find out more about UnannotatedSimpleMessage"
+        example: "{\"id\": \"myid\"}"
+  field:
+    - field: grpc.gateway.examples.internal.proto.examplepb.UnannotatedSimpleMessage.num
+      option:
+        description: "Int value field"
+        default: "42"
+        required: ['num']

From d6298e35e4c208152c7fe9381b1738ca4a81e592 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 14:30:02 -0700
Subject: [PATCH 08/17] Update Bazel build

---
 internal/descriptor/BUILD.bazel                      | 5 ++++-
 protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index e35fb6dfd31..ceb98344b2f 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -6,6 +6,7 @@ go_library(
     name = "go_default_library",
     srcs = [
         "grpc_api_configuration.go",
+        "openapi_configuration.go",
         "registry.go",
         "services.go",
         "types.go",
@@ -14,6 +15,7 @@ go_library(
     deps = [
         "//internal/casing:go_default_library",
         "//internal/descriptor/apiconfig:go_default_library",
+        "//internal/descriptor/openapiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "@com_github_ghodss_yaml//:go_default_library",
@@ -31,13 +33,14 @@ go_test(
     size = "small",
     srcs = [
         "grpc_api_configuration_test.go",
+        "openapi_configuration_test.go",
         "registry_test.go",
         "services_test.go",
         "types_test.go",
     ],
     embed = [":go_default_library"],
     deps = [
-        "//internal/descriptor/apiconfig:go_default_library",
+        "//internal/descriptor/openapiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
diff --git a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
index edec6baf010..35793faf629 100644
--- a/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
+++ b/protoc-gen-openapiv2/internal/genopenapi/BUILD.bazel
@@ -37,7 +37,7 @@ go_test(
     embed = [":go_default_library"],
     deps = [
         "//internal/descriptor:go_default_library",
-        "//internal/descriptor/apiconfig:go_default_library",
+        "//internal/descriptor/openapiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
         "//runtime:go_default_library",

From 10cdac880052e34d934b2f283b8fa30a1b76f459 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 14:58:32 -0700
Subject: [PATCH 09/17] Always prepend "." to proto selectors to mirror HTTP
 selector behavior

---
 internal/descriptor/registry.go      | 20 ++++----------------
 internal/descriptor/registry_test.go |  3 ++-
 2 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/internal/descriptor/registry.go b/internal/descriptor/registry.go
index ad078b369a7..dbe21c3e625 100644
--- a/internal/descriptor/registry.go
+++ b/internal/descriptor/registry.go
@@ -632,10 +632,7 @@ func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) er
 	}
 
 	for _, opt := range opts.Method {
-		qualifiedMethod := opt.Method
-		if !strings.HasPrefix(qualifiedMethod, ".") {
-			qualifiedMethod = "." + qualifiedMethod
-		}
+		qualifiedMethod := "." + opt.Method
 		if _, ok := methods[qualifiedMethod]; !ok {
 			return fmt.Errorf("no method %s found", opt.Method)
 		}
@@ -643,10 +640,7 @@ func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) er
 	}
 
 	for _, opt := range opts.Message {
-		qualifiedMessage := opt.Message
-		if !strings.HasPrefix(qualifiedMessage, ".") {
-			qualifiedMessage = "." + qualifiedMessage
-		}
+		qualifiedMessage := "." + opt.Message
 		if _, ok := r.msgs[qualifiedMessage]; !ok {
 			return fmt.Errorf("no message %s found", opt.Message)
 		}
@@ -654,10 +648,7 @@ func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) er
 	}
 
 	for _, opt := range opts.Service {
-		qualifiedService := opt.Service
-		if !strings.HasPrefix(qualifiedService, ".") {
-			qualifiedService = "." + qualifiedService
-		}
+		qualifiedService := "." + opt.Service
 		if _, ok := services[qualifiedService]; !ok {
 			return fmt.Errorf("no service %s found", opt.Service)
 		}
@@ -672,10 +663,7 @@ func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) er
 		}
 	}
 	for _, opt := range opts.Field {
-		qualifiedField := opt.Field
-		if !strings.HasPrefix(qualifiedField, ".") {
-			qualifiedField = "." + qualifiedField
-		}
+		qualifiedField := "." + opt.Field
 		if _, ok := fields[qualifiedField]; !ok {
 			return fmt.Errorf("no field %s found", opt.Field)
 		}
diff --git a/internal/descriptor/registry_test.go b/internal/descriptor/registry_test.go
index 90a7fe4e755..5ef5d72af81 100644
--- a/internal/descriptor/registry_test.go
+++ b/internal/descriptor/registry_test.go
@@ -703,7 +703,7 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 			},
 		},
 		{
-			desc: "successfully accept fully qualified names",
+			desc: "reject fully qualified names with leading \".\"",
 			options: &openapiconfig.OpenAPIOptions{
 				File: []*openapiconfig.OpenAPIFileOption{
 					{
@@ -731,6 +731,7 @@ func TestRegisterOpenAPIOptions(t *testing.T) {
 					},
 				},
 			},
+			shouldErr: true,
 		},
 		{
 			desc: "error if file does not exist",

From e0a785024c0e03ac5d1e5ad322220c84d131500d Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 15:02:11 -0700
Subject: [PATCH 10/17] Add OpenAPIv2 go proto stubs as dependency of
 protoc-gen-grpc-gateway

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 046f5b83740..fc1f76938ac 100644
--- a/Makefile
+++ b/Makefile
@@ -157,7 +157,7 @@ $(GO_GRPC_PLUGIN):
 $(OPENAPIV2_GO): $(OPENAPIV2_PROTO) $(GO_PLUGIN)
 	protoc -I $(PROTOC_INC_PATH) --plugin=$(GO_PLUGIN) -I. --go_out=paths=source_relative:. $(OPENAPIV2_PROTO)
 
-$(GATEWAY_PLUGIN): $(GATEWAY_PLUGIN_SRC)
+$(GATEWAY_PLUGIN): $(GATEWAY_PLUGIN_SRC) $(OPENAPIV2_GO)
 	go build -o $@ $(GATEWAY_PLUGIN_PKG)
 
 $(OPENAPI_PLUGIN): $(OPENAPI_PLUGIN_SRC) $(OPENAPIV2_GO)

From ad06ba4d7d22c396dfaaf91cb4679f1f57699dce Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 15:17:33 -0700
Subject: [PATCH 11/17] Add generated files and Bazel rules

---
 .../clients/unannotatedecho/BUILD.bazel       |   2 +
 .../clients/unannotatedecho/api/swagger.yaml  | 169 ++++++--
 .../clients/unannotatedecho/api_echo_rpc.go   | 389 ++++++++++++++++++
 .../api_unannotated_echo_service.go           | 320 ++++----------
 .../clients/unannotatedecho/client.go         |  10 +-
 .../clients/unannotatedecho/configuration.go  |   7 +-
 .../model_examplepb_numeric_enum.go           |  19 +
 .../model_examplepb_unannotated_embedded.go   |   5 +-
 ...el_examplepb_unannotated_simple_message.go |  10 +-
 .../unannotatedecho/model_protobuf_any.go     |   5 +-
 .../unannotatedecho/model_rpc_status.go       |   5 +-
 .../clients/unannotatedecho/response.go       |   5 +-
 12 files changed, 662 insertions(+), 284 deletions(-)
 create mode 100644 examples/internal/clients/unannotatedecho/api_echo_rpc.go
 create mode 100644 examples/internal/clients/unannotatedecho/model_examplepb_numeric_enum.go

diff --git a/examples/internal/clients/unannotatedecho/BUILD.bazel b/examples/internal/clients/unannotatedecho/BUILD.bazel
index 4fc29ca5847..b10e40c8658 100644
--- a/examples/internal/clients/unannotatedecho/BUILD.bazel
+++ b/examples/internal/clients/unannotatedecho/BUILD.bazel
@@ -5,9 +5,11 @@ package(default_visibility = ["//visibility:public"])
 go_library(
     name = "go_default_library",
     srcs = [
+        "api_echo_rpc.go",
         "api_unannotated_echo_service.go",
         "client.go",
         "configuration.go",
+        "model_examplepb_numeric_enum.go",
         "model_examplepb_unannotated_embedded.go",
         "model_examplepb_unannotated_simple_message.go",
         "model_protobuf_any.go",
diff --git a/examples/internal/clients/unannotatedecho/api/swagger.yaml b/examples/internal/clients/unannotatedecho/api/swagger.yaml
index 79e3952a630..bd9a9a4661f 100644
--- a/examples/internal/clients/unannotatedecho/api/swagger.yaml
+++ b/examples/internal/clients/unannotatedecho/api/swagger.yaml
@@ -5,19 +5,40 @@ info:
     \ annotations. See\nunannotated_echo_service.yaml for the equivalent of the annotations\
     \ in\ngRPC API configuration format.\n\nEcho Service API consists of a single\
     \ service which returns\na message."
-  version: "version not set"
-  title: "examples/internal/proto/examplepb/unannotated_echo_service.proto"
+  version: "1.0"
+  title: "Unannotated Echo"
+  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/master/LICENSE.txt"
+  x-something-something: "yadda"
+schemes:
+- "http"
+- "https"
+- "wss"
 consumes:
 - "application/json"
+- "application/x-foo-mime"
 produces:
 - "application/json"
+- "application/x-foo-mime"
+security:
+- ApiKeyAuth: []
+  BasicAuth: []
+- ApiKeyAuth: []
+  OAuth2:
+  - "read"
+  - "write"
 paths:
   /v1/example/echo/{id}:
     post:
       tags:
-      - "UnannotatedEchoService"
-      summary: "Echo method receives a simple message and returns it."
-      description: "The message posted as the id parameter will also be\nreturned."
+      - "echo rpc"
+      summary: "Summary: Echo rpc"
+      description: "Description Echo"
       operationId: "UnannotatedEchoService_Echo"
       parameters:
       - name: "id"
@@ -29,18 +50,41 @@ paths:
       responses:
         200:
           description: "A successful response."
+          examples:
+            application/json:
+              value: "the input value"
           schema:
             $ref: "#/definitions/examplepbUnannotatedSimpleMessage"
+        403:
+          description: "Returned when the user does not have permission to access\
+            \ the resource."
+          schema: {}
+        404:
+          description: "Returned when the resource does not exist."
+          schema:
+            type: "integer"
+            format: "integer"
+        418:
+          description: "I'm a teapot."
+          schema:
+            $ref: "#/definitions/examplepbNumericEnum"
+        503:
+          description: "Returned when the resource is temporarily unavailable."
+          schema: {}
+          x-number: 100
         default:
           description: "An unexpected error response."
           schema:
             $ref: "#/definitions/rpcStatus"
+      externalDocs:
+        description: "Find out more Echo"
+        url: "https://github.com/grpc-ecosystem/grpc-gateway"
   /v1/example/echo/{id}/{num}:
     get:
       tags:
-      - "UnannotatedEchoService"
-      summary: "Echo method receives a simple message and returns it."
-      description: "The message posted as the id parameter will also be\nreturned."
+      - "echo rpc"
+      summary: "Summary: Echo rpc"
+      description: "Description Echo"
       operationId: "UnannotatedEchoService_Echo2"
       parameters:
       - name: "id"
@@ -51,8 +95,10 @@ paths:
         x-exportParamName: "Id"
       - name: "num"
         in: "path"
+        description: "Int value field"
         required: true
         type: "string"
+        default: "42"
         format: "int64"
         x-exportParamName: "Num"
       - name: "duration"
@@ -110,12 +156,35 @@ paths:
       responses:
         200:
           description: "A successful response."
+          examples:
+            application/json:
+              value: "the input value"
           schema:
             $ref: "#/definitions/examplepbUnannotatedSimpleMessage"
+        403:
+          description: "Returned when the user does not have permission to access\
+            \ the resource."
+          schema: {}
+        404:
+          description: "Returned when the resource does not exist."
+          schema:
+            type: "integer"
+            format: "integer"
+        418:
+          description: "I'm a teapot."
+          schema:
+            $ref: "#/definitions/examplepbNumericEnum"
+        503:
+          description: "Returned when the resource is temporarily unavailable."
+          schema: {}
+          x-number: 100
         default:
           description: "An unexpected error response."
           schema:
             $ref: "#/definitions/rpcStatus"
+      externalDocs:
+        description: "Find out more Echo"
+        url: "https://github.com/grpc-ecosystem/grpc-gateway"
   /v1/example/echo_body:
     post:
       tags:
@@ -134,6 +203,19 @@ paths:
           description: "A successful response."
           schema:
             $ref: "#/definitions/examplepbUnannotatedSimpleMessage"
+        403:
+          description: "Returned when the user does not have permission to access\
+            \ the resource."
+          schema: {}
+        404:
+          description: "Returned when the resource does not exist."
+          schema:
+            type: "string"
+            format: "string"
+        418:
+          description: "I'm a teapot."
+          schema:
+            $ref: "#/definitions/examplepbNumericEnum"
         default:
           description: "An unexpected error response."
           schema:
@@ -154,11 +236,12 @@ paths:
         x-optionalDataType: "String"
       - name: "num"
         in: "query"
-        required: false
+        description: "Int value field"
+        required: true
         type: "string"
+        default: "42"
         format: "int64"
         x-exportParamName: "Num"
-        x-optionalDataType: "String"
       - name: "duration"
         in: "query"
         required: false
@@ -216,11 +299,43 @@ paths:
           description: "A successful response."
           schema:
             $ref: "#/definitions/examplepbUnannotatedSimpleMessage"
+        403:
+          description: "Returned when the user does not have permission to access\
+            \ the resource."
+          schema: {}
+        404:
+          description: "Returned when the resource does not exist."
+          schema:
+            type: "string"
+            format: "string"
+        418:
+          description: "I'm a teapot."
+          schema:
+            $ref: "#/definitions/examplepbNumericEnum"
         default:
           description: "An unexpected error response."
           schema:
             $ref: "#/definitions/rpcStatus"
+securityDefinitions:
+  ApiKeyAuth:
+    type: "apiKey"
+    name: "X-API-Key"
+    in: "header"
+    x-amazon-apigateway-authorizer:
+      authorizerResultTtlInSeconds: 60
+      type: "token"
+    x-amazon-apigateway-authtype: "oauth2"
+  BasicAuth:
+    type: "basic"
 definitions:
+  examplepbNumericEnum:
+    type: "string"
+    description: "NumericEnum is one or zero.\n\n - ZERO: ZERO means 0\n - ONE: ONE\
+      \ means 1"
+    enum:
+    - "ZERO"
+    - "ONE"
+    default: "ZERO"
   examplepbUnannotatedEmbedded:
     type: "object"
     properties:
@@ -230,11 +345,10 @@ definitions:
       note:
         type: "string"
     description: "Embedded represents a message embedded in SimpleMessage."
-    example:
-      note: "note"
-      progress: "progress"
   examplepbUnannotatedSimpleMessage:
     type: "object"
+    required:
+    - "id"
     properties:
       id:
         type: "string"
@@ -242,6 +356,8 @@ definitions:
       num:
         type: "string"
         format: "int64"
+        description: "Int value field"
+        default: "42"
       duration:
         type: "string"
       lineNum:
@@ -256,21 +372,13 @@ definitions:
         format: "int64"
       "no":
         $ref: "#/definitions/examplepbUnannotatedEmbedded"
-    description: "UnannotatedSimpleMessage represents a simple message sent to the\
-      \ unannotated Echo service."
+    externalDocs:
+      description: "Find out more about UnannotatedSimpleMessage"
+      url: "https://github.com/grpc-ecosystem/grpc-gateway"
+    title: "A bit of everything"
+    description: "A simple message with many types"
     example:
-      duration: "duration"
-      "no":
-        note: "note"
-        progress: "progress"
-      num: "num"
-      lineNum: "lineNum"
-      en: "en"
-      id: "id"
-      lang: "lang"
-      status:
-        note: "note"
-        progress: "progress"
+      id: "myid"
   protobufAny:
     type: "object"
     properties:
@@ -341,3 +449,10 @@ definitions:
         type: "array"
         items:
           $ref: "#/definitions/protobufAny"
+externalDocs:
+  description: "More about gRPC-Gateway"
+  url: "https://github.com/grpc-ecosystem/grpc-gateway"
+x-grpc-gateway-baz-list:
+- "one"
+- true
+x-grpc-gateway-foo: "bar"
diff --git a/examples/internal/clients/unannotatedecho/api_echo_rpc.go b/examples/internal/clients/unannotatedecho/api_echo_rpc.go
new file mode 100644
index 00000000000..a9cca7517c5
--- /dev/null
+++ b/examples/internal/clients/unannotatedecho/api_echo_rpc.go
@@ -0,0 +1,389 @@
+/*
+ * Unannotated Echo
+ *
+ * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
+ *
+ * API version: 1.0
+ * Contact: none@example.com
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package unannotatedecho
+
+import (
+	"context"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strings"
+	"fmt"
+	"github.com/antihax/optional"
+)
+
+// Linger please
+var (
+	_ context.Context
+)
+
+type EchoRpcApiService service
+
+/* 
+EchoRpcApiService Summary: Echo rpc
+Description Echo
+ * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param id Id represents the message identifier.
+
+@return ExamplepbUnannotatedSimpleMessage
+*/
+func (a *EchoRpcApiService) UnannotatedEchoServiceEcho(ctx context.Context, id string) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+	var (
+		localVarHttpMethod = strings.ToUpper("Post")
+		localVarPostBody   interface{}
+		localVarFileName   string
+		localVarFileBytes  []byte
+		localVarReturnValue ExamplepbUnannotatedSimpleMessage
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}"
+	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := url.Values{}
+	localVarFormParams := url.Values{}
+
+	// to determine the Content-Type header
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
+
+	// set Content-Type header
+	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
+	if localVarHttpContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHttpContentType
+	}
+
+	// to determine the Accept header
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
+
+	// set Accept header
+	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
+	if localVarHttpHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+	}
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return localVarReturnValue, nil, err
+	}
+
+	localVarHttpResponse, err := a.client.callAPI(r)
+	if err != nil || localVarHttpResponse == nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
+	localVarHttpResponse.Body.Close()
+	if err != nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	if localVarHttpResponse.StatusCode < 300 {
+		// If we succeed, return the data, otherwise pass on to decode error.
+		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+		if err == nil { 
+			return localVarReturnValue, localVarHttpResponse, err
+		}
+	}
+
+	if localVarHttpResponse.StatusCode >= 300 {
+		newErr := GenericSwaggerError{
+			body: localVarBody,
+			error: localVarHttpResponse.Status,
+		}
+		
+		if localVarHttpResponse.StatusCode == 200 {
+			var v ExamplepbUnannotatedSimpleMessage
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 404 {
+			var v int32
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 503 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 0 {
+			var v RpcStatus
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		return localVarReturnValue, localVarHttpResponse, newErr
+	}
+
+	return localVarReturnValue, localVarHttpResponse, nil
+}
+
+/* 
+EchoRpcApiService Summary: Echo rpc
+Description Echo
+ * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param id Id represents the message identifier.
+ * @param num Int value field
+ * @param optional nil or *UnannotatedEchoServiceEcho2Opts - Optional Parameters:
+     * @param "Duration" (optional.String) - 
+     * @param "LineNum" (optional.String) - 
+     * @param "Lang" (optional.String) - 
+     * @param "StatusProgress" (optional.String) - 
+     * @param "StatusNote" (optional.String) - 
+     * @param "En" (optional.String) - 
+     * @param "NoProgress" (optional.String) - 
+     * @param "NoNote" (optional.String) - 
+
+@return ExamplepbUnannotatedSimpleMessage
+*/
+
+type UnannotatedEchoServiceEcho2Opts struct { 
+	Duration optional.String
+	LineNum optional.String
+	Lang optional.String
+	StatusProgress optional.String
+	StatusNote optional.String
+	En optional.String
+	NoProgress optional.String
+	NoNote optional.String
+}
+
+func (a *EchoRpcApiService) UnannotatedEchoServiceEcho2(ctx context.Context, id string, num string, localVarOptionals *UnannotatedEchoServiceEcho2Opts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+	var (
+		localVarHttpMethod = strings.ToUpper("Get")
+		localVarPostBody   interface{}
+		localVarFileName   string
+		localVarFileBytes  []byte
+		localVarReturnValue ExamplepbUnannotatedSimpleMessage
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}/{num}"
+	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+	localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := url.Values{}
+	localVarFormParams := url.Values{}
+
+	if localVarOptionals != nil && localVarOptionals.Duration.IsSet() {
+		localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.LineNum.IsSet() {
+		localVarQueryParams.Add("lineNum", parameterToString(localVarOptionals.LineNum.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.Lang.IsSet() {
+		localVarQueryParams.Add("lang", parameterToString(localVarOptionals.Lang.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.StatusProgress.IsSet() {
+		localVarQueryParams.Add("status.progress", parameterToString(localVarOptionals.StatusProgress.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.StatusNote.IsSet() {
+		localVarQueryParams.Add("status.note", parameterToString(localVarOptionals.StatusNote.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.En.IsSet() {
+		localVarQueryParams.Add("en", parameterToString(localVarOptionals.En.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.NoProgress.IsSet() {
+		localVarQueryParams.Add("no.progress", parameterToString(localVarOptionals.NoProgress.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.NoNote.IsSet() {
+		localVarQueryParams.Add("no.note", parameterToString(localVarOptionals.NoNote.Value(), ""))
+	}
+	// to determine the Content-Type header
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
+
+	// set Content-Type header
+	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
+	if localVarHttpContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHttpContentType
+	}
+
+	// to determine the Accept header
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
+
+	// set Accept header
+	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
+	if localVarHttpHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+	}
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return localVarReturnValue, nil, err
+	}
+
+	localVarHttpResponse, err := a.client.callAPI(r)
+	if err != nil || localVarHttpResponse == nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
+	localVarHttpResponse.Body.Close()
+	if err != nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	if localVarHttpResponse.StatusCode < 300 {
+		// If we succeed, return the data, otherwise pass on to decode error.
+		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+		if err == nil { 
+			return localVarReturnValue, localVarHttpResponse, err
+		}
+	}
+
+	if localVarHttpResponse.StatusCode >= 300 {
+		newErr := GenericSwaggerError{
+			body: localVarBody,
+			error: localVarHttpResponse.Status,
+		}
+		
+		if localVarHttpResponse.StatusCode == 200 {
+			var v ExamplepbUnannotatedSimpleMessage
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 404 {
+			var v int32
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 503 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 0 {
+			var v RpcStatus
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		return localVarReturnValue, localVarHttpResponse, newErr
+	}
+
+	return localVarReturnValue, localVarHttpResponse, nil
+}
diff --git a/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go b/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
index 79d1c9ae844..27c0afd27d8 100644
--- a/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
+++ b/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
@@ -15,7 +16,6 @@ import (
 	"net/http"
 	"net/url"
 	"strings"
-	"fmt"
 	"github.com/antihax/optional"
 )
 
@@ -27,14 +27,13 @@ var (
 type UnannotatedEchoServiceApiService service
 
 /* 
-UnannotatedEchoServiceApiService Echo method receives a simple message and returns it.
-The message posted as the id parameter will also be returned.
+UnannotatedEchoServiceApiService EchoBody method receives a simple message and returns it.
  * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param id Id represents the message identifier.
+ * @param body
 
 @return ExamplepbUnannotatedSimpleMessage
 */
-func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx context.Context, id string) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoBody(ctx context.Context, body ExamplepbUnannotatedSimpleMessage) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
 	var (
 		localVarHttpMethod = strings.ToUpper("Post")
 		localVarPostBody   interface{}
@@ -44,15 +43,14 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx contex
 	)
 
 	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}"
-	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+	localVarPath := a.client.cfg.BasePath + "/v1/example/echo_body"
 
 	localVarHeaderParams := make(map[string]string)
 	localVarQueryParams := url.Values{}
 	localVarFormParams := url.Values{}
 
 	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json"}
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
 
 	// set Content-Type header
 	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
@@ -61,13 +59,28 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx contex
 	}
 
 	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json"}
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
 
 	// set Accept header
 	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
 	if localVarHttpHeaderAccept != "" {
 		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
 	}
+	// body params
+	localVarPostBody = &body
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
 	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
 	if err != nil {
 		return localVarReturnValue, nil, err
@@ -109,8 +122,8 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx contex
 				return localVarReturnValue, localVarHttpResponse, newErr
 		}
 		
-		if localVarHttpResponse.StatusCode == 0 {
-			var v RpcStatus
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
 			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
 				if err != nil {
 					newErr.error = err.Error()
@@ -120,133 +133,8 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx contex
 				return localVarReturnValue, localVarHttpResponse, newErr
 		}
 		
-		return localVarReturnValue, localVarHttpResponse, newErr
-	}
-
-	return localVarReturnValue, localVarHttpResponse, nil
-}
-
-/* 
-UnannotatedEchoServiceApiService Echo method receives a simple message and returns it.
-The message posted as the id parameter will also be returned.
- * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param id Id represents the message identifier.
- * @param num
- * @param optional nil or *UnannotatedEchoServiceEcho2Opts - Optional Parameters:
-     * @param "Duration" (optional.String) - 
-     * @param "LineNum" (optional.String) - 
-     * @param "Lang" (optional.String) - 
-     * @param "StatusProgress" (optional.String) - 
-     * @param "StatusNote" (optional.String) - 
-     * @param "En" (optional.String) - 
-     * @param "NoProgress" (optional.String) - 
-     * @param "NoNote" (optional.String) - 
-
-@return ExamplepbUnannotatedSimpleMessage
-*/
-
-type UnannotatedEchoServiceEcho2Opts struct { 
-	Duration optional.String
-	LineNum optional.String
-	Lang optional.String
-	StatusProgress optional.String
-	StatusNote optional.String
-	En optional.String
-	NoProgress optional.String
-	NoNote optional.String
-}
-
-func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho2(ctx context.Context, id string, num string, localVarOptionals *UnannotatedEchoServiceEcho2Opts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
-	var (
-		localVarHttpMethod = strings.ToUpper("Get")
-		localVarPostBody   interface{}
-		localVarFileName   string
-		localVarFileBytes  []byte
-		localVarReturnValue ExamplepbUnannotatedSimpleMessage
-	)
-
-	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}/{num}"
-	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
-	localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1)
-
-	localVarHeaderParams := make(map[string]string)
-	localVarQueryParams := url.Values{}
-	localVarFormParams := url.Values{}
-
-	if localVarOptionals != nil && localVarOptionals.Duration.IsSet() {
-		localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.LineNum.IsSet() {
-		localVarQueryParams.Add("lineNum", parameterToString(localVarOptionals.LineNum.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.Lang.IsSet() {
-		localVarQueryParams.Add("lang", parameterToString(localVarOptionals.Lang.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.StatusProgress.IsSet() {
-		localVarQueryParams.Add("status.progress", parameterToString(localVarOptionals.StatusProgress.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.StatusNote.IsSet() {
-		localVarQueryParams.Add("status.note", parameterToString(localVarOptionals.StatusNote.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.En.IsSet() {
-		localVarQueryParams.Add("en", parameterToString(localVarOptionals.En.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.NoProgress.IsSet() {
-		localVarQueryParams.Add("no.progress", parameterToString(localVarOptionals.NoProgress.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.NoNote.IsSet() {
-		localVarQueryParams.Add("no.note", parameterToString(localVarOptionals.NoNote.Value(), ""))
-	}
-	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json"}
-
-	// set Content-Type header
-	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
-	if localVarHttpContentType != "" {
-		localVarHeaderParams["Content-Type"] = localVarHttpContentType
-	}
-
-	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json"}
-
-	// set Accept header
-	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
-	if localVarHttpHeaderAccept != "" {
-		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
-	}
-	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
-	if err != nil {
-		return localVarReturnValue, nil, err
-	}
-
-	localVarHttpResponse, err := a.client.callAPI(r)
-	if err != nil || localVarHttpResponse == nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
-	localVarHttpResponse.Body.Close()
-	if err != nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	if localVarHttpResponse.StatusCode < 300 {
-		// If we succeed, return the data, otherwise pass on to decode error.
-		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-		if err == nil { 
-			return localVarReturnValue, localVarHttpResponse, err
-		}
-	}
-
-	if localVarHttpResponse.StatusCode >= 300 {
-		newErr := GenericSwaggerError{
-			body: localVarBody,
-			error: localVarHttpResponse.Status,
-		}
-		
-		if localVarHttpResponse.StatusCode == 200 {
-			var v ExamplepbUnannotatedSimpleMessage
+		if localVarHttpResponse.StatusCode == 404 {
+			var v string
 			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
 				if err != nil {
 					newErr.error = err.Error()
@@ -256,97 +144,8 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho2(ctx conte
 				return localVarReturnValue, localVarHttpResponse, newErr
 		}
 		
-		if localVarHttpResponse.StatusCode == 0 {
-			var v RpcStatus
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		return localVarReturnValue, localVarHttpResponse, newErr
-	}
-
-	return localVarReturnValue, localVarHttpResponse, nil
-}
-
-/* 
-UnannotatedEchoServiceApiService EchoBody method receives a simple message and returns it.
- * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param body
-
-@return ExamplepbUnannotatedSimpleMessage
-*/
-func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoBody(ctx context.Context, body ExamplepbUnannotatedSimpleMessage) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
-	var (
-		localVarHttpMethod = strings.ToUpper("Post")
-		localVarPostBody   interface{}
-		localVarFileName   string
-		localVarFileBytes  []byte
-		localVarReturnValue ExamplepbUnannotatedSimpleMessage
-	)
-
-	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/v1/example/echo_body"
-
-	localVarHeaderParams := make(map[string]string)
-	localVarQueryParams := url.Values{}
-	localVarFormParams := url.Values{}
-
-	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json"}
-
-	// set Content-Type header
-	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
-	if localVarHttpContentType != "" {
-		localVarHeaderParams["Content-Type"] = localVarHttpContentType
-	}
-
-	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json"}
-
-	// set Accept header
-	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
-	if localVarHttpHeaderAccept != "" {
-		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
-	}
-	// body params
-	localVarPostBody = &body
-	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
-	if err != nil {
-		return localVarReturnValue, nil, err
-	}
-
-	localVarHttpResponse, err := a.client.callAPI(r)
-	if err != nil || localVarHttpResponse == nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
-	localVarHttpResponse.Body.Close()
-	if err != nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	if localVarHttpResponse.StatusCode < 300 {
-		// If we succeed, return the data, otherwise pass on to decode error.
-		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-		if err == nil { 
-			return localVarReturnValue, localVarHttpResponse, err
-		}
-	}
-
-	if localVarHttpResponse.StatusCode >= 300 {
-		newErr := GenericSwaggerError{
-			body: localVarBody,
-			error: localVarHttpResponse.Status,
-		}
-		
-		if localVarHttpResponse.StatusCode == 200 {
-			var v ExamplepbUnannotatedSimpleMessage
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
 			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
 				if err != nil {
 					newErr.error = err.Error()
@@ -376,9 +175,9 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoBody(ctx co
 /* 
 UnannotatedEchoServiceApiService EchoDelete method receives a simple message and returns it.
  * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param num Int value field
  * @param optional nil or *UnannotatedEchoServiceEchoDeleteOpts - Optional Parameters:
      * @param "Id" (optional.String) -  Id represents the message identifier.
-     * @param "Num" (optional.String) - 
      * @param "Duration" (optional.String) - 
      * @param "LineNum" (optional.String) - 
      * @param "Lang" (optional.String) - 
@@ -393,7 +192,6 @@ UnannotatedEchoServiceApiService EchoDelete method receives a simple message and
 
 type UnannotatedEchoServiceEchoDeleteOpts struct { 
 	Id optional.String
-	Num optional.String
 	Duration optional.String
 	LineNum optional.String
 	Lang optional.String
@@ -404,7 +202,7 @@ type UnannotatedEchoServiceEchoDeleteOpts struct {
 	NoNote optional.String
 }
 
-func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx context.Context, localVarOptionals *UnannotatedEchoServiceEchoDeleteOpts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx context.Context, num string, localVarOptionals *UnannotatedEchoServiceEchoDeleteOpts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
 	var (
 		localVarHttpMethod = strings.ToUpper("Delete")
 		localVarPostBody   interface{}
@@ -423,9 +221,7 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx
 	if localVarOptionals != nil && localVarOptionals.Id.IsSet() {
 		localVarQueryParams.Add("id", parameterToString(localVarOptionals.Id.Value(), ""))
 	}
-	if localVarOptionals != nil && localVarOptionals.Num.IsSet() {
-		localVarQueryParams.Add("num", parameterToString(localVarOptionals.Num.Value(), ""))
-	}
+	localVarQueryParams.Add("num", parameterToString(num, ""))
 	if localVarOptionals != nil && localVarOptionals.Duration.IsSet() {
 		localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), ""))
 	}
@@ -451,7 +247,7 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx
 		localVarQueryParams.Add("no.note", parameterToString(localVarOptionals.NoNote.Value(), ""))
 	}
 	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json"}
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
 
 	// set Content-Type header
 	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
@@ -460,13 +256,26 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx
 	}
 
 	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json"}
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
 
 	// set Accept header
 	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
 	if localVarHttpHeaderAccept != "" {
 		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
 	}
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
 	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
 	if err != nil {
 		return localVarReturnValue, nil, err
@@ -508,6 +317,39 @@ func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEchoDelete(ctx
 				return localVarReturnValue, localVarHttpResponse, newErr
 		}
 		
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 404 {
+			var v string
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
 		if localVarHttpResponse.StatusCode == 0 {
 			var v RpcStatus
 			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
diff --git a/examples/internal/clients/unannotatedecho/client.go b/examples/internal/clients/unannotatedecho/client.go
index 7907be83032..7c7fb6c03eb 100644
--- a/examples/internal/clients/unannotatedecho/client.go
+++ b/examples/internal/clients/unannotatedecho/client.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
@@ -37,7 +38,7 @@ var (
 	xmlCheck  = regexp.MustCompile("(?i:[application|text]/xml)")
 )
 
-// APIClient manages communication with the examples/internal/proto/examplepb/unannotated_echo_service.proto API vversion not set
+// APIClient manages communication with the Unannotated Echo API v1.0
 // In most cases there should be only one, shared, APIClient.
 type APIClient struct {
 	cfg    *Configuration
@@ -45,6 +46,8 @@ type APIClient struct {
 
 	// API Services
 
+	EchoRpcApi *EchoRpcApiService
+
 	UnannotatedEchoServiceApi *UnannotatedEchoServiceApiService
 }
 
@@ -64,6 +67,7 @@ func NewAPIClient(cfg *Configuration) *APIClient {
 	c.common.client = c
 
 	// API Services
+	c.EchoRpcApi = (*EchoRpcApiService)(&c.common)
 	c.UnannotatedEchoServiceApi = (*UnannotatedEchoServiceApiService)(&c.common)
 
 	return c
diff --git a/examples/internal/clients/unannotatedecho/configuration.go b/examples/internal/clients/unannotatedecho/configuration.go
index 1b38b7fb35f..a471d97120f 100644
--- a/examples/internal/clients/unannotatedecho/configuration.go
+++ b/examples/internal/clients/unannotatedecho/configuration.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
@@ -60,7 +61,7 @@ type Configuration struct {
 
 func NewConfiguration() *Configuration {
 	cfg := &Configuration{
-		BasePath:      "https://localhost",
+		BasePath:      "http://localhost",
 		DefaultHeader: make(map[string]string),
 		UserAgent:     "Swagger-Codegen/1.0.0/go",
 	}
diff --git a/examples/internal/clients/unannotatedecho/model_examplepb_numeric_enum.go b/examples/internal/clients/unannotatedecho/model_examplepb_numeric_enum.go
new file mode 100644
index 00000000000..a5dc7def37a
--- /dev/null
+++ b/examples/internal/clients/unannotatedecho/model_examplepb_numeric_enum.go
@@ -0,0 +1,19 @@
+/*
+ * Unannotated Echo
+ *
+ * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
+ *
+ * API version: 1.0
+ * Contact: none@example.com
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package unannotatedecho
+// ExamplepbNumericEnum : NumericEnum is one or zero.   - ZERO: ZERO means 0  - ONE: ONE means 1
+type ExamplepbNumericEnum string
+
+// List of examplepbNumericEnum
+const (
+	ZERO_ExamplepbNumericEnum ExamplepbNumericEnum = "ZERO"
+	ONE_ExamplepbNumericEnum ExamplepbNumericEnum = "ONE"
+)
diff --git a/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_embedded.go b/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_embedded.go
index 0372fc9b738..e91de2d8a02 100644
--- a/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_embedded.go
+++ b/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_embedded.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
diff --git a/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_simple_message.go b/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_simple_message.go
index 79c87c6416e..13ee07cae2b 100644
--- a/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_simple_message.go
+++ b/examples/internal/clients/unannotatedecho/model_examplepb_unannotated_simple_message.go
@@ -1,18 +1,20 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
 package unannotatedecho
 
-// UnannotatedSimpleMessage represents a simple message sent to the unannotated Echo service.
+// A simple message with many types
 type ExamplepbUnannotatedSimpleMessage struct {
 	// Id represents the message identifier.
-	Id string `json:"id,omitempty"`
+	Id string `json:"id"`
+	// Int value field
 	Num string `json:"num,omitempty"`
 	Duration string `json:"duration,omitempty"`
 	LineNum string `json:"lineNum,omitempty"`
diff --git a/examples/internal/clients/unannotatedecho/model_protobuf_any.go b/examples/internal/clients/unannotatedecho/model_protobuf_any.go
index 4b15d3ef00d..6ffa3a72d3e 100644
--- a/examples/internal/clients/unannotatedecho/model_protobuf_any.go
+++ b/examples/internal/clients/unannotatedecho/model_protobuf_any.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
diff --git a/examples/internal/clients/unannotatedecho/model_rpc_status.go b/examples/internal/clients/unannotatedecho/model_rpc_status.go
index 2ffb74dc75d..d97aeea06d4 100644
--- a/examples/internal/clients/unannotatedecho/model_rpc_status.go
+++ b/examples/internal/clients/unannotatedecho/model_rpc_status.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 
diff --git a/examples/internal/clients/unannotatedecho/response.go b/examples/internal/clients/unannotatedecho/response.go
index 56f6b3e0bf8..16ded8dab1d 100644
--- a/examples/internal/clients/unannotatedecho/response.go
+++ b/examples/internal/clients/unannotatedecho/response.go
@@ -1,9 +1,10 @@
 /*
- * examples/internal/proto/examplepb/unannotated_echo_service.proto
+ * Unannotated Echo
  *
  * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
  *
- * API version: version not set
+ * API version: 1.0
+ * Contact: none@example.com
  * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
  */
 

From a750c98afa0b9248275b35fd73bdf1b0105d2a17 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Mon, 14 Sep 2020 15:48:31 -0700
Subject: [PATCH 12/17] Fix YAML annotation

Don't set the method option "tag" so examples/internal/clients/unannotatedecho/api/swagger.yaml
generates a examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go that works with existing tests.
---
 .../clients/unannotatedecho/BUILD.bazel       |   1 -
 .../clients/unannotatedecho/api/swagger.yaml  |   4 +-
 .../clients/unannotatedecho/api_echo_rpc.go   | 389 ------------------
 .../api_unannotated_echo_service.go           | 362 ++++++++++++++++
 .../clients/unannotatedecho/client.go         |   3 -
 .../unannotated_echo_service.swagger.json     |   4 +-
 .../unannotated_echo_service.swagger.yaml     |   1 -
 7 files changed, 366 insertions(+), 398 deletions(-)
 delete mode 100644 examples/internal/clients/unannotatedecho/api_echo_rpc.go

diff --git a/examples/internal/clients/unannotatedecho/BUILD.bazel b/examples/internal/clients/unannotatedecho/BUILD.bazel
index b10e40c8658..574f2eaf0d5 100644
--- a/examples/internal/clients/unannotatedecho/BUILD.bazel
+++ b/examples/internal/clients/unannotatedecho/BUILD.bazel
@@ -5,7 +5,6 @@ package(default_visibility = ["//visibility:public"])
 go_library(
     name = "go_default_library",
     srcs = [
-        "api_echo_rpc.go",
         "api_unannotated_echo_service.go",
         "client.go",
         "configuration.go",
diff --git a/examples/internal/clients/unannotatedecho/api/swagger.yaml b/examples/internal/clients/unannotatedecho/api/swagger.yaml
index bd9a9a4661f..73199b9e063 100644
--- a/examples/internal/clients/unannotatedecho/api/swagger.yaml
+++ b/examples/internal/clients/unannotatedecho/api/swagger.yaml
@@ -36,7 +36,7 @@ paths:
   /v1/example/echo/{id}:
     post:
       tags:
-      - "echo rpc"
+      - "UnannotatedEchoService"
       summary: "Summary: Echo rpc"
       description: "Description Echo"
       operationId: "UnannotatedEchoService_Echo"
@@ -82,7 +82,7 @@ paths:
   /v1/example/echo/{id}/{num}:
     get:
       tags:
-      - "echo rpc"
+      - "UnannotatedEchoService"
       summary: "Summary: Echo rpc"
       description: "Description Echo"
       operationId: "UnannotatedEchoService_Echo2"
diff --git a/examples/internal/clients/unannotatedecho/api_echo_rpc.go b/examples/internal/clients/unannotatedecho/api_echo_rpc.go
deleted file mode 100644
index a9cca7517c5..00000000000
--- a/examples/internal/clients/unannotatedecho/api_echo_rpc.go
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Unannotated Echo
- *
- * Unannotated Echo Service Similar to echo_service.proto but without annotations. See unannotated_echo_service.yaml for the equivalent of the annotations in gRPC API configuration format.  Echo Service API consists of a single service which returns a message.
- *
- * API version: 1.0
- * Contact: none@example.com
- * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
- */
-
-package unannotatedecho
-
-import (
-	"context"
-	"io/ioutil"
-	"net/http"
-	"net/url"
-	"strings"
-	"fmt"
-	"github.com/antihax/optional"
-)
-
-// Linger please
-var (
-	_ context.Context
-)
-
-type EchoRpcApiService service
-
-/* 
-EchoRpcApiService Summary: Echo rpc
-Description Echo
- * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param id Id represents the message identifier.
-
-@return ExamplepbUnannotatedSimpleMessage
-*/
-func (a *EchoRpcApiService) UnannotatedEchoServiceEcho(ctx context.Context, id string) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
-	var (
-		localVarHttpMethod = strings.ToUpper("Post")
-		localVarPostBody   interface{}
-		localVarFileName   string
-		localVarFileBytes  []byte
-		localVarReturnValue ExamplepbUnannotatedSimpleMessage
-	)
-
-	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}"
-	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
-
-	localVarHeaderParams := make(map[string]string)
-	localVarQueryParams := url.Values{}
-	localVarFormParams := url.Values{}
-
-	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
-
-	// set Content-Type header
-	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
-	if localVarHttpContentType != "" {
-		localVarHeaderParams["Content-Type"] = localVarHttpContentType
-	}
-
-	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
-
-	// set Accept header
-	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
-	if localVarHttpHeaderAccept != "" {
-		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
-	}
-	if ctx != nil {
-		// API Key Authentication
-		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
-			var key string
-			if auth.Prefix != "" {
-				key = auth.Prefix + " " + auth.Key
-			} else {
-				key = auth.Key
-			}
-			localVarHeaderParams["X-API-Key"] = key
-			
-		}
-	}
-	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
-	if err != nil {
-		return localVarReturnValue, nil, err
-	}
-
-	localVarHttpResponse, err := a.client.callAPI(r)
-	if err != nil || localVarHttpResponse == nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
-	localVarHttpResponse.Body.Close()
-	if err != nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	if localVarHttpResponse.StatusCode < 300 {
-		// If we succeed, return the data, otherwise pass on to decode error.
-		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-		if err == nil { 
-			return localVarReturnValue, localVarHttpResponse, err
-		}
-	}
-
-	if localVarHttpResponse.StatusCode >= 300 {
-		newErr := GenericSwaggerError{
-			body: localVarBody,
-			error: localVarHttpResponse.Status,
-		}
-		
-		if localVarHttpResponse.StatusCode == 200 {
-			var v ExamplepbUnannotatedSimpleMessage
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 403 {
-			var v interface{}
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 404 {
-			var v int32
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 418 {
-			var v ExamplepbNumericEnum
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 503 {
-			var v interface{}
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 0 {
-			var v RpcStatus
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		return localVarReturnValue, localVarHttpResponse, newErr
-	}
-
-	return localVarReturnValue, localVarHttpResponse, nil
-}
-
-/* 
-EchoRpcApiService Summary: Echo rpc
-Description Echo
- * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
- * @param id Id represents the message identifier.
- * @param num Int value field
- * @param optional nil or *UnannotatedEchoServiceEcho2Opts - Optional Parameters:
-     * @param "Duration" (optional.String) - 
-     * @param "LineNum" (optional.String) - 
-     * @param "Lang" (optional.String) - 
-     * @param "StatusProgress" (optional.String) - 
-     * @param "StatusNote" (optional.String) - 
-     * @param "En" (optional.String) - 
-     * @param "NoProgress" (optional.String) - 
-     * @param "NoNote" (optional.String) - 
-
-@return ExamplepbUnannotatedSimpleMessage
-*/
-
-type UnannotatedEchoServiceEcho2Opts struct { 
-	Duration optional.String
-	LineNum optional.String
-	Lang optional.String
-	StatusProgress optional.String
-	StatusNote optional.String
-	En optional.String
-	NoProgress optional.String
-	NoNote optional.String
-}
-
-func (a *EchoRpcApiService) UnannotatedEchoServiceEcho2(ctx context.Context, id string, num string, localVarOptionals *UnannotatedEchoServiceEcho2Opts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
-	var (
-		localVarHttpMethod = strings.ToUpper("Get")
-		localVarPostBody   interface{}
-		localVarFileName   string
-		localVarFileBytes  []byte
-		localVarReturnValue ExamplepbUnannotatedSimpleMessage
-	)
-
-	// create path and map variables
-	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}/{num}"
-	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
-	localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1)
-
-	localVarHeaderParams := make(map[string]string)
-	localVarQueryParams := url.Values{}
-	localVarFormParams := url.Values{}
-
-	if localVarOptionals != nil && localVarOptionals.Duration.IsSet() {
-		localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.LineNum.IsSet() {
-		localVarQueryParams.Add("lineNum", parameterToString(localVarOptionals.LineNum.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.Lang.IsSet() {
-		localVarQueryParams.Add("lang", parameterToString(localVarOptionals.Lang.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.StatusProgress.IsSet() {
-		localVarQueryParams.Add("status.progress", parameterToString(localVarOptionals.StatusProgress.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.StatusNote.IsSet() {
-		localVarQueryParams.Add("status.note", parameterToString(localVarOptionals.StatusNote.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.En.IsSet() {
-		localVarQueryParams.Add("en", parameterToString(localVarOptionals.En.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.NoProgress.IsSet() {
-		localVarQueryParams.Add("no.progress", parameterToString(localVarOptionals.NoProgress.Value(), ""))
-	}
-	if localVarOptionals != nil && localVarOptionals.NoNote.IsSet() {
-		localVarQueryParams.Add("no.note", parameterToString(localVarOptionals.NoNote.Value(), ""))
-	}
-	// to determine the Content-Type header
-	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
-
-	// set Content-Type header
-	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
-	if localVarHttpContentType != "" {
-		localVarHeaderParams["Content-Type"] = localVarHttpContentType
-	}
-
-	// to determine the Accept header
-	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
-
-	// set Accept header
-	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
-	if localVarHttpHeaderAccept != "" {
-		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
-	}
-	if ctx != nil {
-		// API Key Authentication
-		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
-			var key string
-			if auth.Prefix != "" {
-				key = auth.Prefix + " " + auth.Key
-			} else {
-				key = auth.Key
-			}
-			localVarHeaderParams["X-API-Key"] = key
-			
-		}
-	}
-	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
-	if err != nil {
-		return localVarReturnValue, nil, err
-	}
-
-	localVarHttpResponse, err := a.client.callAPI(r)
-	if err != nil || localVarHttpResponse == nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
-	localVarHttpResponse.Body.Close()
-	if err != nil {
-		return localVarReturnValue, localVarHttpResponse, err
-	}
-
-	if localVarHttpResponse.StatusCode < 300 {
-		// If we succeed, return the data, otherwise pass on to decode error.
-		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-		if err == nil { 
-			return localVarReturnValue, localVarHttpResponse, err
-		}
-	}
-
-	if localVarHttpResponse.StatusCode >= 300 {
-		newErr := GenericSwaggerError{
-			body: localVarBody,
-			error: localVarHttpResponse.Status,
-		}
-		
-		if localVarHttpResponse.StatusCode == 200 {
-			var v ExamplepbUnannotatedSimpleMessage
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 403 {
-			var v interface{}
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 404 {
-			var v int32
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 418 {
-			var v ExamplepbNumericEnum
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 503 {
-			var v interface{}
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		if localVarHttpResponse.StatusCode == 0 {
-			var v RpcStatus
-			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
-				if err != nil {
-					newErr.error = err.Error()
-					return localVarReturnValue, localVarHttpResponse, newErr
-				}
-				newErr.model = v
-				return localVarReturnValue, localVarHttpResponse, newErr
-		}
-		
-		return localVarReturnValue, localVarHttpResponse, newErr
-	}
-
-	return localVarReturnValue, localVarHttpResponse, nil
-}
diff --git a/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go b/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
index 27c0afd27d8..a9359ada8a8 100644
--- a/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
+++ b/examples/internal/clients/unannotatedecho/api_unannotated_echo_service.go
@@ -16,6 +16,7 @@ import (
 	"net/http"
 	"net/url"
 	"strings"
+	"fmt"
 	"github.com/antihax/optional"
 )
 
@@ -26,6 +27,367 @@ var (
 
 type UnannotatedEchoServiceApiService service
 
+/* 
+UnannotatedEchoServiceApiService Summary: Echo rpc
+Description Echo
+ * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param id Id represents the message identifier.
+
+@return ExamplepbUnannotatedSimpleMessage
+*/
+func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho(ctx context.Context, id string) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+	var (
+		localVarHttpMethod = strings.ToUpper("Post")
+		localVarPostBody   interface{}
+		localVarFileName   string
+		localVarFileBytes  []byte
+		localVarReturnValue ExamplepbUnannotatedSimpleMessage
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}"
+	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := url.Values{}
+	localVarFormParams := url.Values{}
+
+	// to determine the Content-Type header
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
+
+	// set Content-Type header
+	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
+	if localVarHttpContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHttpContentType
+	}
+
+	// to determine the Accept header
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
+
+	// set Accept header
+	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
+	if localVarHttpHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+	}
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return localVarReturnValue, nil, err
+	}
+
+	localVarHttpResponse, err := a.client.callAPI(r)
+	if err != nil || localVarHttpResponse == nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
+	localVarHttpResponse.Body.Close()
+	if err != nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	if localVarHttpResponse.StatusCode < 300 {
+		// If we succeed, return the data, otherwise pass on to decode error.
+		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+		if err == nil { 
+			return localVarReturnValue, localVarHttpResponse, err
+		}
+	}
+
+	if localVarHttpResponse.StatusCode >= 300 {
+		newErr := GenericSwaggerError{
+			body: localVarBody,
+			error: localVarHttpResponse.Status,
+		}
+		
+		if localVarHttpResponse.StatusCode == 200 {
+			var v ExamplepbUnannotatedSimpleMessage
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 404 {
+			var v int32
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 503 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 0 {
+			var v RpcStatus
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		return localVarReturnValue, localVarHttpResponse, newErr
+	}
+
+	return localVarReturnValue, localVarHttpResponse, nil
+}
+
+/* 
+UnannotatedEchoServiceApiService Summary: Echo rpc
+Description Echo
+ * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
+ * @param id Id represents the message identifier.
+ * @param num Int value field
+ * @param optional nil or *UnannotatedEchoServiceEcho2Opts - Optional Parameters:
+     * @param "Duration" (optional.String) - 
+     * @param "LineNum" (optional.String) - 
+     * @param "Lang" (optional.String) - 
+     * @param "StatusProgress" (optional.String) - 
+     * @param "StatusNote" (optional.String) - 
+     * @param "En" (optional.String) - 
+     * @param "NoProgress" (optional.String) - 
+     * @param "NoNote" (optional.String) - 
+
+@return ExamplepbUnannotatedSimpleMessage
+*/
+
+type UnannotatedEchoServiceEcho2Opts struct { 
+	Duration optional.String
+	LineNum optional.String
+	Lang optional.String
+	StatusProgress optional.String
+	StatusNote optional.String
+	En optional.String
+	NoProgress optional.String
+	NoNote optional.String
+}
+
+func (a *UnannotatedEchoServiceApiService) UnannotatedEchoServiceEcho2(ctx context.Context, id string, num string, localVarOptionals *UnannotatedEchoServiceEcho2Opts) (ExamplepbUnannotatedSimpleMessage, *http.Response, error) {
+	var (
+		localVarHttpMethod = strings.ToUpper("Get")
+		localVarPostBody   interface{}
+		localVarFileName   string
+		localVarFileBytes  []byte
+		localVarReturnValue ExamplepbUnannotatedSimpleMessage
+	)
+
+	// create path and map variables
+	localVarPath := a.client.cfg.BasePath + "/v1/example/echo/{id}/{num}"
+	localVarPath = strings.Replace(localVarPath, "{"+"id"+"}", fmt.Sprintf("%v", id), -1)
+	localVarPath = strings.Replace(localVarPath, "{"+"num"+"}", fmt.Sprintf("%v", num), -1)
+
+	localVarHeaderParams := make(map[string]string)
+	localVarQueryParams := url.Values{}
+	localVarFormParams := url.Values{}
+
+	if localVarOptionals != nil && localVarOptionals.Duration.IsSet() {
+		localVarQueryParams.Add("duration", parameterToString(localVarOptionals.Duration.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.LineNum.IsSet() {
+		localVarQueryParams.Add("lineNum", parameterToString(localVarOptionals.LineNum.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.Lang.IsSet() {
+		localVarQueryParams.Add("lang", parameterToString(localVarOptionals.Lang.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.StatusProgress.IsSet() {
+		localVarQueryParams.Add("status.progress", parameterToString(localVarOptionals.StatusProgress.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.StatusNote.IsSet() {
+		localVarQueryParams.Add("status.note", parameterToString(localVarOptionals.StatusNote.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.En.IsSet() {
+		localVarQueryParams.Add("en", parameterToString(localVarOptionals.En.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.NoProgress.IsSet() {
+		localVarQueryParams.Add("no.progress", parameterToString(localVarOptionals.NoProgress.Value(), ""))
+	}
+	if localVarOptionals != nil && localVarOptionals.NoNote.IsSet() {
+		localVarQueryParams.Add("no.note", parameterToString(localVarOptionals.NoNote.Value(), ""))
+	}
+	// to determine the Content-Type header
+	localVarHttpContentTypes := []string{"application/json", "application/x-foo-mime"}
+
+	// set Content-Type header
+	localVarHttpContentType := selectHeaderContentType(localVarHttpContentTypes)
+	if localVarHttpContentType != "" {
+		localVarHeaderParams["Content-Type"] = localVarHttpContentType
+	}
+
+	// to determine the Accept header
+	localVarHttpHeaderAccepts := []string{"application/json", "application/x-foo-mime"}
+
+	// set Accept header
+	localVarHttpHeaderAccept := selectHeaderAccept(localVarHttpHeaderAccepts)
+	if localVarHttpHeaderAccept != "" {
+		localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
+	}
+	if ctx != nil {
+		// API Key Authentication
+		if auth, ok := ctx.Value(ContextAPIKey).(APIKey); ok {
+			var key string
+			if auth.Prefix != "" {
+				key = auth.Prefix + " " + auth.Key
+			} else {
+				key = auth.Key
+			}
+			localVarHeaderParams["X-API-Key"] = key
+			
+		}
+	}
+	r, err := a.client.prepareRequest(ctx, localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
+	if err != nil {
+		return localVarReturnValue, nil, err
+	}
+
+	localVarHttpResponse, err := a.client.callAPI(r)
+	if err != nil || localVarHttpResponse == nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	localVarBody, err := ioutil.ReadAll(localVarHttpResponse.Body)
+	localVarHttpResponse.Body.Close()
+	if err != nil {
+		return localVarReturnValue, localVarHttpResponse, err
+	}
+
+	if localVarHttpResponse.StatusCode < 300 {
+		// If we succeed, return the data, otherwise pass on to decode error.
+		err = a.client.decode(&localVarReturnValue, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+		if err == nil { 
+			return localVarReturnValue, localVarHttpResponse, err
+		}
+	}
+
+	if localVarHttpResponse.StatusCode >= 300 {
+		newErr := GenericSwaggerError{
+			body: localVarBody,
+			error: localVarHttpResponse.Status,
+		}
+		
+		if localVarHttpResponse.StatusCode == 200 {
+			var v ExamplepbUnannotatedSimpleMessage
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 403 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 404 {
+			var v int32
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 418 {
+			var v ExamplepbNumericEnum
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 503 {
+			var v interface{}
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		if localVarHttpResponse.StatusCode == 0 {
+			var v RpcStatus
+			err = a.client.decode(&v, localVarBody, localVarHttpResponse.Header.Get("Content-Type"));
+				if err != nil {
+					newErr.error = err.Error()
+					return localVarReturnValue, localVarHttpResponse, newErr
+				}
+				newErr.model = v
+				return localVarReturnValue, localVarHttpResponse, newErr
+		}
+		
+		return localVarReturnValue, localVarHttpResponse, newErr
+	}
+
+	return localVarReturnValue, localVarHttpResponse, nil
+}
+
 /* 
 UnannotatedEchoServiceApiService EchoBody method receives a simple message and returns it.
  * @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
diff --git a/examples/internal/clients/unannotatedecho/client.go b/examples/internal/clients/unannotatedecho/client.go
index 7c7fb6c03eb..80ae2022984 100644
--- a/examples/internal/clients/unannotatedecho/client.go
+++ b/examples/internal/clients/unannotatedecho/client.go
@@ -46,8 +46,6 @@ type APIClient struct {
 
 	// API Services
 
-	EchoRpcApi *EchoRpcApiService
-
 	UnannotatedEchoServiceApi *UnannotatedEchoServiceApiService
 }
 
@@ -67,7 +65,6 @@ func NewAPIClient(cfg *Configuration) *APIClient {
 	c.common.client = c
 
 	// API Services
-	c.EchoRpcApi = (*EchoRpcApiService)(&c.common)
 	c.UnannotatedEchoServiceApi = (*UnannotatedEchoServiceApiService)(&c.common)
 
 	return c
diff --git a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
index 8bafa922d6a..acb67777798 100644
--- a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
+++ b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.json
@@ -85,7 +85,7 @@
           }
         ],
         "tags": [
-          "echo rpc"
+          "UnannotatedEchoService"
         ],
         "externalDocs": {
           "description": "Find out more Echo",
@@ -210,7 +210,7 @@
           }
         ],
         "tags": [
-          "echo rpc"
+          "UnannotatedEchoService"
         ],
         "externalDocs": {
           "description": "Find out more Echo",
diff --git a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
index 91b19169c71..9d7bc39b8cb 100644
--- a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
+++ b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
@@ -81,7 +81,6 @@ openapiOptions:
       option:
         description: "Description Echo"
         summary: "Summary: Echo rpc"
-        tags: ["echo rpc"]
         external_docs:
           url: "https://github.com/grpc-ecosystem/grpc-gateway"
           description: "Find out more Echo"

From 1b7e6ad28b9d2b3ddb935fcc2badf8ebe43ec481 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Tue, 15 Sep 2020 10:31:47 -0700
Subject: [PATCH 13/17] Fix Bazel rule after rebasing

---
 internal/descriptor/BUILD.bazel | 2 --
 1 file changed, 2 deletions(-)

diff --git a/internal/descriptor/BUILD.bazel b/internal/descriptor/BUILD.bazel
index ceb98344b2f..abc245ceec2 100644
--- a/internal/descriptor/BUILD.bazel
+++ b/internal/descriptor/BUILD.bazel
@@ -43,8 +43,6 @@ go_test(
         "//internal/descriptor/openapiconfig:go_default_library",
         "//internal/httprule:go_default_library",
         "//protoc-gen-openapiv2/options:go_default_library",
-        "@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
-        "@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
         "@org_golang_google_protobuf//encoding/prototext:go_default_library",
         "@org_golang_google_protobuf//proto:go_default_library",
         "@org_golang_google_protobuf//types/descriptorpb:go_default_library",

From 5e2765723c7c2f696d34180aca119069caa7a77e Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Tue, 15 Sep 2020 11:12:51 -0700
Subject: [PATCH 14/17] Document using OpenAPI YAML annotations

---
 docs/_docs/grpcapiconfiguration.md            | 30 +++++++++++++++----
 .../unannotated_echo_service.swagger.yaml     |  1 +
 2 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/docs/_docs/grpcapiconfiguration.md b/docs/_docs/grpcapiconfiguration.md
index 0c0846aa777..9d3bdf80411 100644
--- a/docs/_docs/grpcapiconfiguration.md
+++ b/docs/_docs/grpcapiconfiguration.md
@@ -5,17 +5,19 @@ order: 100
 ---
 
 # gRPC API Configuration
-In some sitations annotating the .proto file of a service is not an option. For example you might not have control over the .proto file or you might want to expose the same gRPC API multiple times in completely different ways.
+In some situations annotating the .proto file of a service is not an option. For example you might not have control over the .proto file or you might want to expose the same gRPC API multiple times in completely different ways.
 
 Google Cloud Platform offers a way to do this for services hosted with them called ["gRPC API Configuration"](https://cloud.google.com/endpoints/docs/grpc/grpc-service-config). It can be used to define the behavior of a gRPC API service without modifications to the service itself in the form of [YAML](https://en.wikipedia.org/wiki/YAML) configuration files.
 
 grpc-gateway generators implement the [HTTP rules part](https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#httprule) of this specification. This allows you to take a completely unannotated service proto file, add a YAML file describing its HTTP endpoints and use them together like a annotated proto file with the grpc-gateway generators.
 
+OpenAPI options may also be configured via ["OpenAPI Configuration"](internal/descriptor/openapiconfig/openapiconfig.proto) in the form of YAML configuration files.
+
 ## Usage of gRPC API Configuration YAML files
 The following is equivalent to the basic [usage example](usage.html) but without direct annotation for grpc-gateway in the .proto file. Only some steps require minor changes to use a gRPC API Configuration YAML file instead:
 
 1. Define your service in gRPC as usual
-   
+
     your_service.proto:
     ```protobuf
     syntax = "proto3";
@@ -24,7 +26,7 @@ The following is equivalent to the basic [usage example](usage.html) but without
     message StringMessage {
       string value = 1;
     }
-    
+
     service YourService {
       rpc Echo(StringMessage) returns (StringMessage) {}
     }
@@ -45,11 +47,11 @@ The following is equivalent to the basic [usage example](usage.html) but without
     Use a [linter](http://www.yamllint.com/) to validate your YAML.
 
 3. Generate gRPC stub as before
-   
+
     ```sh
     protoc -I. --go_out=plugins=grpc,paths=source_relative:./gen/go/ your/service/v1/your_service.proto
     ```
-   
+
   It will generate a stub file with path `./gen/go/your/service/v1/your_service.pb.go`.
 
 4. Implement your service in gRPC as usual
@@ -61,7 +63,23 @@ The following is equivalent to the basic [usage example](usage.html) but without
     protoc -I. --grpc-gateway_out=logtostderr=true,paths=source_relative,grpc_api_configuration=path/to/your_service.yaml:./gen/go \
       your/service/v1/your_service.proto
     ```
-   
+
    This will generate a reverse proxy `gen/go/your/service/v1/your_service.pb.gw.go` that is identical to the one produced for the annotated proto.
 
+6. Generate the optional your_service.swagger.json
+
+    ```sh
+    protoc -I. --swagger_out=grpc_api_configuration=path/to/your_service.yaml:./gen/go \
+      your/service/v1/your_service.proto
+    ```
+
+    or using an OpenAPI config file
+
+    ```sh
+    protoc -I. --swagger_out=grpc_api_configuration=path/to/your_service.yaml,openapi_configuration=path/to/your_service_swagger.yaml:./gen/go \
+      your/service/v1/your_service.proto
+    ```
+
+    For an example of an OpenAPI configuration file, see [unannotated_echo_service.swagger.yaml](examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml), which adds OpenAPI options to [unannotated_echo_service.proto](examples/internal/proto/examplepb/unannotated_echo_service.proto).
+
 All other steps work as before. If you want you can remove the googleapis include path in step 3 and 4 as the unannotated proto no longer requires them.
diff --git a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
index 9d7bc39b8cb..125de1d5a75 100644
--- a/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
+++ b/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml
@@ -1,5 +1,6 @@
 openapiOptions:
   file:
+    # the file name must be the same as one passed to protoc when generating generating .swagger.json
     - file: "examples/internal/proto/examplepb/unannotated_echo_service.proto"
       option:
         info:

From 4e7db4820385be33afd9ed3dc68efe0346458367 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Tue, 15 Sep 2020 11:23:53 -0700
Subject: [PATCH 15/17] Fix grpcapiconfiguration.md links

---
 docs/_docs/grpcapiconfiguration.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/_docs/grpcapiconfiguration.md b/docs/_docs/grpcapiconfiguration.md
index 9d3bdf80411..f8f9e069908 100644
--- a/docs/_docs/grpcapiconfiguration.md
+++ b/docs/_docs/grpcapiconfiguration.md
@@ -11,7 +11,7 @@ Google Cloud Platform offers a way to do this for services hosted with them call
 
 grpc-gateway generators implement the [HTTP rules part](https://cloud.google.com/endpoints/docs/grpc-service-config/reference/rpc/google.api#httprule) of this specification. This allows you to take a completely unannotated service proto file, add a YAML file describing its HTTP endpoints and use them together like a annotated proto file with the grpc-gateway generators.
 
-OpenAPI options may also be configured via ["OpenAPI Configuration"](internal/descriptor/openapiconfig/openapiconfig.proto) in the form of YAML configuration files.
+OpenAPI options may also be configured via ["OpenAPI Configuration"](https://github.com/grpc-ecosystem/grpc-gateway/tree/v2/internal/descriptor/openapiconfig/openapiconfig.proto) in the form of YAML configuration files.
 
 ## Usage of gRPC API Configuration YAML files
 The following is equivalent to the basic [usage example](usage.html) but without direct annotation for grpc-gateway in the .proto file. Only some steps require minor changes to use a gRPC API Configuration YAML file instead:
@@ -73,13 +73,13 @@ The following is equivalent to the basic [usage example](usage.html) but without
       your/service/v1/your_service.proto
     ```
 
-    or using an OpenAPI config file
+    or using an OpenAPI configuration file
 
     ```sh
     protoc -I. --swagger_out=grpc_api_configuration=path/to/your_service.yaml,openapi_configuration=path/to/your_service_swagger.yaml:./gen/go \
       your/service/v1/your_service.proto
     ```
 
-    For an example of an OpenAPI configuration file, see [unannotated_echo_service.swagger.yaml](examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml), which adds OpenAPI options to [unannotated_echo_service.proto](examples/internal/proto/examplepb/unannotated_echo_service.proto).
+    For an example of an OpenAPI configuration file, see [unannotated_echo_service.swagger.yaml](https://github.com/grpc-ecosystem/grpc-gateway/tree/v2/examples/internal/proto/examplepb/unannotated_echo_service.swagger.yaml), which adds OpenAPI options to [unannotated_echo_service.proto](https://github.com/grpc-ecosystem/grpc-gateway/tree/v2/examples/internal/proto/examplepb/unannotated_echo_service.proto).
 
 All other steps work as before. If you want you can remove the googleapis include path in step 3 and 4 as the unannotated proto no longer requires them.

From 4b7e202fc7b0c0cdcc6200ea5548a3df2123bc8b Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Wed, 16 Sep 2020 10:25:55 -0700
Subject: [PATCH 16/17] Update docs/_docs/grpcapiconfiguration.md

Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
---
 docs/_docs/grpcapiconfiguration.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/_docs/grpcapiconfiguration.md b/docs/_docs/grpcapiconfiguration.md
index f8f9e069908..2b2c369c15e 100644
--- a/docs/_docs/grpcapiconfiguration.md
+++ b/docs/_docs/grpcapiconfiguration.md
@@ -69,7 +69,8 @@ The following is equivalent to the basic [usage example](usage.html) but without
 6. Generate the optional your_service.swagger.json
 
     ```sh
-    protoc -I. --swagger_out=grpc_api_configuration=path/to/your_service.yaml:./gen/go \
+    protoc -I . --swagger_out ./gen/go \
+      --swagger_opt grpc_api_configuration=path/to/your_service.yaml \
       your/service/v1/your_service.proto
     ```
 

From 425820877a1c7696a9cbd4703305d5c9c491c978 Mon Sep 17 00:00:00 2001
From: Jason Wang <jwang@arista.com>
Date: Wed, 16 Sep 2020 10:26:01 -0700
Subject: [PATCH 17/17] Update docs/_docs/grpcapiconfiguration.md

Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
---
 docs/_docs/grpcapiconfiguration.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/docs/_docs/grpcapiconfiguration.md b/docs/_docs/grpcapiconfiguration.md
index 2b2c369c15e..72aaa7c8074 100644
--- a/docs/_docs/grpcapiconfiguration.md
+++ b/docs/_docs/grpcapiconfiguration.md
@@ -77,7 +77,9 @@ The following is equivalent to the basic [usage example](usage.html) but without
     or using an OpenAPI configuration file
 
     ```sh
-    protoc -I. --swagger_out=grpc_api_configuration=path/to/your_service.yaml,openapi_configuration=path/to/your_service_swagger.yaml:./gen/go \
+    protoc -I . --swagger_out ./gen/go \
+      --swagger_opt grpc_api_configuration=path/to/your_service.yaml \
+      --swagger_opt openapi_configuration=path/to/your_service_swagger.yaml \
       your/service/v1/your_service.proto
     ```