Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: "fill attributes of swagger schema if provided for messages" #849

Merged
merged 7 commits into from
Jan 10, 2019
Merged
397 changes: 202 additions & 195 deletions examples/proto/examplepb/a_bit_of_everything.pb.go

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions examples/proto/examplepb/a_bit_of_everything.proto
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,14 @@ message ABitOfEverything {
url: "https://github.com/grpc-ecosystem/grpc-gateway";
description: "Find out more about ABitOfEverything";
}
example: { value: '{ "uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814" }' }
};

// Nested is nested type.
message Nested {
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = {
example: { value: '{ "ok": "TRUE" }' }
};
// name is nested field.
string name = 1;
uint32 amount = 2;
Expand Down Expand Up @@ -202,6 +206,9 @@ message ABitOfEverything {

// ABitOfEverythingRepeated is used to validate repeated path parameter functionality
message ABitOfEverythingRepeated {
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_schema) = {
example: { value: '{ "path_repeated_bool_value": [true, true, false, true], "path_repeated_int32_value": [1, 2, 3] }' }
};

// repeated values. they are comma-separated in path
repeated float path_repeated_float_value = 1;
Expand Down
19 changes: 19 additions & 0 deletions examples/proto/examplepb/a_bit_of_everything.swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,9 @@
"definitions": {
"ABitOfEverythingNested": {
"type": "object",
"example": {
"ok": "TRUE"
},
"properties": {
"name": {
"type": "string",
Expand Down Expand Up @@ -1429,6 +1432,9 @@
},
"examplepbABitOfEverything": {
"type": "object",
"example": {
"uuid": "0cf361e1-4b44-483d-a159-54dabdf7e814"
},
"properties": {
"single_nested": {
"$ref": "#/definitions/ABitOfEverythingNested"
Expand Down Expand Up @@ -1569,6 +1575,19 @@
},
"examplepbABitOfEverythingRepeated": {
"type": "object",
"example": {
"path_repeated_bool_value": [
true,
true,
false,
true
],
"path_repeated_int32_value": [
1,
2,
3
]
},
"properties": {
"path_repeated_float_value": {
"type": "array",
Expand Down
2 changes: 2 additions & 0 deletions protoc-gen-swagger/genswagger/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ go_test(
deps = [
"//protoc-gen-grpc-gateway/descriptor:go_default_library",
"//protoc-gen-grpc-gateway/httprule:go_default_library",
"//protoc-gen-swagger/options:go_default_library",
"@com_github_golang_protobuf//proto:go_default_library",
"@io_bazel_rules_go//proto/wkt:any_go_proto",
"@io_bazel_rules_go//proto/wkt:compiler_plugin_go_proto",
"@io_bazel_rules_go//proto/wkt:descriptor_go_proto",
],
Expand Down
6 changes: 2 additions & 4 deletions protoc-gen-swagger/genswagger/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,8 @@ func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descrip
}

func skipRenderingRef(refName string) bool {
if _, ok := wktSchemas[refName]; ok {
return true
}
return false
_, ok := wktSchemas[refName]
return ok
}

func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, reg *descriptor.Registry, customRefs refMap) {
Expand Down
192 changes: 192 additions & 0 deletions protoc-gen-swagger/genswagger/template_test.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package genswagger

import (
"encoding/json"
"fmt"
"reflect"
"testing"

"github.com/golang/protobuf/proto"
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
"github.com/golang/protobuf/ptypes/any"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
swagger_options "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"
)

func crossLinkFixture(f *descriptor.File) *descriptor.File {
Expand Down Expand Up @@ -1020,6 +1023,195 @@ func TestSchemaOfField(t *testing.T) {
}
}

func TestRenderMessagesAsDefinition(t *testing.T) {

tests := []struct {
descr string
msgDescs []*protodescriptor.DescriptorProto
schema map[string]swagger_options.Schema // per-message schema to add
defs swaggerDefinitionsObject
}{
{
descr: "no swagger options",
msgDescs: []*protodescriptor.DescriptorProto{
&protodescriptor.DescriptorProto{Name: proto.String("Message")},
},
schema: map[string]swagger_options.Schema{},
defs: map[string]swaggerSchemaObject{
"Message": swaggerSchemaObject{schemaCore: schemaCore{Type: "object"}},
},
},
{
descr: "example option",
msgDescs: []*protodescriptor.DescriptorProto{
&protodescriptor.DescriptorProto{Name: proto.String("Message")},
},
schema: map[string]swagger_options.Schema{
"Message": swagger_options.Schema{
Example: &any.Any{
TypeUrl: "this_isnt_used",
Value: []byte(`{"foo":"bar"}`),
},
},
},
defs: map[string]swaggerSchemaObject{
"Message": swaggerSchemaObject{schemaCore: schemaCore{
Type: "object",
Example: json.RawMessage(`{"foo":"bar"}`),
}},
},
},
{
descr: "example option with something non-json",
msgDescs: []*protodescriptor.DescriptorProto{
&protodescriptor.DescriptorProto{Name: proto.String("Message")},
},
schema: map[string]swagger_options.Schema{
"Message": swagger_options.Schema{
Example: &any.Any{
Value: []byte(`XXXX anything goes XXXX`),
},
},
},
defs: map[string]swaggerSchemaObject{
"Message": swaggerSchemaObject{schemaCore: schemaCore{
Type: "object",
Example: json.RawMessage(`XXXX anything goes XXXX`),
}},
},
},
{
descr: "external docs option",
msgDescs: []*protodescriptor.DescriptorProto{
&protodescriptor.DescriptorProto{Name: proto.String("Message")},
},
schema: map[string]swagger_options.Schema{
"Message": swagger_options.Schema{
ExternalDocs: &swagger_options.ExternalDocumentation{
Description: "glorious docs",
Url: "https://nada",
},
},
},
defs: map[string]swaggerSchemaObject{
"Message": swaggerSchemaObject{
schemaCore: schemaCore{
Type: "object",
},
ExternalDocs: &swaggerExternalDocumentationObject{
Description: "glorious docs",
URL: "https://nada",
},
},
},
},
{
descr: "JSONSchema options",
msgDescs: []*protodescriptor.DescriptorProto{
&protodescriptor.DescriptorProto{Name: proto.String("Message")},
},
schema: map[string]swagger_options.Schema{
"Message": swagger_options.Schema{
JsonSchema: &swagger_options.JSONSchema{
Title: "title",
Description: "desc",
MultipleOf: 100,
Maximum: 101,
ExclusiveMaximum: true,
Minimum: 1,
ExclusiveMinimum: true,
MaxLength: 10,
MinLength: 3,
Pattern: "[a-z]+",
MaxItems: 20,
MinItems: 2,
UniqueItems: true,
MaxProperties: 33,
MinProperties: 22,
Required: []string{"req"},
},
},
},
defs: map[string]swaggerSchemaObject{
"Message": swaggerSchemaObject{
schemaCore: schemaCore{
Type: "object",
},
Title: "title",
Description: "desc",
MultipleOf: 100,
Maximum: 101,
ExclusiveMaximum: true,
Minimum: 1,
ExclusiveMinimum: true,
MaxLength: 10,
MinLength: 3,
Pattern: "[a-z]+",
MaxItems: 20,
MinItems: 2,
UniqueItems: true,
MaxProperties: 33,
MinProperties: 22,
Required: []string{"req"},
},
},
},
}

for _, test := range tests {
t.Run(test.descr, func(t *testing.T) {

msgs := []*descriptor.Message{}
for _, msgdesc := range test.msgDescs {
msgdesc.Options = &protodescriptor.MessageOptions{}
msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
}

reg := descriptor.NewRegistry()
file := descriptor.File{
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
Name: proto.String("example.proto"),
Package: proto.String("example"),
Dependency: []string{},
MessageType: test.msgDescs,
EnumType: []*protodescriptor.EnumDescriptorProto{},
Service: []*protodescriptor.ServiceDescriptorProto{},
},
Messages: msgs,
}
reg.Load(&plugin.CodeGeneratorRequest{
ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
})

msgMap := map[string]*descriptor.Message{}
for _, d := range test.msgDescs {
name := d.GetName()
msg, err := reg.LookupMsg("example", name)
if err != nil {
t.Fatalf("lookup message %v: %v", name, err)
}
msgMap[msg.FQMN()] = msg

if schema, ok := test.schema[name]; ok {
err := proto.SetExtension(d.Options, swagger_options.E_Openapiv2Schema, &schema)
if err != nil {
t.Fatalf("SetExtension(%s, ...) returned error: %v", msg, err)
}
}
}

refs := make(refMap)
actual := make(swaggerDefinitionsObject)
renderMessagesAsDefinition(msgMap, actual, reg, refs)

if !reflect.DeepEqual(actual, test.defs) {
t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
}
})
}
}

func fileFixtureServices(services []string) *descriptor.File {
var (
svcdesc = []*protodescriptor.ServiceDescriptorProto{}
Expand Down