From 6d157cc8acdf2e86fdafc5e33fb3a28ecb4cb9ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Vu=C4=8Dica?= Date: Sat, 7 May 2016 23:38:36 +0100 Subject: [PATCH] Swagger: Replaced '' for raw JSON with last paragraph 'Swagger: '. Per discussion in gengo/grpc-gateway#134. --- examples/examplepb/echo_service.pb.go | 16 ++---- examples/examplepb/echo_service.proto | 12 ++--- protoc-gen-swagger/genswagger/template.go | 63 ++++++++--------------- 3 files changed, 28 insertions(+), 63 deletions(-) diff --git a/examples/examplepb/echo_service.pb.go b/examples/examplepb/echo_service.pb.go index d191846eaec..54eb9da7ddd 100644 --- a/examples/examplepb/echo_service.pb.go +++ b/examples/examplepb/echo_service.pb.go @@ -10,8 +10,7 @@ Echo Service Echo Service API consists of a single service which returns a message. - It is generated from these files: examples/examplepb/echo_service.proto @@ -67,14 +65,12 @@ const _ = proto.ProtoPackageIsVersion1 // SimpleMessage represents a simple message sent to the Echo service. // -// type SimpleMessage struct { // Id represents the message identifier. Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` @@ -105,14 +101,12 @@ type EchoServiceClient interface { // The message posted as the id parameter will also be // returned. // - // Echo(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) // EchoBody method receives a simple message and returns it. EchoBody(ctx context.Context, in *SimpleMessage, opts ...grpc.CallOption) (*SimpleMessage, error) @@ -152,14 +146,12 @@ type EchoServiceServer interface { // The message posted as the id parameter will also be // returned. // - // Echo(context.Context, *SimpleMessage) (*SimpleMessage, error) // EchoBody method receives a simple message and returns it. EchoBody(context.Context, *SimpleMessage) (*SimpleMessage, error) diff --git a/examples/examplepb/echo_service.proto b/examples/examplepb/echo_service.proto index 4baeddad06c..3da0087eecb 100644 --- a/examples/examplepb/echo_service.proto +++ b/examples/examplepb/echo_service.proto @@ -6,8 +6,7 @@ option go_package = "examplepb"; // Echo Service API consists of a single service which returns // a message. // -// 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; @@ -50,14 +46,12 @@ service EchoService { // The message posted as the id parameter will also be // returned. // - // rpc Echo(SimpleMessage) returns (SimpleMessage) { option (google.api.http) = { post: "/v1/example/echo/{id}" diff --git a/protoc-gen-swagger/genswagger/template.go b/protoc-gen-swagger/genswagger/template.go index 538376cd6de..76b9a33bdee 100644 --- a/protoc-gen-swagger/genswagger/template.go +++ b/protoc-gen-swagger/genswagger/template.go @@ -13,8 +13,6 @@ import ( pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" ) -var swaggerExtrasRegexp = regexp.MustCompile(`(?s)^(.*[^\s])[\s]*[\s]*(.*)$`) - // findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service. func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, e enumMap) { for _, svc := range s { @@ -541,55 +539,27 @@ func applyTemplate(p param) (string, error) { // updateSwaggerDataFromComments updates a Swagger object based on a comment // from the proto file. // -// As a first step, a section matching: -// -// -// -// where .* contains valid JSON will be stored for later processing, and then -// removed from the passed string. -// (Implementation note: Currently, the JSON gets immediately applied and -// thus cannot override summary and description.) -// -// First paragraph of a comment is used for summary. Remaining paragraphs of a -// comment are used for description. If 'Summary' field is not present on the -// passed swaggerObject, the summary and description are joined by \n\n. +// First paragraph of a comment is used for summary. Remaining paragraphs of +// a comment are used for description. If 'Summary' field is not present on +// the passed swaggerObject, the summary and description are joined by \n\n. // // If there is a field named 'Info', its 'Summary' and 'Description' fields // will be updated instead. (JSON always gets applied directly to the passed -// object.) +// object, never to 'Info'.) // // If there is no 'Summary', the same behavior will be attempted on 'Title', // but only if the last character is not a period. // -// To apply additional Swagger properties, one can pass valid JSON as described -// before. This JSON gets parsed and applied to the passed swaggerObject -// directly. This lets users easily apply custom properties such as contact -// details, API base path, et al. +// To apply additional Swagger properties, one can pass valid JSON +// in the last paragraph of the comment. The last paragraph needs to start +// with the string 'Swagger: '. This JSON gets parsed and applied to +// the passed swaggerObject directly. This lets developers easily apply +// custom properties such as contact details, API base path, et al. func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) error { if len(comment) == 0 { return nil } - // Find a section containing additional Swagger metadata. - matches := swaggerExtrasRegexp.FindStringSubmatch(comment) - - if len(matches) > 0 { - // If found, before further processing, replace the - // comment with a version that does not contain the - // extras. - comment = matches[1] - if len(matches[3]) > 0 { - comment += "\n\n" + matches[3] - } - - // Parse the JSON and apply it. - // TODO(ivucica): apply extras /after/ applying summary - // and description. - if err := json.Unmarshal([]byte(matches[2]), swaggerObject); err != nil { - return fmt.Errorf("error: %s, parsing: %s", err.Error(), matches[2]) - } - } - // Figure out what to apply changes to. swaggerObjectValue := reflect.ValueOf(swaggerObject) infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info") @@ -608,11 +578,20 @@ func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) er usingTitle = true } + // Parse the JSON and apply it. + // TODO(ivucica): apply extras /after/ applying summary + // and description. + paragraphs := strings.Split(comment, "\n\n") + if len(paragraphs) > 0 && strings.HasPrefix(strings.TrimLeft(paragraphs[len(paragraphs)-1], " "), "Swagger: ") { + if err := json.Unmarshal([]byte(strings.TrimLeft(paragraphs[len(paragraphs)-1], " "))[len("Swagger: "):], swaggerObject); err != nil { + return fmt.Errorf("error: %s, parsing: %s", err.Error(), paragraphs[len(paragraphs)-1]) + } + paragraphs = paragraphs[:len(paragraphs)-1] + } + // If there is a summary (or summary-equivalent), use the first // paragraph as summary, and the rest as description. if summaryValue.CanSet() { - paragraphs := strings.Split(comment, "\n\n") - summary := strings.TrimSpace(paragraphs[0]) description := strings.TrimSpace(strings.Join(paragraphs[1:], "\n\n")) if !usingTitle || summary[len(summary)-1] != '.' { @@ -632,7 +611,7 @@ func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) er // There was no summary field on the swaggerObject. Try to apply the // whole comment into description. if descriptionValue.CanSet() { - descriptionValue.Set(reflect.ValueOf(comment)) + descriptionValue.Set(reflect.ValueOf(strings.Join(paragraphs, "\n\n"))) return nil }