Skip to content

Commit

Permalink
Apply additional Swagger properties using JSON.
Browse files Browse the repository at this point in the history
Depends on PR #134. Reverts 562956f.
  • Loading branch information
ivucica committed Oct 27, 2017
1 parent 1fd8ba6 commit 6ff06d8
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 3 deletions.
46 changes: 46 additions & 0 deletions examples/examplepb/echo_service.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions examples/examplepb/echo_service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,39 @@ option go_package = "examplepb";
//
// Echo Service API consists of a single service which returns
// a message.
//
// <!-- swagger extras start
// {
// "info": {
// "title": "Echo Service",
// "version": "1.0",
// "contact": {
// "name": "gRPC-Gateway project",
// "url": "https://github.com/grpc-ecosystem/grpc-gateway",
// "email": "none@example.com"
// }
// },
// "host": "localhost",
// "externalDocs": {
// "url": "https://github.com/grpc-ecosystem/grpc-gateway",
// "description": "More about gRPC-Gateway"
// }
// }
// swagger extras end -->
package grpc.gateway.examples.examplepb;

import "google/api/annotations.proto";

// SimpleMessage represents a simple message sent to the Echo service.
//
// <!-- swagger extras start
// {
// "externalDocs": {
// "url": "http://github.com/grpc-ecosystem/grpc-gateway",
// "description": "Find out more about EchoService"
// }
// }
// swagger extras end -->
message SimpleMessage {
// Id represents the message identifier.
string id = 1;
Expand All @@ -22,6 +50,15 @@ service EchoService {
//
// The message posted as the id parameter will also be
// returned.
//
// <!-- swagger extras start
// {
// "externalDocs": {
// "url": "http://github.com/grpc-ecosystem/grpc-gateway",
// "description": "Find out more about EchoService"
// }
// }
// swagger extras end -->
rpc Echo(SimpleMessage) returns (SimpleMessage) {
option (google.api.http) = {
post: "/v1/example/echo/{id}"
Expand Down
14 changes: 12 additions & 2 deletions examples/examplepb/echo_service.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@
"info": {
"title": "Echo Service",
"description": "Echo 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/gengo/grpc-gateway",
"email": "none@example.com"
}
},
"host": "localhost",
"schemes": [
"http",
"https"
Expand Down Expand Up @@ -39,7 +45,11 @@
],
"tags": [
"EchoService"
]
],
"externalDocs": {
"description": "Find out more about EchoService",
"url": "http://github.com/gengo/grpc-gateway"
}
}
},
"/v1/example/echo/{id}/{num}": {
Expand Down
39 changes: 38 additions & 1 deletion protoc-gen-swagger/genswagger/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
)

var swaggerExtrasRegexp = regexp.MustCompile(`(?s)^(.*[^\s])[\s]*<!-- swagger extras start(.*)swagger extras end -->[\s]*(.*)$`)

func listEnumNames(enum *descriptor.Enum) (names []string) {
for _, value := range enum.GetValue() {
names = append(names, value.GetName())
Expand Down Expand Up @@ -631,20 +633,55 @@ 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:
//
// <!-- swagger extras start.*swagger extras end-->
//
// 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.
//
// If there is a field named 'Info', its 'Summary' and 'Description' fields
// will be updated instead.
// will be updated instead. (JSON always gets applied directly to the passed
// object.)
//
// 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.
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")
Expand Down

0 comments on commit 6ff06d8

Please sign in to comment.