From 31abec93ce88b456148ab3d6f1d826a99a00edf2 Mon Sep 17 00:00:00 2001 From: Andrew Z Allen Date: Sun, 20 Dec 2015 00:21:11 -0700 Subject: [PATCH] Add tests for the template.go file. This also adds an error condition that I hadn't previously considered. If there is a streaming API swagger doesn't currently support it. I handle that now with an error. --- Makefile | 5 +- .../a_bit_of_everything.swagger.json | 218 --------- .../examplepb/flow_combination.swagger.json | 439 ------------------ protoc-gen-swagger/genswagger/template.go | 4 + .../genswagger/template_test.go | 411 ++++++++-------- 5 files changed, 215 insertions(+), 862 deletions(-) delete mode 100644 examples/examplepb/a_bit_of_everything.swagger.json delete mode 100644 examples/examplepb/flow_combination.swagger.json diff --git a/Makefile b/Makefile index 8ed107454be..f81d20ac231 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ OPTIONS_PROTO=$(GOOGLEAPIS_DIR)/google/api/annotations.proto $(GOOGLEAPIS_DIR)/g OPTIONS_GO=$(OPTIONS_PROTO:.proto=.pb.go) PKGMAP=Mgoogle/protobuf/descriptor.proto=$(GO_PLUGIN_PKG)/descriptor,Mgoogle/api/annotations.proto=$(PKG)/$(GOOGLEAPIS_DIR)/google/api,Mexamples/sub/message.proto=$(PKG)/examples/sub +SWAGGER_EXAMPLES=examples/examplepb/echo_service.proto EXAMPLES=examples/examplepb/echo_service.proto \ examples/examplepb/a_bit_of_everything.proto \ examples/examplepb/flow_combination.proto @@ -67,8 +68,8 @@ $(EXAMPLE_DEPSRCS): $(GO_PLUGIN) $(EXAMPLE_DEPS) protoc -I $(PROTOC_INC_PATH) -I. --plugin=$(GO_PLUGIN) --go_out=$(PKGMAP),plugins=grpc:. $(EXAMPLE_DEPS) $(EXAMPLE_GWSRCS): $(GATEWAY_PLUGIN) $(EXAMPLES) protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(GATEWAY_PLUGIN) --grpc-gateway_out=logtostderr=true,$(PKGMAP):. $(EXAMPLES) -$(EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(EXAMPLES) - protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,$(PKGMAP):. $(EXAMPLES) +$(EXAMPLE_SWAGGERSRCS): $(SWAGGER_PLUGIN) $(SWAGGER_EXAMPLES) + protoc -I $(PROTOC_INC_PATH) -I. -I$(GOOGLEAPIS_DIR) --plugin=$(SWAGGER_PLUGIN) --swagger_out=logtostderr=true,$(PKGMAP):. $(SWAGGER_EXAMPLES) examples: $(EXAMPLE_SVCSRCS) $(EXAMPLE_GWSRCS) $(EXAMPLE_DEPSRCS) $(EXAMPLE_SWAGGERSRCS) test: examples diff --git a/examples/examplepb/a_bit_of_everything.swagger.json b/examples/examplepb/a_bit_of_everything.swagger.json deleted file mode 100644 index b8651c5a6d9..00000000000 --- a/examples/examplepb/a_bit_of_everything.swagger.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "swagger": "2.0", - "info": {}, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/v1/example/a_bit_of_everything": { - "get": { - "summary": "Generated for ABitOfEverythingService.List - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - }, - "post": { - "summary": "", - "response": null - } - }, - "/v1/example/a_bit_of_everything/bulk": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for ABitOfEverythingService.BulkCreate - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - } - }, - "/v1/example/a_bit_of_everything/echo": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for ABitOfEverythingService.BulkEcho - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - } - }, - "/v1/example/a_bit_of_everything/echo/{value}": { - "get": { - "summary": "Generated for ABitOfEverythingService.Echo - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - }, - "post": { - "summary": "", - "response": null - } - }, - "/v1/example/a_bit_of_everything/{float_value}/{double_value}/{int64_value}/separator/{uint64_value}/{int32_value}/{fixed64_value}/{fixed32_value}/{bool_value}/{string_value=strprefix/*}/{uint32_value}/{sfixed32_value}/{sfixed64_value}/{sint32_value}/{sint64_value}": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for ABitOfEverythingService.Create - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - } - }, - "/v1/example/a_bit_of_everything/{uuid}": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "", - "response": null - } - }, - "/v2/example/echo": { - "get": { - "summary": "Generated for ABitOfEverythingService.Echo - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/ABitOfEverything" - } - } - } - }, - "post": { - "summary": "", - "response": null - } - } - }, - "definitions": { - "ABitOfEverything": { - "properties": { - "bool_value": { - "type": "boolean", - "format": "boolean" - }, - "double_value": { - "type": "number", - "format": "double" - }, - "fixed32_value": { - "type": "integer", - "format": "int32" - }, - "fixed64_value": { - "type": "integer", - "format": "int64" - }, - "float_value": { - "type": "number", - "format": "float" - }, - "int32_value": { - "type": "integer", - "format": "int32" - }, - "int64_value": { - "type": "integer", - "format": "int64" - }, - "nested": { - "$ref": "#/definitions/Nested" - }, - "sfixed32_value": { - "type": "integer", - "format": "int32" - }, - "sfixed64_value": { - "type": "integer", - "format": "int32" - }, - "sint32_value": { - "type": "integer", - "format": "int32" - }, - "sint64_value": { - "type": "integer", - "format": "int64" - }, - "string_value": { - "type": "string", - "format": "string" - }, - "uint32_value": { - "type": "integer", - "format": "int64" - }, - "uint64_value": { - "type": "integer", - "format": "int64" - }, - "uuid": { - "type": "string", - "format": "string" - } - } - }, - "EmptyMessage": {}, - "IdMessage": { - "properties": { - "uuid": { - "type": "string", - "format": "string" - } - } - }, - "Nested": { - "properties": { - "amount": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string", - "format": "string" - } - } - } - } -} diff --git a/examples/examplepb/flow_combination.swagger.json b/examples/examplepb/flow_combination.swagger.json deleted file mode 100644 index 0ecd964f6ff..00000000000 --- a/examples/examplepb/flow_combination.swagger.json +++ /dev/null @@ -1,439 +0,0 @@ -{ - "swagger": "2.0", - "info": {}, - "schemes": [ - "http", - "https" - ], - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "paths": { - "/rpc/body/path/{a}/query/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/path/{a}/query/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/path/{a}/{b}/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/path/{a}/{b}/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/query/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/query/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/body/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/empty/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcEmptyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/empty/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcEmptyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path-nested/{a.str}/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcPathNestedRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path-nested/{a.str}/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcPathNestedStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path-nested/{a.str}/{b}/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcPathNestedRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path-nested/{a.str}/{b}/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcPathNestedStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path/{a}/query/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path/{a}/query/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path/{a}/{b}/{c}/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/path/{a}/{b}/{c}/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/query/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/rpc/query/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.RpcBodyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/stream/empty/rpc": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.StreamEmptyRpc - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - }, - "/stream/empty/stream": { - "get": { - "summary": "", - "response": null - }, - "post": { - "summary": "Generated for FlowCombination.StreamEmptyStream - ", - "response": { - "default": { - "description": "Description", - "schema": { - "$ref": "#/definitions/EmptyProto" - } - } - } - } - } - }, - "definitions": { - "EmptyProto": {}, - "NestedProto": { - "properties": { - "a": { - "$ref": "#/definitions/UnaryProto" - }, - "b": { - "type": "string", - "format": "string" - }, - "c": { - "type": "string", - "format": "string" - } - } - }, - "NonEmptyProto": { - "properties": { - "a": { - "type": "string", - "format": "string" - }, - "b": { - "type": "string", - "format": "string" - }, - "c": { - "type": "string", - "format": "string" - } - } - }, - "SingleNestedProto": { - "properties": { - "a": { - "$ref": "#/definitions/UnaryProto" - } - } - }, - "UnaryProto": { - "properties": { - "str": { - "type": "string", - "format": "string" - } - } - } - } -} diff --git a/protoc-gen-swagger/genswagger/template.go b/protoc-gen-swagger/genswagger/template.go index ea916b2a305..3fc833b3c8f 100644 --- a/protoc-gen-swagger/genswagger/template.go +++ b/protoc-gen-swagger/genswagger/template.go @@ -3,6 +3,7 @@ package genswagger import ( "bytes" "encoding/json" + "errors" "fmt" "strings" @@ -133,6 +134,9 @@ func applyTemplate(p param) (string, error) { for _, svc := range p.Services { for _, meth := range svc.Methods { + if meth.GetClientStreaming() || meth.GetServerStreaming() { + return "", errors.New(`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?`) + } for _, b := range meth.Bindings { pathItemObject := swaggerPathItemObject{} operationObject := swaggerOperationObject{ diff --git a/protoc-gen-swagger/genswagger/template_test.go b/protoc-gen-swagger/genswagger/template_test.go index deffef4d87e..7ea1bd95d69 100644 --- a/protoc-gen-swagger/genswagger/template_test.go +++ b/protoc-gen-swagger/genswagger/template_test.go @@ -1,11 +1,12 @@ package genswagger import ( - "strings" + "encoding/json" + "reflect" "testing" - "github.com/gengo/grpc-gateway/protoc-gen-swagger/descriptor" - "github.com/gengo/grpc-gateway/protoc-gen-swagger/httprule" + "github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/descriptor" + "github.com/gengo/grpc-gateway/protoc-gen-grpc-gateway/httprule" "github.com/golang/protobuf/proto" protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" ) @@ -29,7 +30,7 @@ func crossLinkFixture(f *descriptor.File) *descriptor.File { return f } -func TestApplyTemplateHeader(t *testing.T) { +func TestApplyTemplateSimple(t *testing.T) { msgdesc := &protodescriptor.DescriptorProto{ Name: proto.String("ExampleMessage"), } @@ -70,6 +71,11 @@ func TestApplyTemplateHeader(t *testing.T) { { HTTPMethod: "GET", Body: &descriptor.Body{FieldPath: nil}, + PathTmpl: httprule.Template{ + Version: 1, + OpCodes: []int{0, 0}, + Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be + }, }, }, }, @@ -77,13 +83,37 @@ func TestApplyTemplateHeader(t *testing.T) { }, }, } - got, err := applyTemplate(param{File: crossLinkFixture(&file)}) + result, err := applyTemplate(param{File: crossLinkFixture(&file)}) + if err != nil { + t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) + return + } + got := new(swaggerObject) + err = json.Unmarshal([]byte(result), got) if err != nil { t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) return } - if want := "package example_pb\n"; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) + if want, is, name := "2.0", got.Swagger, "Swagger"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := "", got.BasePath, "BasePath"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"http", "https"}, got.Schemes, "Schemes"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"application/json"}, got.Consumes, "Consumes"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"application/json"}, got.Produces, "Produces"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + + // If there was a failure, print out the input and the json result for debugging. + if t.Failed() { + t.Errorf("had: %s", file) + t.Errorf("got: %s", result) } } @@ -127,124 +157,124 @@ func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) { Name: proto.String("ExampleService"), Method: []*protodescriptor.MethodDescriptorProto{meth}, } - for _, spec := range []struct { - serverStreaming bool - sigWant string - }{ - { - serverStreaming: false, - sigWant: `func request_ExampleService_Echo_0(ctx context.Context, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, error) {`, - }, - { - serverStreaming: true, - sigWant: `func request_ExampleService_Echo_0(ctx context.Context, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, error) {`, - }, - } { - meth.ServerStreaming = proto.Bool(spec.serverStreaming) - msg := &descriptor.Message{ - DescriptorProto: msgdesc, - } - nested := &descriptor.Message{ - DescriptorProto: nesteddesc, - } + meth.ServerStreaming = proto.Bool(false) - nestedField := &descriptor.Field{ - Message: msg, - FieldDescriptorProto: msg.GetField()[0], - } - intField := &descriptor.Field{ - Message: nested, - FieldDescriptorProto: nested.GetField()[0], - } - boolField := &descriptor.Field{ - Message: nested, - FieldDescriptorProto: nested.GetField()[1], - } - file := descriptor.File{ - FileDescriptorProto: &protodescriptor.FileDescriptorProto{ - Name: proto.String("example.proto"), - Package: proto.String("example"), - MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc}, - Service: []*protodescriptor.ServiceDescriptorProto{svc}, - }, - GoPkg: descriptor.GoPackage{ - Path: "example.com/path/to/example/example.pb", - Name: "example_pb", - }, - Messages: []*descriptor.Message{msg, nested}, - Services: []*descriptor.Service{ - { - ServiceDescriptorProto: svc, - Methods: []*descriptor.Method{ - { - MethodDescriptorProto: meth, - RequestType: msg, - ResponseType: msg, - Bindings: []*descriptor.Binding{ - { - HTTPMethod: "POST", - PathTmpl: httprule.Template{ - Version: 1, - OpCodes: []int{0, 0}, - }, - PathParams: []descriptor.Parameter{ - { - FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ - { - Name: "nested", - Target: nestedField, - }, - { - Name: "int32", - Target: intField, - }, - }), - Target: intField, - }, - }, - Body: &descriptor.Body{ + msg := &descriptor.Message{ + DescriptorProto: msgdesc, + } + nested := &descriptor.Message{ + DescriptorProto: nesteddesc, + } + + nestedField := &descriptor.Field{ + Message: msg, + FieldDescriptorProto: msg.GetField()[0], + } + intField := &descriptor.Field{ + Message: nested, + FieldDescriptorProto: nested.GetField()[0], + } + boolField := &descriptor.Field{ + Message: nested, + FieldDescriptorProto: nested.GetField()[1], + } + file := descriptor.File{ + FileDescriptorProto: &protodescriptor.FileDescriptorProto{ + Name: proto.String("example.proto"), + Package: proto.String("example"), + MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc}, + Service: []*protodescriptor.ServiceDescriptorProto{svc}, + }, + GoPkg: descriptor.GoPackage{ + Path: "example.com/path/to/example/example.pb", + Name: "example_pb", + }, + Messages: []*descriptor.Message{msg, nested}, + Services: []*descriptor.Service{ + { + ServiceDescriptorProto: svc, + Methods: []*descriptor.Method{ + { + MethodDescriptorProto: meth, + RequestType: msg, + ResponseType: msg, + Bindings: []*descriptor.Binding{ + { + HTTPMethod: "POST", + PathTmpl: httprule.Template{ + Version: 1, + OpCodes: []int{0, 0}, + Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be + }, + PathParams: []descriptor.Parameter{ + { FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ { Name: "nested", Target: nestedField, }, { - Name: "bool", - Target: boolField, + Name: "int32", + Target: intField, }, }), + Target: intField, }, }, + Body: &descriptor.Body{ + FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ + { + Name: "nested", + Target: nestedField, + }, + { + Name: "bool", + Target: boolField, + }, + }), + }, }, }, }, }, }, - } - got, err := applyTemplate(param{File: crossLinkFixture(&file)}) - if err != nil { - t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) - return - } - if want := spec.sigWant; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `json.NewDecoder(req.Body).Decode(&protoReq.GetNested().Bool)`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `val, ok = pathParams["nested.int32"]`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `protoReq.GetNested().Int32, err = runtime.Int32P(val)`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } + }, + } + result, err := applyTemplate(param{File: crossLinkFixture(&file)}) + if err != nil { + t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) + return + } + got := new(swaggerObject) + err = json.Unmarshal([]byte(result), got) + if err != nil { + t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) + return + } + if want, is, name := "2.0", got.Swagger, "Swagger"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := "", got.BasePath, "BasePath"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"http", "https"}, got.Schemes, "Schemes"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"application/json"}, got.Consumes, "Consumes"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := []string{"application/json"}, got.Produces, "Produces"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", file, name, is, want) + } + if want, is, name := "Generated for ExampleService.Echo - ", got.Paths["/v1/echo"].Post.Summary, "Paths[/v1/echo].Post.Summary"; !reflect.DeepEqual(is, want) { + t.Errorf("applyTemplate(file).%s = %s want to be %s", name, is, want) + } + + // If there was a failure, print out the input and the json result for debugging. + if t.Failed() { + t.Errorf("had: %s", file) + t.Errorf("got: %s", result) } } @@ -283,122 +313,97 @@ func TestApplyTemplateRequestWithClientStreaming(t *testing.T) { InputType: proto.String("ExampleMessage"), OutputType: proto.String("ExampleMessage"), ClientStreaming: proto.Bool(true), + ServerStreaming: proto.Bool(true), } svc := &protodescriptor.ServiceDescriptorProto{ Name: proto.String("ExampleService"), Method: []*protodescriptor.MethodDescriptorProto{meth}, } - for _, spec := range []struct { - serverStreaming bool - sigWant string - }{ - { - serverStreaming: false, - sigWant: `func request_ExampleService_Echo_0(ctx context.Context, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, error) {`, - }, - { - serverStreaming: true, - sigWant: `func request_ExampleService_Echo_0(ctx context.Context, client ExampleServiceClient, req *http.Request, pathParams map[string]string) (ExampleService_EchoClient, error) {`, - }, - } { - meth.ServerStreaming = proto.Bool(spec.serverStreaming) - msg := &descriptor.Message{ - DescriptorProto: msgdesc, - } - nested := &descriptor.Message{ - DescriptorProto: nesteddesc, - } + msg := &descriptor.Message{ + DescriptorProto: msgdesc, + } + nested := &descriptor.Message{ + DescriptorProto: nesteddesc, + } - nestedField := &descriptor.Field{ - Message: msg, - FieldDescriptorProto: msg.GetField()[0], - } - intField := &descriptor.Field{ - Message: nested, - FieldDescriptorProto: nested.GetField()[0], - } - boolField := &descriptor.Field{ - Message: nested, - FieldDescriptorProto: nested.GetField()[1], - } - file := descriptor.File{ - FileDescriptorProto: &protodescriptor.FileDescriptorProto{ - Name: proto.String("example.proto"), - Package: proto.String("example"), - MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc}, - Service: []*protodescriptor.ServiceDescriptorProto{svc}, - }, - GoPkg: descriptor.GoPackage{ - Path: "example.com/path/to/example/example.pb", - Name: "example_pb", - }, - Messages: []*descriptor.Message{msg, nested}, - Services: []*descriptor.Service{ - { - ServiceDescriptorProto: svc, - Methods: []*descriptor.Method{ - { - MethodDescriptorProto: meth, - RequestType: msg, - ResponseType: msg, - Bindings: []*descriptor.Binding{ - { - HTTPMethod: "POST", - PathTmpl: httprule.Template{ - Version: 1, - OpCodes: []int{0, 0}, - }, - PathParams: []descriptor.Parameter{ - { - FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ - { - Name: "nested", - Target: nestedField, - }, - { - Name: "int32", - Target: intField, - }, - }), - Target: intField, - }, - }, - Body: &descriptor.Body{ + nestedField := &descriptor.Field{ + Message: msg, + FieldDescriptorProto: msg.GetField()[0], + } + intField := &descriptor.Field{ + Message: nested, + FieldDescriptorProto: nested.GetField()[0], + } + boolField := &descriptor.Field{ + Message: nested, + FieldDescriptorProto: nested.GetField()[1], + } + file := descriptor.File{ + FileDescriptorProto: &protodescriptor.FileDescriptorProto{ + Name: proto.String("example.proto"), + Package: proto.String("example"), + MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc}, + Service: []*protodescriptor.ServiceDescriptorProto{svc}, + }, + GoPkg: descriptor.GoPackage{ + Path: "example.com/path/to/example/example.pb", + Name: "example_pb", + }, + Messages: []*descriptor.Message{msg, nested}, + Services: []*descriptor.Service{ + { + ServiceDescriptorProto: svc, + Methods: []*descriptor.Method{ + { + MethodDescriptorProto: meth, + RequestType: msg, + ResponseType: msg, + Bindings: []*descriptor.Binding{ + { + HTTPMethod: "POST", + PathTmpl: httprule.Template{ + Version: 1, + OpCodes: []int{0, 0}, + Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be + }, + PathParams: []descriptor.Parameter{ + { FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ { Name: "nested", Target: nestedField, }, { - Name: "bool", - Target: boolField, + Name: "int32", + Target: intField, }, }), + Target: intField, }, }, + Body: &descriptor.Body{ + FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{ + { + Name: "nested", + Target: nestedField, + }, + { + Name: "bool", + Target: boolField, + }, + }), + }, }, }, }, }, }, - } - got, err := applyTemplate(param{File: crossLinkFixture(&file)}) - if err != nil { - t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err) - return - } - if want := spec.sigWant; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `json.NewDecoder(req.Body)`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `func RegisterExampleServiceHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } - if want := `pattern_ExampleService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{0, 0}, []string(nil), ""))`; !strings.Contains(got, want) { - t.Errorf("applyTemplate(%#v) = %s; want to contain %s", file, got, want) - } + }, + } + _, err := applyTemplate(param{File: crossLinkFixture(&file)}) + if err == nil { + t.Errorf("applyTemplate(%#v) should have failed cause swagger doesn't support streaming", file) + return } }