diff --git a/examples/examplepb/echo_service.proto b/examples/examplepb/echo_service.proto index 5e44b2827c6..83ebe704ed3 100644 --- a/examples/examplepb/echo_service.proto +++ b/examples/examplepb/echo_service.proto @@ -4,16 +4,21 @@ package gengo.grpc.gateway.examples.examplepb; import "google/api/annotations.proto"; +// SimpleMessage represents a simple message sent to the Echo service. message SimpleMessage { + // Id represents the message identifier. string id = 1; } +// Echo service responds to incoming echo requests. service EchoService { + // Echo method receives a simple message and returns it. rpc Echo(SimpleMessage) returns (SimpleMessage) { option (google.api.http) = { post: "/v1/example/echo/{id}" }; } + // EchoBody method receives a simple message and returns it. rpc EchoBody(SimpleMessage) returns (SimpleMessage) { option (google.api.http) = { post: "/v1/example/echo_body" diff --git a/examples/examplepb/echo_service.swagger.json b/examples/examplepb/echo_service.swagger.json index 76a905721d6..3db1c964c7c 100644 --- a/examples/examplepb/echo_service.swagger.json +++ b/examples/examplepb/echo_service.swagger.json @@ -18,6 +18,7 @@ "/v1/example/echo/{id}": { "post": { "summary": "EchoService.Echo", + "description": "Echo method receives a simple message and returns it.", "operationId": "Echo", "responses": { "default": { @@ -44,6 +45,7 @@ "/v1/example/echo_body": { "post": { "summary": "EchoService.EchoBody", + "description": "EchoBody method receives a simple message and returns it.", "operationId": "EchoBody", "responses": { "default": { diff --git a/protoc-gen-swagger/genswagger/template.go b/protoc-gen-swagger/genswagger/template.go index 26a272cd34e..ef2075e4730 100644 --- a/protoc-gen-swagger/genswagger/template.go +++ b/protoc-gen-swagger/genswagger/template.go @@ -6,6 +6,7 @@ import ( "fmt" "reflect" "regexp" + "strconv" "strings" "github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor" @@ -304,8 +305,9 @@ func templateToSwaggerPath(path string) string { } func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry) error { - for _, svc := range services { - for _, meth := range svc.Methods { + // Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array. + for svcIdx, svc := range services { + for methIdx, meth := range svc.Methods { if meth.GetClientStreaming() || meth.GetServerStreaming() { return fmt.Errorf(`service uses streaming, which is not currently supported. Maybe you would like to implement it? It wouldn't be that hard and we don't bite. Why don't you send a pull request to https://github.com/gengo/grpc-gateway?`) } @@ -415,8 +417,41 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re if !ok { pathItemObject = swaggerPathItemObject{} } + + // TODO(ivucica): make this a module-level function and use elsewhere. + protoPath := func(descriptorType reflect.Type, what string) int32 { + // TODO(ivucica): handle errors obtaining any of the following. + field, ok := descriptorType.Elem().FieldByName(what) + if !ok { + // TODO(ivucica): consider being more graceful. + panic(fmt.Errorf("Could not find type id for %s.", what)) + } + pbtag := field.Tag.Get("protobuf") + if pbtag == "" { + // TODO(ivucica): consider being more graceful. + panic(fmt.Errorf("No protobuf tag on %s.", what)) + } + // TODO(ivucica): handle error + path, _ := strconv.Atoi(strings.Split(pbtag, ",")[1]) + + return int32(path) + } + methDescription := "" + for _, loc := range svc.File.SourceCodeInfo.Location { + if len(loc.Path) < 4 { + continue + } + if loc.Path[0] == protoPath(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Service") && loc.Path[1] == int32(svcIdx) && loc.Path[2] == protoPath(reflect.TypeOf((*pbdescriptor.ServiceDescriptorProto)(nil)), "Method") && loc.Path[3] == int32(methIdx) { + if loc.LeadingComments != nil { + methDescription = strings.TrimRight(*loc.LeadingComments, "\n") + methDescription = strings.TrimLeft(methDescription, " ") + } + break + } + } operationObject := &swaggerOperationObject{ Summary: fmt.Sprintf("%s.%s", svc.GetName(), meth.GetName()), + Description: methDescription, Tags: []string{svc.GetName()}, OperationId: fmt.Sprintf("%s", meth.GetName()), Parameters: parameters, diff --git a/protoc-gen-swagger/genswagger/types.go b/protoc-gen-swagger/genswagger/types.go index fd441683b1b..a3844420342 100644 --- a/protoc-gen-swagger/genswagger/types.go +++ b/protoc-gen-swagger/genswagger/types.go @@ -46,6 +46,7 @@ type swaggerPathItemObject struct { // http://swagger.io/specification/#operationObject type swaggerOperationObject struct { Summary string `json:"summary"` + Description string `json:"description,omitempty"` OperationId string `json:"operationId"` Responses swaggerResponsesObject `json:"responses"` Parameters swaggerParametersObject `json:"parameters,omitempty"`