diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb19b68..4182c04 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: - name: checkout uses: actions/checkout@v2 - name: lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 continue-on-error: true with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 545baf2..f807f24 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -33,7 +33,7 @@ jobs: - name: checkout uses: actions/checkout@v2 - name: lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 continue-on-error: true with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. diff --git a/go.mod b/go.mod index 993255b..e415ad8 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/fatih/structtag v1.2.0 - go.unistack.org/micro-proto/v3 v3.2.3 + go.unistack.org/micro-proto/v3 v3.2.5 golang.org/x/tools v0.1.9 google.golang.org/protobuf v1.27.1 ) diff --git a/go.sum b/go.sum index 5cbb1e5..a10a838 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1: github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.unistack.org/micro-proto/v3 v3.2.3 h1:vSRI6VoZrlv0pUdo69irHv6HbbnD+oZOGEUE/TS5XBQ= -go.unistack.org/micro-proto/v3 v3.2.3/go.mod h1:ZltVWNECD5yK+40+OCONzGw4OtmSdTpVi8/KFgo9dqM= +go.unistack.org/micro-proto/v3 v3.2.5 h1:qaMr9oaO8J2X9nvcZfQ+JriuEFfoMzRvFfQdKoVlWgU= +go.unistack.org/micro-proto/v3 v3.2.5/go.mod h1:ZltVWNECD5yK+40+OCONzGw4OtmSdTpVi8/KFgo9dqM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= diff --git a/openapiv3.go b/openapiv3.go index f0dffb4..00a3ed8 100644 --- a/openapiv3.go +++ b/openapiv3.go @@ -22,13 +22,14 @@ import ( "regexp" "sort" "strings" + "sync" "go.unistack.org/micro-proto/v3/api" - // v2 "go.unistack.org/micro-proto/v3/openapiv2" v3 "go.unistack.org/micro-proto/v3/openapiv3" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/reflect/protoregistry" ) const ( @@ -47,6 +48,19 @@ type openapiv3Generator struct { namedPathPattern *regexp.Regexp } +var ( + once sync.Once + protofiles = &protoregistry.Files{} +) + +func protofilesAdd(plugin *protogen.Plugin) { + for path, f := range plugin.FilesByPath { + if _, err := protofiles.FindFileByPath(path); err != nil { + protofiles.RegisterFile(f.Desc) + } + } +} + // openapiv3Generate creates a new generator for a protoc plugin invocation. func (g *Generator) openapiv3Generate(component string, plugin *protogen.Plugin) error { og := &openapiv3Generator{ @@ -61,6 +75,8 @@ func (g *Generator) openapiv3Generate(component string, plugin *protogen.Plugin) namedPathPattern: regexp.MustCompile("{(.+)=(.+)}"), } + protofilesAdd(plugin) + d := og.buildDocumentV3(plugin) bytes, err := d.YAMLValue("Generated with protoc-gen-go-micro\n") if err != nil { @@ -245,14 +261,6 @@ func (g *openapiv3Generator) addPathsToDocumentV3(d *v3.Document, file *protogen outputMessage := method.Output operationID := service.GoName + "_" + method.GoName - /* - e2opt := proto.GetExtension(method.Desc.Options(), v2.E_Openapiv2Operation) - if e2opt != nil && e2opt != v2.E_Openapiv2Operation.InterfaceOf(v2.E_Openapiv2Operation.Zero()) { - if opt, ok := e2opt.(*v2.Operation); ok && opt.OperationId != "" { - operationID = opt.OperationId - } - } - */ e3opt := proto.GetExtension(method.Desc.Options(), v3.E_Openapiv3Operation) if e3opt != nil && e3opt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) { if opt, ok := e3opt.(*v3.Operation); ok && opt.OperationId != "" { @@ -640,22 +648,71 @@ func (g *openapiv3Generator) buildOperationV3( } } - // Create the response. - responses := &v3.Responses{ - ResponseOrReference: []*v3.NamedResponseOrReference{ - { - Name: "200", - Value: &v3.ResponseOrReference{ - Oneof: &v3.ResponseOrReference_Response{ - Response: &v3.Response{ - Description: "OK", - Content: g.responseContentForMessage(outputMessage), - }, + var responses *v3.Responses + if eopt != nil && eopt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) { + opt := eopt.(*v3.Operation) + if r := opt.Responses; r != nil { + responses = r + } + + if ref := responses.Default.GetReference(); ref != nil && ref.GetXRef() != "" { + xref := strings.TrimPrefix(ref.GetXRef(), ".") + description := "Default" + if strings.Contains(xref, "micro.errors.Error") { + description += " Error" + } + desc, err := protofiles.FindDescriptorByName(protoreflect.FullName(xref)) + if err != nil { + log.Printf("unknown ref type %s err %v", xref, err) + } else { + responses.Default.Oneof = &v3.ResponseOrReference_Response{ + Response: &v3.Response{ + Description: description, + Content: g.responseContentForMessage(&protogen.Message{ + Desc: desc.(protoreflect.MessageDescriptor), + }), }, + } + } + } + for _, rref := range responses.GetResponseOrReference() { + if ref := rref.Value.GetReference(); ref != nil && ref.GetXRef() != "" { + xref := strings.TrimPrefix(ref.GetXRef(), ".") + description := "Default" + if strings.Contains(xref, "micro.errors.Error") { + description += " Error" + } + desc, err := protofiles.FindDescriptorByName(protoreflect.FullName(xref)) + if err != nil { + log.Printf("unknown ref type %s err %v", xref, err) + } else { + responses.Default.Oneof = &v3.ResponseOrReference_Response{ + Response: &v3.Response{ + Description: description, + Content: g.responseContentForMessage(&protogen.Message{ + Desc: desc.(protoreflect.MessageDescriptor), + }), + }, + } + } + } + } + } else { + responses = &v3.Responses{} + } + + // Create the response. + responses.ResponseOrReference = append(responses.ResponseOrReference, &v3.NamedResponseOrReference{ + Name: "200", + Value: &v3.ResponseOrReference{ + Oneof: &v3.ResponseOrReference_Response{ + Response: &v3.Response{ + Description: "OK", + Content: g.responseContentForMessage(outputMessage), }, }, }, - } + }) // Create the operation. op := &v3.Operation{ @@ -682,7 +739,6 @@ func (g *openapiv3Generator) buildOperationV3( // Pass the entire request message as the request body. typeName := g.fullMessageTypeName(inputMessage.Desc) requestSchema = g.schemaOrReferenceForType(typeName) - } else { // If body refers to a message field, use that type. for _, field := range inputMessage.Fields { diff --git a/variables.go b/variables.go index 0cf425b..05d4a01 100644 --- a/variables.go +++ b/variables.go @@ -16,8 +16,8 @@ var ( microServerPackage = protogen.GoImportPath("go.unistack.org/micro/v3/server") microClientHttpPackage = protogen.GoImportPath("go.unistack.org/micro-client-http/v3") microServerHttpPackage = protogen.GoImportPath("go.unistack.org/micro-server-http/v3") - microCodecPackage = protogen.GoImportPath("go.unistack.org/micro/v3/codec") - microErrorsPackage = protogen.GoImportPath("go.unistack.org/micro/v3/errors") + microCodecPackage = protogen.GoImportPath("go.unistack.org/micro-proto/v3/codec") + microErrorsPackage = protogen.GoImportPath("go.unistack.org/micro-proto/v3/errors") timePackage = protogen.GoImportPath("time") deprecationComment = "// Deprecated: Do not use." versionComment = "v3.5.3"