| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 | // Copyright 2020 Google LLC. All Rights Reserved.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at////    http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.//package generatorimport (	"log"	"strings"	"google.golang.org/protobuf/reflect/protoreflect"	wk "git.ikuban.com/server/swagger-api/v2/generator/wellknown"	v3 "github.com/google/gnostic/openapiv3")const (	protobufValueName = "GoogleProtobufValue"	protobufAnyName   = "GoogleProtobufAny")type OpenAPIv3Reflector struct {	conf Configuration	requiredSchemas []string // Names of schemas which are used through references.}// NewOpenAPIv3Reflector creates a new reflector.func NewOpenAPIv3Reflector(conf Configuration) *OpenAPIv3Reflector {	return &OpenAPIv3Reflector{		conf: conf,		requiredSchemas: make([]string, 0),	}}func (r *OpenAPIv3Reflector) getMessageName(message protoreflect.MessageDescriptor) string {	prefix := ""	parent := message.Parent()	if _, ok := parent.(protoreflect.MessageDescriptor); ok {		prefix = string(parent.Name()) + "_" + prefix	}	return prefix + string(message.Name())}func (r *OpenAPIv3Reflector) formatMessageName(message protoreflect.MessageDescriptor) string {	typeName := r.fullMessageTypeName(message)	name := r.getMessageName(message)	if !*r.conf.FQSchemaNaming {		if typeName == ".google.protobuf.Value" {			name = protobufValueName		} else if typeName == ".google.protobuf.Any" {			name = protobufAnyName		}	}	if *r.conf.Naming == "json" {		if len(name) > 1 {			name = strings.ToUpper(name[0:1]) + name[1:]		}		if len(name) == 1 {			name = strings.ToLower(name)		}	}	if *r.conf.FQSchemaNaming {		package_name := string(message.ParentFile().Package())		name = package_name + "." + name	}	return name}func (r *OpenAPIv3Reflector) formatFieldName(field protoreflect.FieldDescriptor) string {	if *r.conf.Naming == "proto" {		return string(field.Name())	}	return field.JSONName()}// fullMessageTypeName builds the full type name of a message.func (r *OpenAPIv3Reflector) fullMessageTypeName(message protoreflect.MessageDescriptor) string {	name := r.getMessageName(message)	return "." + string(message.ParentFile().Package()) + "." + name}func (r *OpenAPIv3Reflector) responseContentForMessage(message protoreflect.MessageDescriptor) (string, *v3.MediaTypes) {	typeName := r.fullMessageTypeName(message)	if typeName == ".google.protobuf.Empty" {		return "200", wk.NewApplicationJsonMediaType(r.schemaOrReferenceForMessage(message))	}	if typeName == ".google.api.HttpBody" {		return "200", wk.NewGoogleApiHttpBodyMediaType()	}	return "200", wk.NewApplicationJsonMediaType(r.schemaOrReferenceForMessage(message))}func (r *OpenAPIv3Reflector) schemaReferenceForMessage(message protoreflect.MessageDescriptor) string {	schemaName := r.formatMessageName(message)	if !contains(r.requiredSchemas, schemaName) {		r.requiredSchemas = append(r.requiredSchemas, schemaName)	}	return "#/components/schemas/" + schemaName}// Returns a full schema for simple types, and a schema reference for complex types that reference// the definition in `#/components/schemas/`func (r *OpenAPIv3Reflector) schemaOrReferenceForMessage(message protoreflect.MessageDescriptor) *v3.SchemaOrReference {	typeName := r.fullMessageTypeName(message)	switch typeName {	case ".google.api.HttpBody":		return wk.NewGoogleApiHttpBodySchema()	case ".google.protobuf.Timestamp":		return wk.NewGoogleProtobufTimestampSchema()	case ".google.protobuf.Duration":		return wk.NewGoogleProtobufDurationSchema()	case ".google.type.Date":		return wk.NewGoogleTypeDateSchema()	case ".google.type.DateTime":		return wk.NewGoogleTypeDateTimeSchema()	case ".google.protobuf.FieldMask":		return wk.NewGoogleProtobufFieldMaskSchema()	case ".google.protobuf.Struct":		return wk.NewGoogleProtobufStructSchema()	case ".google.protobuf.Empty":		// Empty is closer to JSON undefined than null, so ignore this field		return wk.NewGoogleProtobufStructSchema() //&v3.SchemaOrReference{Oneof: &v3.SchemaOrReference_Schema{Schema: &v3.Schema{Type: "null"}}}	case ".google.protobuf.BoolValue":		return wk.NewBooleanSchema()	case ".google.protobuf.BytesValue":		return wk.NewBytesSchema()	case ".google.protobuf.Int32Value", ".google.protobuf.UInt32Value", ".google.protobuf.Int64Value", ".google.protobuf.UInt64Value":		return wk.NewIntegerSchema(getValueKind(message))	case ".google.protobuf.StringValue":		return wk.NewStringSchema()	case ".google.protobuf.FloatValue", ".google.protobuf.DoubleValue":		return wk.NewNumberSchema(getValueKind(message))	default:		ref := r.schemaReferenceForMessage(message)		return &v3.SchemaOrReference{			Oneof: &v3.SchemaOrReference_Reference{				Reference: &v3.Reference{XRef: ref}}}	}}func (r *OpenAPIv3Reflector) schemaOrReferenceForField(field protoreflect.FieldDescriptor) *v3.SchemaOrReference {	var kindSchema *v3.SchemaOrReference	kind := field.Kind()	switch kind {	case protoreflect.MessageKind:		if field.IsMap() {			// This means the field is a map, for example:			//   map<string, value_type> map_field = 1;			//			// The map ends up getting converted into something like this:			//   message MapFieldEntry {			//     string key = 1;			//     value_type value = 2;			//   }			//			//   repeated MapFieldEntry map_field = N;			//			// So we need to find the `value` field in the `MapFieldEntry` message and			// then return a MapFieldEntry schema using the schema for the `value` field			return wk.NewGoogleProtobufMapFieldEntrySchema(r.schemaOrReferenceForField(field.MapValue()))		} else {			kindSchema = r.schemaOrReferenceForMessage(field.Message())		}	case protoreflect.StringKind:		kindSchema = wk.NewStringSchema()	case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Uint32Kind,		protoreflect.Sfixed32Kind, protoreflect.Fixed32Kind:		kindSchema = wk.NewIntegerSchema(kind.String())	case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Uint64Kind,		protoreflect.Sfixed64Kind, protoreflect.Fixed64Kind:		kindSchema = wk.NewIntegerSchema(kind.String())	case protoreflect.EnumKind:		kindSchema = wk.NewEnumSchema(*&r.conf.EnumType, field)	case protoreflect.BoolKind:		kindSchema = wk.NewBooleanSchema()	case protoreflect.FloatKind, protoreflect.DoubleKind:		kindSchema = wk.NewNumberSchema(kind.String())	case protoreflect.BytesKind:		kindSchema = wk.NewBytesSchema()	default:		log.Printf("(TODO) Unsupported field type: %+v", r.fullMessageTypeName(field.Message()))	}	if field.IsList() {		kindSchema = wk.NewListSchema(kindSchema)	}	return kindSchema}
 |