From cb6425bfb78d519f91960e097cb0cff8ab493d26 Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Fri, 22 Jul 2016 16:53:45 +0000 Subject: [PATCH 1/3] write query parameters to swagger definition addresses #159 --- protoc-gen-swagger/genswagger/template.go | 50 ++++++++ .../genswagger/template_test.go | 118 ++++++++++++++++++ 2 files changed, 168 insertions(+) diff --git a/protoc-gen-swagger/genswagger/template.go b/protoc-gen-swagger/genswagger/template.go index d360dff63ba..653125ae9d2 100644 --- a/protoc-gen-swagger/genswagger/template.go +++ b/protoc-gen-swagger/genswagger/template.go @@ -13,6 +13,49 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" ) +// messageToQueryParameters converts a message to a list of swagger query parameters. +func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter) ([]swaggerParameterObject, error) { + var parameters []swaggerParameterObject + var addParameterWithPrefix func(string, *descriptor.Field) error + addParameterWithPrefix = func(prefix string, field *descriptor.Field) error { + // make sure the parameter is not already listed as a path parameter + for _, pathParam := range pathParams { + if pathParam.Target == field { + return nil + } + } + schema := schemaOfField(field, reg) + if schema.Type != "" { + // basic type, add a basic query parameter + parameters = append(parameters, swaggerParameterObject{ + Name: prefix + field.GetName(), + Description: schema.Description, + In: "query", + Type: schema.Type, + }) + return nil + } + + // nested type, recurse + msg, err := reg.LookupMsg("", field.GetTypeName()) + if err != nil { + return fmt.Errorf("unknown message type %s", field.GetTypeName()) + } + for _, nestedField := range msg.Fields { + if err := addParameterWithPrefix(field.GetName()+".", nestedField); err != nil { + return err + } + } + return nil + } + for _, field := range message.Fields { + if err := addParameterWithPrefix("", field); err != nil { + return parameters, err + } + } + return parameters, nil +} + // 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 { @@ -400,6 +443,13 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re Required: true, Schema: &schema, }) + } else if b.HTTPMethod == "GET" { + // add the parameters to the query string + queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams) + if err != nil { + return err + } + parameters = append(parameters, queryParams...) } pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template)] diff --git a/protoc-gen-swagger/genswagger/template_test.go b/protoc-gen-swagger/genswagger/template_test.go index 1c897be8acd..fb18ca9ab29 100644 --- a/protoc-gen-swagger/genswagger/template_test.go +++ b/protoc-gen-swagger/genswagger/template_test.go @@ -7,6 +7,7 @@ import ( "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/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule" ) @@ -30,6 +31,123 @@ func crossLinkFixture(f *descriptor.File) *descriptor.File { return f } +func TestMessageToQueryParameters(t *testing.T) { + type test struct { + MsgDescs []*protodescriptor.DescriptorProto + Message string + Params []swaggerParameterObject + } + + tests := []test{ + { + MsgDescs: []*protodescriptor.DescriptorProto{ + &protodescriptor.DescriptorProto{ + Name: proto.String("ExampleMessage"), + Field: []*protodescriptor.FieldDescriptorProto{ + { + Name: proto.String("a"), + Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(), + Number: proto.Int32(1), + }, + { + Name: proto.String("b"), + Type: protodescriptor.FieldDescriptorProto_TYPE_DOUBLE.Enum(), + Number: proto.Int32(2), + }, + }, + }, + }, + Message: "ExampleMessage", + Params: []swaggerParameterObject{ + swaggerParameterObject{ + Name: "a", + In: "query", + Required: false, + Type: "string", + }, + swaggerParameterObject{ + Name: "b", + In: "query", + Required: false, + Type: "number", + }, + }, + }, + { + MsgDescs: []*protodescriptor.DescriptorProto{ + &protodescriptor.DescriptorProto{ + Name: proto.String("ExampleMessage"), + Field: []*protodescriptor.FieldDescriptorProto{ + { + Name: proto.String("nested"), + Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(), + TypeName: proto.String(".example.Nested"), + Number: proto.Int32(1), + }, + }, + }, + &protodescriptor.DescriptorProto{ + Name: proto.String("Nested"), + Field: []*protodescriptor.FieldDescriptorProto{ + { + Name: proto.String("a"), + Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(), + Number: proto.Int32(1), + }, + }, + }, + }, + Message: "ExampleMessage", + Params: []swaggerParameterObject{ + swaggerParameterObject{ + Name: "nested.a", + In: "query", + Required: false, + Type: "string", + }, + }, + }, + } + + for _, test := range tests { + reg := descriptor.NewRegistry() + msgs := []*descriptor.Message{} + for _, msgdesc := range test.MsgDescs { + msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc}) + } + file := descriptor.File{ + FileDescriptorProto: &protodescriptor.FileDescriptorProto{ + SourceCodeInfo: &protodescriptor.SourceCodeInfo{}, + Name: proto.String("example.proto"), + Package: proto.String("example"), + Dependency: []string{}, + MessageType: test.MsgDescs, + Service: []*protodescriptor.ServiceDescriptorProto{}, + }, + GoPkg: descriptor.GoPackage{ + Path: "example.com/path/to/example/example.pb", + Name: "example_pb", + }, + Messages: msgs, + } + reg.Load(&plugin.CodeGeneratorRequest{ + ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto}, + }) + + message, err := reg.LookupMsg("", ".example."+test.Message) + if err != nil { + t.Fatalf("failed to lookup message: %s", err) + } + params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}) + if err != nil { + t.Fatalf("failed to convert message to query parameters: %s", err) + } + if !reflect.DeepEqual(params, test.Params) { + t.Errorf("expected %v, got %v", test.Params, params) + } + } +} + func TestApplyTemplateSimple(t *testing.T) { msgdesc := &protodescriptor.DescriptorProto{ Name: proto.String("ExampleMessage"), From 56af20c2745420d3c745adc155ac2d737c178d2a Mon Sep 17 00:00:00 2001 From: Rafael Garcia Date: Mon, 25 Jul 2016 18:34:39 +0000 Subject: [PATCH 2/3] regenerate examples --- examples/clients/abe/ABitOfEverythingServiceApi.go | 10 ++++++++-- examples/examplepb/a_bit_of_everything.swagger.json | 8 ++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/clients/abe/ABitOfEverythingServiceApi.go b/examples/clients/abe/ABitOfEverythingServiceApi.go index eb470763999..975564f4069 100644 --- a/examples/clients/abe/ABitOfEverythingServiceApi.go +++ b/examples/clients/abe/ABitOfEverythingServiceApi.go @@ -354,10 +354,11 @@ func (a ABitOfEverythingServiceApi) Echo (value string) (SubStringMessage, error /** * * + * @param value * @return SubStringMessage */ -//func (a ABitOfEverythingServiceApi) Echo_1 () (SubStringMessage, error) { -func (a ABitOfEverythingServiceApi) Echo_1 () (SubStringMessage, error) { +//func (a ABitOfEverythingServiceApi) Echo_1 (value string) (SubStringMessage, error) { +func (a ABitOfEverythingServiceApi) Echo_1 (value string) (SubStringMessage, error) { _sling := sling.New().Get(a.basePath) @@ -366,6 +367,11 @@ func (a ABitOfEverythingServiceApi) Echo_1 () (SubStringMessage, error) { _sling = _sling.Path(path) + type QueryParams struct { + value string `url:"value,omitempty"` + +} + _sling = _sling.QueryStruct(&QueryParams{ value: value }) // accept header accepts := []string { "application/json" } for key := range accepts { diff --git a/examples/examplepb/a_bit_of_everything.swagger.json b/examples/examplepb/a_bit_of_everything.swagger.json index 0d9366f7ad9..f7c7f6c9a59 100644 --- a/examples/examplepb/a_bit_of_everything.swagger.json +++ b/examples/examplepb/a_bit_of_everything.swagger.json @@ -312,6 +312,14 @@ } } }, + "parameters": [ + { + "name": "value", + "in": "query", + "required": false, + "type": "string" + } + ], "tags": [ "ABitOfEverythingService" ] From 28132c2563d9480a7e75a2fee970a7133a864882 Mon Sep 17 00:00:00 2001 From: Yukinari Toyota Date: Fri, 13 Jan 2017 01:13:25 +0900 Subject: [PATCH 3/3] swagger support enum in query. fix deep nested type handling. refactor. #199 --- .../clients/abe/ABitOfEverythingServiceApi.go | 110 ++++++++ .../clients/abe/ExamplepbABitOfEverything.go | 1 + examples/examplepb/a_bit_of_everything.pb.go | 202 ++++++++------ .../examplepb/a_bit_of_everything.pb.gw.go | 67 +++++ examples/examplepb/a_bit_of_everything.proto | 8 + .../a_bit_of_everything.swagger.json | 256 +++++++++++++++--- examples/examplepb/echo_service.swagger.json | 4 +- examples/server/a_bit_of_everything.go | 13 + protoc-gen-swagger/genswagger/template.go | 197 +++++++++----- .../genswagger/template_test.go | 46 ++++ protoc-gen-swagger/genswagger/types.go | 17 +- 11 files changed, 739 insertions(+), 182 deletions(-) diff --git a/examples/clients/abe/ABitOfEverythingServiceApi.go b/examples/clients/abe/ABitOfEverythingServiceApi.go index 975564f4069..fcc60763608 100644 --- a/examples/clients/abe/ABitOfEverythingServiceApi.go +++ b/examples/clients/abe/ABitOfEverythingServiceApi.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "github.com/dghubble/sling" + "time" ) type ABitOfEverythingServiceApi struct { @@ -474,6 +475,115 @@ func (a ABitOfEverythingServiceApi) Echo_2 (body string) (SubStringMessage, erro return *successPayload, err } +/** + * + * + * @param uuid + * @param singleNestedName name is nested field. + * @param singleNestedAmount + * @param singleNestedOk - FALSE: FALSE is false.\n - TRUE: TRUE is true. + * @param floatValue + * @param doubleValue + * @param int64Value + * @param uint64Value + * @param int32Value + * @param fixed64Value + * @param fixed32Value + * @param boolValue + * @param stringValue + * @param uint32Value TODO(yugui) add bytes_value. + * @param enumValue - ZERO: ZERO means 0\n - ONE: ONE means 1 + * @param sfixed32Value + * @param sfixed64Value + * @param sint32Value + * @param sint64Value + * @param repeatedStringValue + * @param oneofString + * @param nonConventionalNameValue + * @param timestampValue + * @param repeatedEnumValue repeated enum value. it is comma-separated in query.\n\n - ZERO: ZERO means 0\n - ONE: ONE means 1 + * @return ProtobufEmpty + */ +//func (a ABitOfEverythingServiceApi) GetQuery (uuid string, singleNestedName string, singleNestedAmount int64, singleNestedOk string, floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, uint32Value int64, enumValue string, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, repeatedStringValue []string, oneofString string, nonConventionalNameValue string, timestampValue time.Time, repeatedEnumValue []string) (ProtobufEmpty, error) { +func (a ABitOfEverythingServiceApi) GetQuery (uuid string, singleNestedName string, singleNestedAmount int64, singleNestedOk string, floatValue float32, doubleValue float64, int64Value string, uint64Value string, int32Value int32, fixed64Value string, fixed32Value int64, boolValue bool, stringValue string, uint32Value int64, enumValue string, sfixed32Value int32, sfixed64Value string, sint32Value int32, sint64Value string, repeatedStringValue []string, oneofString string, nonConventionalNameValue string, timestampValue time.Time, repeatedEnumValue []string) (ProtobufEmpty, error) { + + _sling := sling.New().Get(a.basePath) + + // create path and map variables + path := "/v1/example/a_bit_of_everything/query/{uuid}" + path = strings.Replace(path, "{" + "uuid" + "}", fmt.Sprintf("%v", uuid), -1) + + _sling = _sling.Path(path) + + type QueryParams struct { + singleNestedName string `url:"single_nested.name,omitempty"` + singleNestedAmount int64 `url:"single_nested.amount,omitempty"` + singleNestedOk string `url:"single_nested.ok,omitempty"` + floatValue float32 `url:"float_value,omitempty"` + doubleValue float64 `url:"double_value,omitempty"` + int64Value string `url:"int64_value,omitempty"` + uint64Value string `url:"uint64_value,omitempty"` + int32Value int32 `url:"int32_value,omitempty"` + fixed64Value string `url:"fixed64_value,omitempty"` + fixed32Value int64 `url:"fixed32_value,omitempty"` + boolValue bool `url:"bool_value,omitempty"` + stringValue string `url:"string_value,omitempty"` + uint32Value int64 `url:"uint32_value,omitempty"` + enumValue string `url:"enum_value,omitempty"` + sfixed32Value int32 `url:"sfixed32_value,omitempty"` + sfixed64Value string `url:"sfixed64_value,omitempty"` + sint32Value int32 `url:"sint32_value,omitempty"` + sint64Value string `url:"sint64_value,omitempty"` + repeatedStringValue []string `url:"repeated_string_value,omitempty"` + oneofString string `url:"oneof_string,omitempty"` + nonConventionalNameValue string `url:"nonConventionalNameValue,omitempty"` + timestampValue time.Time `url:"timestamp_value,omitempty"` + repeatedEnumValue []string `url:"repeated_enum_value,omitempty"` + +} + _sling = _sling.QueryStruct(&QueryParams{ singleNestedName: singleNestedName,singleNestedAmount: singleNestedAmount,singleNestedOk: singleNestedOk,floatValue: floatValue,doubleValue: doubleValue,int64Value: int64Value,uint64Value: uint64Value,int32Value: int32Value,fixed64Value: fixed64Value,fixed32Value: fixed32Value,boolValue: boolValue,stringValue: stringValue,uint32Value: uint32Value,enumValue: enumValue,sfixed32Value: sfixed32Value,sfixed64Value: sfixed64Value,sint32Value: sint32Value,sint64Value: sint64Value,repeatedStringValue: repeatedStringValue,oneofString: oneofString,nonConventionalNameValue: nonConventionalNameValue,timestampValue: timestampValue,repeatedEnumValue: repeatedEnumValue }) + // accept header + accepts := []string { "application/json" } + for key := range accepts { + _sling = _sling.Set("Accept", accepts[key]) + break // only use the first Accept + } + + + var successPayload = new(ProtobufEmpty) + + // We use this map (below) so that any arbitrary error JSON can be handled. + // FIXME: This is in the absence of this Go generator honoring the non-2xx + // response (error) models, which needs to be implemented at some point. + var failurePayload map[string]interface{} + + httpResponse, err := _sling.Receive(successPayload, &failurePayload) + + if err == nil { + // err == nil only means that there wasn't a sub-application-layer error (e.g. no network error) + if failurePayload != nil { + // If the failurePayload is present, there likely was some kind of non-2xx status + // returned (and a JSON payload error present) + var str []byte + str, err = json.Marshal(failurePayload) + if err == nil { // For safety, check for an error marshalling... probably superfluous + // This will return the JSON error body as a string + err = errors.New(string(str)) + } + } else { + // So, there was no network-type error, and nothing in the failure payload, + // but we should still check the status code + if httpResponse == nil { + // This should never happen... + err = errors.New("No HTTP Response received.") + } else if code := httpResponse.StatusCode; 200 > code || code > 299 { + err = errors.New("HTTP Error: " + string(httpResponse.StatusCode)) + } + } + } + + return *successPayload, err +} /** * * diff --git a/examples/clients/abe/ExamplepbABitOfEverything.go b/examples/clients/abe/ExamplepbABitOfEverything.go index 3440ace1888..2892eb624d9 100644 --- a/examples/clients/abe/ExamplepbABitOfEverything.go +++ b/examples/clients/abe/ExamplepbABitOfEverything.go @@ -31,5 +31,6 @@ type ExamplepbABitOfEverything struct { MappedNestedValue map[string]ABitOfEverythingNested `json:"mapped_nested_value,omitempty"` NonConventionalNameValue string `json:"nonConventionalNameValue,omitempty"` TimestampValue time.Time `json:"timestamp_value,omitempty"` + RepeatedEnumValue []ExamplepbNumericEnum `json:"repeated_enum_value,omitempty"` } diff --git a/examples/examplepb/a_bit_of_everything.pb.go b/examples/examplepb/a_bit_of_everything.pb.go index c6cba0d1293..964d9ca7d39 100644 --- a/examples/examplepb/a_bit_of_everything.pb.go +++ b/examples/examplepb/a_bit_of_everything.pb.go @@ -105,6 +105,8 @@ type ABitOfEverything struct { MappedNestedValue map[string]*ABitOfEverything_Nested `protobuf:"bytes,24,rep,name=mapped_nested_value,json=mappedNestedValue" json:"mapped_nested_value,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` NonConventionalNameValue string `protobuf:"bytes,26,opt,name=nonConventionalNameValue" json:"nonConventionalNameValue,omitempty"` TimestampValue *google_protobuf2.Timestamp `protobuf:"bytes,27,opt,name=timestamp_value,json=timestampValue" json:"timestamp_value,omitempty"` + // repeated enum value. it is comma-separated in query + RepeatedEnumValue []NumericEnum `protobuf:"varint,28,rep,packed,name=repeated_enum_value,json=repeatedEnumValue,enum=grpc.gateway.examples.examplepb.NumericEnum" json:"repeated_enum_value,omitempty"` } func (m *ABitOfEverything) Reset() { *m = ABitOfEverything{} } @@ -315,6 +317,13 @@ func (m *ABitOfEverything) GetTimestampValue() *google_protobuf2.Timestamp { return nil } +func (m *ABitOfEverything) GetRepeatedEnumValue() []NumericEnum { + if m != nil { + return m.RepeatedEnumValue + } + return nil +} + // XXX_OneofFuncs is for the internal use of the proto package. func (*ABitOfEverything) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { return _ABitOfEverything_OneofMarshaler, _ABitOfEverything_OneofUnmarshaler, _ABitOfEverything_OneofSizer, []interface{}{ @@ -442,6 +451,7 @@ type ABitOfEverythingServiceClient interface { Lookup(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*ABitOfEverything, error) Update(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) Delete(ctx context.Context, in *sub2.IdMessage, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) + GetQuery(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) Echo(ctx context.Context, in *grpc_gateway_examples_sub.StringMessage, opts ...grpc.CallOption) (*grpc_gateway_examples_sub.StringMessage, error) DeepPathEcho(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*ABitOfEverything, error) NoBindings(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) @@ -501,6 +511,15 @@ func (c *aBitOfEverythingServiceClient) Delete(ctx context.Context, in *sub2.IdM return out, nil } +func (c *aBitOfEverythingServiceClient) GetQuery(ctx context.Context, in *ABitOfEverything, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) { + out := new(google_protobuf1.Empty) + err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/GetQuery", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *aBitOfEverythingServiceClient) Echo(ctx context.Context, in *grpc_gateway_examples_sub.StringMessage, opts ...grpc.CallOption) (*grpc_gateway_examples_sub.StringMessage, error) { out := new(grpc_gateway_examples_sub.StringMessage) err := grpc.Invoke(ctx, "/grpc.gateway.examples.examplepb.ABitOfEverythingService/Echo", in, out, c.cc, opts...) @@ -545,6 +564,7 @@ type ABitOfEverythingServiceServer interface { Lookup(context.Context, *sub2.IdMessage) (*ABitOfEverything, error) Update(context.Context, *ABitOfEverything) (*google_protobuf1.Empty, error) Delete(context.Context, *sub2.IdMessage) (*google_protobuf1.Empty, error) + GetQuery(context.Context, *ABitOfEverything) (*google_protobuf1.Empty, error) Echo(context.Context, *grpc_gateway_examples_sub.StringMessage) (*grpc_gateway_examples_sub.StringMessage, error) DeepPathEcho(context.Context, *ABitOfEverything) (*ABitOfEverything, error) NoBindings(context.Context, *google_protobuf1.Empty) (*google_protobuf1.Empty, error) @@ -645,6 +665,24 @@ func _ABitOfEverythingService_Delete_Handler(srv interface{}, ctx context.Contex return interceptor(ctx, in, info, handler) } +func _ABitOfEverythingService_GetQuery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ABitOfEverything) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ABitOfEverythingServiceServer).GetQuery(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/grpc.gateway.examples.examplepb.ABitOfEverythingService/GetQuery", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ABitOfEverythingServiceServer).GetQuery(ctx, req.(*ABitOfEverything)) + } + return interceptor(ctx, in, info, handler) +} + func _ABitOfEverythingService_Echo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(grpc_gateway_examples_sub.StringMessage) if err := dec(in); err != nil { @@ -741,6 +779,10 @@ var _ABitOfEverythingService_serviceDesc = grpc.ServiceDesc{ MethodName: "Delete", Handler: _ABitOfEverythingService_Delete_Handler, }, + { + MethodName: "GetQuery", + Handler: _ABitOfEverythingService_GetQuery_Handler, + }, { MethodName: "Echo", Handler: _ABitOfEverythingService_Echo_Handler, @@ -829,83 +871,85 @@ var _AnotherServiceWithNoBindings_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("examples/examplepb/a_bit_of_everything.proto", fileDescriptor1) } var fileDescriptor1 = []byte{ - // 1233 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x57, 0xcd, 0x6e, 0xdb, 0x46, - 0x17, 0xf5, 0x48, 0xb6, 0x6c, 0x5d, 0x5a, 0xb2, 0x32, 0x4e, 0x1c, 0x45, 0xc9, 0x07, 0xf1, 0x53, - 0xda, 0x82, 0x70, 0x03, 0x12, 0x51, 0x82, 0x22, 0x31, 0xd0, 0x06, 0x56, 0xa2, 0xc2, 0x45, 0x13, - 0x39, 0xa5, 0x93, 0x14, 0x30, 0x9a, 0x0a, 0x94, 0x34, 0x92, 0x08, 0x8b, 0x1c, 0x82, 0x1c, 0xaa, - 0x16, 0x54, 0x75, 0xd1, 0x45, 0x5f, 0xa0, 0xfb, 0x6c, 0x0a, 0x14, 0xdd, 0x74, 0xd9, 0x75, 0x1f, - 0xa2, 0x8b, 0xee, 0x8b, 0x3e, 0x48, 0xc1, 0x19, 0x92, 0xa6, 0x64, 0x0b, 0xf2, 0x0f, 0x90, 0x9d, - 0x66, 0xee, 0xb9, 0xe7, 0xdc, 0x9f, 0x99, 0x3b, 0x14, 0xdc, 0x23, 0xc7, 0x86, 0xe5, 0x0c, 0x88, - 0xa7, 0x85, 0x3f, 0x9c, 0x96, 0x66, 0x34, 0x5b, 0x26, 0x6b, 0xd2, 0x6e, 0x93, 0x0c, 0x89, 0x3b, - 0x62, 0x7d, 0xd3, 0xee, 0xa9, 0x8e, 0x4b, 0x19, 0xc5, 0xe5, 0x9e, 0xeb, 0xb4, 0xd5, 0x9e, 0xc1, - 0xc8, 0x77, 0xc6, 0x48, 0x8d, 0x5c, 0xd5, 0xd8, 0xb5, 0x74, 0xa7, 0x47, 0x69, 0x6f, 0x40, 0x34, - 0xc3, 0x31, 0x35, 0xc3, 0xb6, 0x29, 0x33, 0x98, 0x49, 0x6d, 0x4f, 0xb8, 0x97, 0x6e, 0x87, 0x56, - 0xbe, 0x6a, 0xf9, 0x5d, 0x8d, 0x58, 0x0e, 0x1b, 0x85, 0xc6, 0x52, 0x1c, 0x89, 0xe7, 0xb7, 0x34, - 0x8b, 0x78, 0x9e, 0xd1, 0x23, 0x91, 0x63, 0xd2, 0x56, 0x9d, 0x31, 0x96, 0x67, 0x59, 0x99, 0x69, - 0x11, 0x8f, 0x19, 0x96, 0x23, 0x00, 0x95, 0x7f, 0xf2, 0x50, 0xd8, 0xad, 0x99, 0x6c, 0xbf, 0x5b, - 0x8f, 0x13, 0xc2, 0x6f, 0x21, 0xe7, 0x99, 0x76, 0x6f, 0x40, 0x9a, 0x36, 0xf1, 0x18, 0xe9, 0x14, - 0x6f, 0xc9, 0x48, 0x91, 0xaa, 0x8f, 0xd4, 0x05, 0x29, 0xaa, 0xb3, 0x4c, 0x6a, 0x83, 0xfb, 0xeb, - 0xeb, 0x82, 0x4e, 0xac, 0x30, 0x86, 0x65, 0xdf, 0x37, 0x3b, 0x45, 0x24, 0x23, 0x25, 0xab, 0xf3, - 0xdf, 0xf8, 0x25, 0x64, 0x42, 0xad, 0x94, 0x9c, 0xbe, 0x92, 0x56, 0xc8, 0x83, 0xcb, 0x20, 0x75, - 0x07, 0xd4, 0x60, 0xcd, 0xa1, 0x31, 0xf0, 0x49, 0x31, 0x2d, 0x23, 0x25, 0xa5, 0x03, 0xdf, 0x7a, - 0x13, 0xec, 0xe0, 0xff, 0xc3, 0x7a, 0x87, 0xfa, 0xad, 0x01, 0x09, 0x11, 0xcb, 0x32, 0x52, 0x90, - 0x2e, 0x89, 0x3d, 0x01, 0x29, 0x83, 0x64, 0xda, 0xec, 0x93, 0x87, 0x21, 0x62, 0x45, 0x46, 0x4a, - 0x5a, 0x07, 0xbe, 0x15, 0x73, 0xf8, 0x49, 0x44, 0x46, 0x46, 0xca, 0xb2, 0x2e, 0xf9, 0x09, 0x88, - 0xe0, 0x78, 0x50, 0x0d, 0x11, 0xab, 0x32, 0x52, 0x56, 0x38, 0xc7, 0x83, 0xaa, 0x00, 0xdc, 0x85, - 0x5c, 0xd7, 0x3c, 0x26, 0x9d, 0x98, 0x64, 0x4d, 0x46, 0x4a, 0x46, 0x5f, 0x0f, 0x37, 0xa7, 0x41, - 0x31, 0x4f, 0x56, 0x46, 0xca, 0x6a, 0x08, 0x8a, 0x98, 0xfe, 0x07, 0xd0, 0xa2, 0x74, 0x10, 0x22, - 0x40, 0x46, 0xca, 0x9a, 0x9e, 0x0d, 0x76, 0xe2, 0x60, 0x3d, 0xe6, 0x9a, 0x76, 0x2f, 0x04, 0x48, - 0xbc, 0xfe, 0x92, 0xd8, 0x9b, 0xca, 0x27, 0x56, 0xc9, 0xc9, 0x48, 0xc9, 0x89, 0x7c, 0x22, 0x91, - 0x2f, 0x01, 0x88, 0xed, 0x5b, 0x21, 0x20, 0x2f, 0x23, 0x25, 0x5f, 0xbd, 0xb7, 0xb0, 0x5b, 0x0d, - 0xdf, 0x22, 0xae, 0xd9, 0xae, 0xdb, 0xbe, 0xa5, 0x67, 0x03, 0x7f, 0x41, 0xf6, 0x21, 0xe4, 0xbd, - 0xe9, 0xbc, 0x36, 0x64, 0xa4, 0x6c, 0xe8, 0x39, 0x6f, 0x2a, 0xb1, 0x18, 0x16, 0xd7, 0xa8, 0x20, - 0x23, 0xa5, 0x10, 0xc1, 0x12, 0xdd, 0xf0, 0x92, 0xd1, 0x5f, 0x93, 0x91, 0x72, 0x4d, 0x97, 0xbc, - 0x44, 0xf4, 0x21, 0x24, 0xe6, 0xc1, 0x32, 0x52, 0xb0, 0x80, 0x44, 0x2c, 0x55, 0xb8, 0xe1, 0x12, - 0x87, 0x18, 0x8c, 0x74, 0x9a, 0x53, 0xf5, 0xda, 0x94, 0xd3, 0x4a, 0x56, 0xdf, 0x8c, 0x8c, 0x07, - 0x89, 0xba, 0x3d, 0x06, 0x89, 0xda, 0x24, 0x18, 0x0b, 0xc1, 0xad, 0x2d, 0x5e, 0xe7, 0xf7, 0x65, - 0x4b, 0x15, 0xb7, 0x4f, 0x8d, 0x6e, 0x9f, 0x5a, 0x0f, 0xac, 0x7b, 0x4b, 0x3a, 0x70, 0x30, 0x5f, - 0xe1, 0xbb, 0xb0, 0x2e, 0x5c, 0x85, 0x56, 0xf1, 0x46, 0xd0, 0x95, 0xbd, 0x25, 0x5d, 0x10, 0x0a, - 0x11, 0xfc, 0x0d, 0x64, 0x2d, 0xc3, 0x09, 0xe3, 0xd8, 0xe2, 0x37, 0xe4, 0xc9, 0xc5, 0x6f, 0xc8, - 0x0b, 0xc3, 0xe1, 0xe1, 0xd6, 0x6d, 0xe6, 0x8e, 0xf4, 0x35, 0x2b, 0x5c, 0xe2, 0x63, 0xd8, 0xb4, - 0x0c, 0xc7, 0x99, 0xcd, 0xf7, 0x26, 0xd7, 0xd9, 0xbb, 0x94, 0x8e, 0x33, 0x55, 0x1f, 0x21, 0x78, - 0xcd, 0x9a, 0xdd, 0x4f, 0x28, 0x8b, 0x5b, 0x1b, 0x2a, 0x17, 0xaf, 0xa6, 0x2c, 0x26, 0xc1, 0x69, - 0xe5, 0xc4, 0x3e, 0xde, 0x81, 0xa2, 0x4d, 0xed, 0xa7, 0xd4, 0x1e, 0x12, 0x3b, 0x98, 0xc3, 0xc6, - 0xa0, 0x61, 0x58, 0xe2, 0xda, 0x17, 0x4b, 0xfc, 0x62, 0xcc, 0xb5, 0xe3, 0xa7, 0xb0, 0x11, 0xcf, - 0xd1, 0x30, 0xe2, 0xdb, 0xbc, 0xe3, 0xa5, 0x53, 0x1d, 0x7f, 0x15, 0xe1, 0xf4, 0x7c, 0xec, 0xc2, - 0x49, 0x4a, 0xbf, 0x21, 0xc8, 0x9c, 0x0c, 0x44, 0xdb, 0xb0, 0x48, 0x34, 0x10, 0x83, 0xdf, 0x78, - 0x0b, 0x32, 0x86, 0x45, 0x7d, 0x9b, 0x15, 0x53, 0xfc, 0x0e, 0x86, 0x2b, 0xfc, 0x15, 0xa4, 0xe8, - 0x11, 0x9f, 0x66, 0xf9, 0xea, 0xee, 0x65, 0x87, 0xa4, 0xfa, 0x8c, 0x10, 0x87, 0xdf, 0xc5, 0x14, - 0x3d, 0xaa, 0x94, 0x61, 0x2d, 0x5a, 0xe3, 0x2c, 0xac, 0x7c, 0xbe, 0xfb, 0xfc, 0xa0, 0x5e, 0x58, - 0xc2, 0x6b, 0xb0, 0xfc, 0x4a, 0x7f, 0x5d, 0x2f, 0xa0, 0x92, 0x09, 0xb9, 0xa9, 0xa3, 0x83, 0x0b, - 0x90, 0x3e, 0x22, 0xa3, 0x30, 0xde, 0xe0, 0x27, 0xae, 0xc1, 0x8a, 0x28, 0x44, 0xea, 0x12, 0x03, - 0x41, 0xb8, 0xee, 0xa4, 0x1e, 0xa1, 0xd2, 0x33, 0xd8, 0x3a, 0xfb, 0xf4, 0x9c, 0xa1, 0x79, 0x3d, - 0xa9, 0x99, 0x4d, 0xb2, 0xfc, 0x10, 0xb1, 0xcc, 0x9e, 0x84, 0x33, 0x58, 0x1a, 0x49, 0x96, 0xab, - 0x3c, 0x3c, 0x27, 0xfa, 0xb5, 0x5c, 0x34, 0x0e, 0xf8, 0xd6, 0xb6, 0x0c, 0x52, 0x22, 0xdd, 0xa0, - 0xb0, 0x87, 0x75, 0x7d, 0xbf, 0xb0, 0x84, 0x57, 0x21, 0xbd, 0xdf, 0xa8, 0x17, 0x50, 0xf5, 0x6f, - 0x09, 0x6e, 0xce, 0xf2, 0x1e, 0x10, 0x77, 0x68, 0xb6, 0x09, 0x7e, 0x97, 0x86, 0xcc, 0x53, 0x37, - 0x18, 0x39, 0xf8, 0xfe, 0x85, 0x83, 0x2b, 0x5d, 0xdc, 0xa5, 0xf2, 0x7b, 0xea, 0xc7, 0xbf, 0xfe, - 0xfd, 0x39, 0xf5, 0x6b, 0xaa, 0xf2, 0x4b, 0x4a, 0x1b, 0xde, 0x8f, 0xbe, 0x7e, 0xce, 0xfa, 0xf6, - 0xd1, 0xc6, 0x89, 0x37, 0x76, 0xa2, 0x8d, 0x93, 0x0f, 0xea, 0x44, 0x1b, 0x27, 0x26, 0xed, 0x44, - 0xf3, 0x88, 0x63, 0xb8, 0x06, 0xa3, 0xae, 0x36, 0xf6, 0xa7, 0x0c, 0xe3, 0xc4, 0xcc, 0x9e, 0x68, - 0xe3, 0xa9, 0x41, 0x1f, 0xad, 0x13, 0xf6, 0x93, 0x27, 0x6e, 0xa2, 0x8d, 0x93, 0x03, 0xeb, 0x53, - 0x8f, 0xb9, 0x8e, 0x4b, 0xba, 0xe6, 0xb1, 0xb6, 0x3d, 0x11, 0x22, 0x09, 0x37, 0x6f, 0x96, 0xc7, - 0x9b, 0x15, 0xf2, 0x66, 0x1c, 0xa6, 0x83, 0x9c, 0x37, 0x0d, 0x26, 0xf8, 0x1d, 0x02, 0x10, 0x0d, - 0xaa, 0xd1, 0xce, 0xe8, 0x3d, 0x35, 0x69, 0x9b, 0xf7, 0xe8, 0x83, 0x4a, 0x79, 0x41, 0x87, 0x76, - 0xd0, 0x36, 0xfe, 0x1e, 0x32, 0xcf, 0x29, 0x3d, 0xf2, 0x1d, 0xbc, 0xa1, 0x06, 0x1f, 0x89, 0xea, - 0x17, 0x9d, 0x17, 0xe2, 0x33, 0xf1, 0x32, 0xca, 0x2a, 0x57, 0x56, 0xf0, 0x47, 0x0b, 0xcf, 0x46, - 0xf0, 0x65, 0x37, 0xc1, 0x3f, 0x21, 0xc8, 0xbc, 0x76, 0x3a, 0x97, 0x3c, 0xbf, 0x73, 0x1e, 0xd1, - 0xca, 0x7d, 0x1e, 0xc5, 0xc7, 0xa5, 0x73, 0x46, 0x11, 0x94, 0xc1, 0x80, 0xcc, 0x33, 0x32, 0x20, - 0x8c, 0x9c, 0x2e, 0xc3, 0x3c, 0x95, 0x30, 0xd7, 0xed, 0xf3, 0xe6, 0xfa, 0x27, 0x82, 0xe5, 0x7a, - 0xbb, 0x4f, 0xb1, 0x32, 0x27, 0x53, 0xcf, 0x6f, 0xa9, 0x62, 0xb4, 0x45, 0xd2, 0xe7, 0x46, 0x56, - 0xda, 0x3c, 0x98, 0xb7, 0xf8, 0xde, 0xa2, 0x60, 0x48, 0xbb, 0x4f, 0xb5, 0xb1, 0x38, 0xb8, 0x87, - 0xb7, 0x2a, 0x05, 0x6d, 0x58, 0x8d, 0xf1, 0x81, 0x6d, 0x47, 0x8c, 0xaa, 0x43, 0x8c, 0x4f, 0x99, - 0xf0, 0x1f, 0x08, 0xd6, 0x83, 0xd7, 0xe0, 0xa5, 0xc1, 0xfa, 0x3c, 0x93, 0xf7, 0x73, 0x9c, 0x9f, - 0xf0, 0xdc, 0x1e, 0x57, 0x1e, 0x2e, 0x2c, 0xf4, 0xd4, 0x3f, 0x13, 0x35, 0x78, 0x2b, 0x79, 0x73, - 0x3f, 0x03, 0x68, 0xd0, 0x9a, 0x69, 0x77, 0x4c, 0xbb, 0xe7, 0xe1, 0x39, 0xfd, 0x9c, 0xdb, 0xe7, - 0x25, 0xfc, 0x06, 0x56, 0x83, 0xb7, 0x9a, 0xfa, 0xec, 0xc2, 0xce, 0xb7, 0x79, 0xec, 0x37, 0xf0, - 0x66, 0xb2, 0x98, 0x4c, 0x90, 0x55, 0xbf, 0x85, 0x3b, 0xbb, 0x36, 0x65, 0x7d, 0xe2, 0x86, 0xf3, - 0xfc, 0x6b, 0x93, 0xf5, 0x13, 0x91, 0x5e, 0x31, 0xee, 0x9a, 0x74, 0x98, 0x8d, 0xcb, 0xda, 0xca, - 0x70, 0xf3, 0x83, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x59, 0xdb, 0x5f, 0x3e, 0xba, 0x0e, 0x00, - 0x00, + // 1278 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xbc, 0x57, 0xcf, 0x6e, 0xdb, 0x46, + 0x13, 0xf7, 0x4a, 0xb6, 0x2c, 0x8d, 0x24, 0x5b, 0x5e, 0x27, 0x8e, 0xa2, 0xe4, 0x83, 0xf8, 0x29, + 0x6d, 0x41, 0xb8, 0x01, 0x89, 0x28, 0x41, 0x91, 0x18, 0x68, 0x03, 0x2b, 0x51, 0x9b, 0xa2, 0x89, + 0x92, 0xd0, 0x49, 0x0a, 0x18, 0x49, 0x05, 0x4a, 0x5a, 0x49, 0x84, 0x45, 0x2e, 0x4b, 0x2e, 0x55, + 0x0b, 0xaa, 0x7a, 0xe8, 0xa1, 0x97, 0x1e, 0x7b, 0xcf, 0xa5, 0x40, 0xd1, 0x4b, 0x8f, 0x3d, 0xb7, + 0xef, 0xd0, 0x57, 0x68, 0xdf, 0xa3, 0xe0, 0x2e, 0x49, 0x53, 0xb2, 0x05, 0xf9, 0x4f, 0x91, 0x1b, + 0x77, 0x67, 0xe6, 0x37, 0xbf, 0x99, 0xd9, 0x99, 0x5d, 0xc2, 0x4d, 0x72, 0xa8, 0x9b, 0xf6, 0x80, + 0xb8, 0x6a, 0xf0, 0x61, 0xb7, 0x54, 0xbd, 0xd9, 0x32, 0x58, 0x93, 0x76, 0x9b, 0x64, 0x48, 0x9c, + 0x11, 0xeb, 0x1b, 0x56, 0x4f, 0xb1, 0x1d, 0xca, 0x28, 0x2e, 0xf7, 0x1c, 0xbb, 0xad, 0xf4, 0x74, + 0x46, 0xbe, 0xd1, 0x47, 0x4a, 0x68, 0xaa, 0x44, 0xa6, 0xa5, 0xeb, 0x3d, 0x4a, 0x7b, 0x03, 0xa2, + 0xea, 0xb6, 0xa1, 0xea, 0x96, 0x45, 0x99, 0xce, 0x0c, 0x6a, 0xb9, 0xc2, 0xbc, 0x74, 0x2d, 0x90, + 0xf2, 0x55, 0xcb, 0xeb, 0xaa, 0xc4, 0xb4, 0xd9, 0x28, 0x10, 0x96, 0x22, 0x26, 0xae, 0xd7, 0x52, + 0x4d, 0xe2, 0xba, 0x7a, 0x8f, 0x84, 0x86, 0x71, 0x59, 0x75, 0x46, 0x58, 0x9e, 0x45, 0x65, 0x86, + 0x49, 0x5c, 0xa6, 0x9b, 0xb6, 0x50, 0xa8, 0xfc, 0xb9, 0x0e, 0x85, 0xdd, 0x9a, 0xc1, 0x9e, 0x76, + 0xeb, 0x51, 0x40, 0xf8, 0x0d, 0xe4, 0x5d, 0xc3, 0xea, 0x0d, 0x48, 0xd3, 0x22, 0x2e, 0x23, 0x9d, + 0xe2, 0x55, 0x09, 0xc9, 0xd9, 0xea, 0x5d, 0x65, 0x41, 0x88, 0xca, 0x2c, 0x92, 0xd2, 0xe0, 0xf6, + 0x5a, 0x4e, 0xc0, 0x89, 0x15, 0xc6, 0xb0, 0xec, 0x79, 0x46, 0xa7, 0x88, 0x24, 0x24, 0x67, 0x34, + 0xfe, 0x8d, 0x9f, 0x41, 0x2a, 0xf0, 0x95, 0x90, 0x92, 0x17, 0xf2, 0x15, 0xe0, 0xe0, 0x32, 0x64, + 0xbb, 0x03, 0xaa, 0xb3, 0xe6, 0x50, 0x1f, 0x78, 0xa4, 0x98, 0x94, 0x90, 0x9c, 0xd0, 0x80, 0x6f, + 0xbd, 0xf2, 0x77, 0xf0, 0xff, 0x21, 0xd7, 0xa1, 0x5e, 0x6b, 0x40, 0x02, 0x8d, 0x65, 0x09, 0xc9, + 0x48, 0xcb, 0x8a, 0x3d, 0xa1, 0x52, 0x86, 0xac, 0x61, 0xb1, 0x8f, 0xee, 0x04, 0x1a, 0x2b, 0x12, + 0x92, 0x93, 0x1a, 0xf0, 0xad, 0x08, 0xc3, 0x8b, 0x6b, 0xa4, 0x24, 0x24, 0x2f, 0x6b, 0x59, 0x2f, + 0xa6, 0x22, 0x30, 0x6e, 0x57, 0x03, 0x8d, 0x55, 0x09, 0xc9, 0x2b, 0x1c, 0xe3, 0x76, 0x55, 0x28, + 0xdc, 0x80, 0x7c, 0xd7, 0x38, 0x24, 0x9d, 0x08, 0x24, 0x2d, 0x21, 0x39, 0xa5, 0xe5, 0x82, 0xcd, + 0x69, 0xa5, 0x08, 0x27, 0x23, 0x21, 0x79, 0x35, 0x50, 0x0a, 0x91, 0xfe, 0x07, 0xd0, 0xa2, 0x74, + 0x10, 0x68, 0x80, 0x84, 0xe4, 0xb4, 0x96, 0xf1, 0x77, 0x22, 0xb2, 0x2e, 0x73, 0x0c, 0xab, 0x17, + 0x28, 0x64, 0x79, 0xfe, 0xb3, 0x62, 0x6f, 0x2a, 0x9e, 0xc8, 0x4b, 0x5e, 0x42, 0x72, 0x5e, 0xc4, + 0x13, 0x3a, 0xf9, 0x02, 0x80, 0x58, 0x9e, 0x19, 0x28, 0xac, 0x49, 0x48, 0x5e, 0xab, 0xde, 0x5c, + 0x58, 0xad, 0x86, 0x67, 0x12, 0xc7, 0x68, 0xd7, 0x2d, 0xcf, 0xd4, 0x32, 0xbe, 0xbd, 0x00, 0x7b, + 0x1f, 0xd6, 0xdc, 0xe9, 0xb8, 0xd6, 0x25, 0x24, 0xaf, 0x6b, 0x79, 0x77, 0x2a, 0xb0, 0x48, 0x2d, + 0xca, 0x51, 0x41, 0x42, 0x72, 0x21, 0x54, 0x8b, 0x55, 0xc3, 0x8d, 0xb3, 0xdf, 0x90, 0x90, 0xbc, + 0xa1, 0x65, 0xdd, 0x18, 0xfb, 0x40, 0x25, 0xc2, 0xc1, 0x12, 0x92, 0xb1, 0x50, 0x09, 0x51, 0xaa, + 0x70, 0xd9, 0x21, 0x36, 0xd1, 0x19, 0xe9, 0x34, 0xa7, 0xf2, 0xb5, 0x29, 0x25, 0xe5, 0x8c, 0xb6, + 0x19, 0x0a, 0xf7, 0x62, 0x79, 0xbb, 0x07, 0x59, 0x6a, 0x11, 0x7f, 0x2c, 0xf8, 0x5d, 0x5b, 0xbc, + 0xc4, 0xfb, 0x65, 0x4b, 0x11, 0xdd, 0xa7, 0x84, 0xdd, 0xa7, 0xd4, 0x7d, 0xe9, 0xa3, 0x25, 0x0d, + 0xb8, 0x32, 0x5f, 0xe1, 0x1b, 0x90, 0x13, 0xa6, 0xc2, 0x57, 0xf1, 0xb2, 0x5f, 0x95, 0x47, 0x4b, + 0x9a, 0x00, 0x14, 0x4e, 0xf0, 0x6b, 0xc8, 0x98, 0xba, 0x1d, 0xf0, 0xd8, 0xe2, 0x1d, 0x72, 0xff, + 0xec, 0x1d, 0xf2, 0x44, 0xb7, 0x39, 0xdd, 0xba, 0xc5, 0x9c, 0x91, 0x96, 0x36, 0x83, 0x25, 0x3e, + 0x84, 0x4d, 0x53, 0xb7, 0xed, 0xd9, 0x78, 0xaf, 0x70, 0x3f, 0x8f, 0xce, 0xe5, 0xc7, 0x9e, 0xca, + 0x8f, 0x70, 0xb8, 0x61, 0xce, 0xee, 0xc7, 0x3c, 0x8b, 0xae, 0x0d, 0x3c, 0x17, 0x2f, 0xe6, 0x59, + 0x4c, 0x82, 0xe3, 0x9e, 0x63, 0xfb, 0x78, 0x07, 0x8a, 0x16, 0xb5, 0x1e, 0x50, 0x6b, 0x48, 0x2c, + 0x7f, 0x0e, 0xeb, 0x83, 0x86, 0x6e, 0x8a, 0xb6, 0x2f, 0x96, 0x78, 0x63, 0xcc, 0x95, 0xe3, 0x07, + 0xb0, 0x1e, 0xcd, 0xd1, 0x80, 0xf1, 0x35, 0x5e, 0xf1, 0xd2, 0xb1, 0x8a, 0xbf, 0x08, 0xf5, 0xb4, + 0xb5, 0xc8, 0x44, 0x80, 0xbc, 0x86, 0xe8, 0x24, 0x35, 0x63, 0x0d, 0x75, 0x5d, 0x4a, 0x9e, 0xb9, + 0xa1, 0x36, 0x42, 0xa0, 0x7a, 0xd8, 0x58, 0xa5, 0x5f, 0x11, 0xa4, 0x8e, 0xc6, 0xad, 0xa5, 0x9b, + 0x24, 0x1c, 0xb7, 0xfe, 0x37, 0xde, 0x82, 0x94, 0x6e, 0x52, 0xcf, 0x62, 0xc5, 0x04, 0xef, 0xf0, + 0x60, 0x85, 0x9f, 0x43, 0x82, 0x1e, 0xf0, 0x59, 0xb9, 0x56, 0xdd, 0x3d, 0xef, 0x08, 0x56, 0x1e, + 0x12, 0x62, 0x73, 0x62, 0x09, 0x7a, 0x50, 0x29, 0x43, 0x3a, 0x5c, 0xe3, 0x0c, 0xac, 0x7c, 0xba, + 0xfb, 0x78, 0xaf, 0x5e, 0x58, 0xc2, 0x69, 0x58, 0x7e, 0xa1, 0xbd, 0xac, 0x17, 0x50, 0xc9, 0x80, + 0xfc, 0xd4, 0xc1, 0xc4, 0x05, 0x48, 0x1e, 0x90, 0x51, 0xc0, 0xd7, 0xff, 0xc4, 0x35, 0x58, 0x11, + 0xd9, 0x49, 0x9c, 0x63, 0xdc, 0x08, 0xd3, 0x9d, 0xc4, 0x5d, 0x54, 0x7a, 0x08, 0x5b, 0x27, 0x9f, + 0xcd, 0x13, 0x7c, 0x5e, 0x8a, 0xfb, 0xcc, 0xc4, 0x51, 0xbe, 0x0b, 0x51, 0x66, 0xcf, 0xd9, 0x09, + 0x28, 0x8d, 0x38, 0xca, 0x45, 0xae, 0xb5, 0x23, 0xff, 0xb5, 0x7c, 0x38, 0x6c, 0xf8, 0xd6, 0xb6, + 0x04, 0xd9, 0x58, 0xb8, 0x7e, 0x62, 0xf7, 0xeb, 0xda, 0xd3, 0xc2, 0x12, 0x5e, 0x85, 0xe4, 0xd3, + 0x46, 0xbd, 0x80, 0xaa, 0xff, 0xe4, 0xe0, 0xca, 0x2c, 0xee, 0x1e, 0x71, 0x86, 0x46, 0x9b, 0xe0, + 0xb7, 0x49, 0x48, 0x3d, 0x70, 0xfc, 0xd3, 0x83, 0x6f, 0x9d, 0x99, 0x5c, 0xe9, 0xec, 0x26, 0x95, + 0xdf, 0x12, 0xdf, 0xff, 0xf5, 0xf7, 0x4f, 0x89, 0x5f, 0x12, 0x95, 0x9f, 0x13, 0xea, 0xf0, 0x56, + 0xf8, 0xb6, 0x3a, 0xe9, 0x65, 0xa5, 0x8e, 0x63, 0x37, 0xf8, 0x44, 0x1d, 0xc7, 0xaf, 0xeb, 0x89, + 0x3a, 0x8e, 0xcd, 0xf1, 0x89, 0xea, 0x12, 0x5b, 0x77, 0x74, 0x46, 0x1d, 0x75, 0xec, 0x4d, 0x09, + 0xc6, 0xb1, 0x1b, 0x61, 0xa2, 0x8e, 0xa7, 0xae, 0x91, 0x70, 0x1d, 0x93, 0x1f, 0x5d, 0xa0, 0x13, + 0x75, 0x1c, 0x1f, 0x87, 0x1f, 0xbb, 0xcc, 0xb1, 0x1d, 0xd2, 0x35, 0x0e, 0xd5, 0xed, 0x89, 0x70, + 0x12, 0x33, 0x73, 0x67, 0x71, 0xdc, 0x59, 0x47, 0xee, 0x8c, 0xc1, 0x34, 0xc9, 0x79, 0xb3, 0x66, + 0x82, 0xdf, 0x22, 0x00, 0x51, 0xa0, 0x1a, 0xed, 0x8c, 0xde, 0x51, 0x91, 0xb6, 0x79, 0x8d, 0xde, + 0xab, 0x94, 0x17, 0x54, 0x68, 0x07, 0x6d, 0xe3, 0x6f, 0x21, 0xf5, 0x98, 0xd2, 0x03, 0xcf, 0xc6, + 0xeb, 0x8a, 0xff, 0x04, 0x55, 0x3e, 0xef, 0x3c, 0x11, 0x8f, 0xd0, 0xf3, 0x78, 0x56, 0xb8, 0x67, + 0x19, 0x7f, 0xb0, 0xf0, 0x6c, 0xf8, 0xef, 0xc6, 0x09, 0xfe, 0x01, 0x41, 0xea, 0xa5, 0xdd, 0x39, + 0xe7, 0xf9, 0x9d, 0x73, 0x45, 0x57, 0x6e, 0x71, 0x16, 0x1f, 0x96, 0x4e, 0xc9, 0xc2, 0x4f, 0x83, + 0x0e, 0xa9, 0x87, 0x64, 0x40, 0x18, 0x39, 0x9e, 0x86, 0x79, 0x5e, 0x82, 0x58, 0xb7, 0x4f, 0x1b, + 0xeb, 0x8f, 0x08, 0xd2, 0x9f, 0x11, 0xf6, 0xdc, 0x23, 0xce, 0xe8, 0xbf, 0x8c, 0xf6, 0x0e, 0xe7, + 0xa1, 0xe0, 0x9b, 0x8b, 0x78, 0x7c, 0xed, 0x7b, 0x0e, 0xd9, 0xfc, 0x81, 0x60, 0xb9, 0xde, 0xee, + 0x53, 0x2c, 0xcf, 0x61, 0xe2, 0x7a, 0x2d, 0x45, 0x0c, 0xda, 0x30, 0x11, 0xa7, 0xd6, 0xac, 0xb4, + 0x39, 0xa5, 0x37, 0x8b, 0x29, 0x91, 0x76, 0x9f, 0xaa, 0x63, 0xd1, 0x46, 0xfb, 0x57, 0x2b, 0x05, + 0x75, 0x58, 0x8d, 0xf4, 0x7d, 0xd9, 0x8e, 0x18, 0x9c, 0xfb, 0x18, 0x1f, 0x13, 0xe1, 0xdf, 0x11, + 0xe4, 0xfc, 0xbb, 0xe9, 0x99, 0xce, 0xfa, 0x3c, 0x92, 0x77, 0xd3, 0x5c, 0xf7, 0x79, 0x6c, 0xf7, + 0x2a, 0x77, 0x16, 0x96, 0x7d, 0xea, 0x2f, 0x4c, 0xf1, 0x6f, 0x6e, 0x7e, 0xd4, 0x3e, 0x01, 0x68, + 0xd0, 0x9a, 0x61, 0x75, 0x0c, 0xab, 0xe7, 0xe2, 0x39, 0x55, 0x9d, 0x5b, 0xed, 0x25, 0xfc, 0x0a, + 0x56, 0xfd, 0x77, 0x09, 0xf5, 0xd8, 0x99, 0x8d, 0xaf, 0x71, 0xee, 0x97, 0xf1, 0x66, 0x3c, 0x99, + 0x4c, 0x80, 0x55, 0xbf, 0x82, 0xeb, 0xbb, 0x16, 0x65, 0x7d, 0xe2, 0x04, 0xb7, 0xcb, 0x97, 0x06, + 0xeb, 0xc7, 0x98, 0x5e, 0x90, 0x77, 0x2d, 0xbb, 0x9f, 0x89, 0xd2, 0xda, 0x4a, 0x71, 0xf1, 0xed, + 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xf7, 0xb8, 0x85, 0xa6, 0x0f, 0x00, 0x00, } diff --git a/examples/examplepb/a_bit_of_everything.pb.gw.go b/examples/examplepb/a_bit_of_everything.pb.gw.go index 213f5415fcd..f8f3eabd0fc 100644 --- a/examples/examplepb/a_bit_of_everything.pb.gw.go +++ b/examples/examplepb/a_bit_of_everything.pb.gw.go @@ -317,6 +317,41 @@ func request_ABitOfEverythingService_Delete_0(ctx context.Context, marshaler run } +var ( + filter_ABitOfEverythingService_GetQuery_0 = &utilities.DoubleArray{Encoding: map[string]int{"uuid": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}} +) + +func request_ABitOfEverythingService_GetQuery_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ABitOfEverything + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["uuid"] + if !ok { + return nil, metadata, grpc.Errorf(codes.InvalidArgument, "missing parameter %s", "uuid") + } + + protoReq.Uuid, err = runtime.String(val) + + if err != nil { + return nil, metadata, err + } + + if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_ABitOfEverythingService_GetQuery_0); err != nil { + return nil, metadata, grpc.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.GetQuery(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + func request_ABitOfEverythingService_Echo_0(ctx context.Context, marshaler runtime.Marshaler, client ABitOfEverythingServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq sub.StringMessage var metadata runtime.ServerMetadata @@ -584,6 +619,34 @@ func RegisterABitOfEverythingServiceHandler(ctx context.Context, mux *runtime.Se }) + mux.Handle("GET", pattern_ABitOfEverythingService_GetQuery_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + if cn, ok := w.(http.CloseNotifier); ok { + go func(done <-chan struct{}, closed <-chan bool) { + select { + case <-done: + case <-closed: + cancel() + } + }(ctx.Done(), cn.CloseNotify()) + } + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, req) + if err != nil { + runtime.HTTPError(ctx, outboundMarshaler, w, req, err) + } + resp, md, err := request_ABitOfEverythingService_GetQuery_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, outboundMarshaler, w, req, err) + return + } + + forward_ABitOfEverythingService_GetQuery_0(ctx, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("GET", pattern_ABitOfEverythingService_Echo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -738,6 +801,8 @@ var ( pattern_ABitOfEverythingService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"v1", "example", "a_bit_of_everything", "uuid"}, "")) + pattern_ABitOfEverythingService_GetQuery_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "example", "a_bit_of_everything", "query", "uuid"}, "")) + pattern_ABitOfEverythingService_Echo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"v1", "example", "a_bit_of_everything", "echo", "value"}, "")) pattern_ABitOfEverythingService_Echo_1 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v2", "example", "echo"}, "")) @@ -760,6 +825,8 @@ var ( forward_ABitOfEverythingService_Delete_0 = runtime.ForwardResponseMessage + forward_ABitOfEverythingService_GetQuery_0 = runtime.ForwardResponseMessage + forward_ABitOfEverythingService_Echo_0 = runtime.ForwardResponseMessage forward_ABitOfEverythingService_Echo_1 = runtime.ForwardResponseMessage diff --git a/examples/examplepb/a_bit_of_everything.proto b/examples/examplepb/a_bit_of_everything.proto index 292803fedd9..7ff55a4a0c6 100644 --- a/examples/examplepb/a_bit_of_everything.proto +++ b/examples/examplepb/a_bit_of_everything.proto @@ -58,6 +58,9 @@ message ABitOfEverything { string nonConventionalNameValue = 26; google.protobuf.Timestamp timestamp_value = 27; + + // repeated enum value. it is comma-separated in query + repeated NumericEnum repeated_enum_value = 28; } // NumericEnum is one or zero. @@ -97,6 +100,11 @@ service ABitOfEverythingService { delete: "/v1/example/a_bit_of_everything/{uuid}" }; } + rpc GetQuery(ABitOfEverything) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/v1/example/a_bit_of_everything/query/{uuid}" + }; + } rpc Echo(grpc.gateway.examples.sub.StringMessage) returns (grpc.gateway.examples.sub.StringMessage) { option (google.api.http) = { get: "/v1/example/a_bit_of_everything/echo/{value}" diff --git a/examples/examplepb/a_bit_of_everything.swagger.json b/examples/examplepb/a_bit_of_everything.swagger.json index f7c7f6c9a59..7f73c164dec 100644 --- a/examples/examplepb/a_bit_of_everything.swagger.json +++ b/examples/examplepb/a_bit_of_everything.swagger.json @@ -57,8 +57,209 @@ "name": "value", "in": "path", "required": true, + "type": "string" + } + ], + "tags": [ + "ABitOfEverythingService" + ] + } + }, + "/v1/example/a_bit_of_everything/query/{uuid}": { + "get": { + "operationId": "GetQuery", + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/protobufEmpty" + } + } + }, + "parameters": [ + { + "name": "uuid", + "in": "path", + "required": true, + "type": "string" + }, + { + "name": "single_nested.name", + "description": "name is nested field.", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "single_nested.amount", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "single_nested.ok", + "description": " - FALSE: FALSE is false.\n - TRUE: TRUE is true.", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "FALSE", + "TRUE" + ], + "default": "FALSE" + }, + { + "name": "float_value", + "in": "query", + "required": false, + "type": "number", + "format": "float" + }, + { + "name": "double_value", + "in": "query", + "required": false, + "type": "number", + "format": "double" + }, + { + "name": "int64_value", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "uint64_value", + "in": "query", + "required": false, + "type": "string", + "format": "uint64" + }, + { + "name": "int32_value", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "fixed64_value", + "in": "query", + "required": false, + "type": "string", + "format": "uint64" + }, + { + "name": "fixed32_value", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "bool_value", + "in": "query", + "required": false, + "type": "boolean", + "format": "boolean" + }, + { + "name": "string_value", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "uint32_value", + "description": "TODO(yugui) add bytes_value.", + "in": "query", + "required": false, + "type": "integer", + "format": "int64" + }, + { + "name": "enum_value", + "description": " - ZERO: ZERO means 0\n - ONE: ONE means 1", + "in": "query", + "required": false, + "type": "string", + "enum": [ + "ZERO", + "ONE" + ], + "default": "ZERO" + }, + { + "name": "sfixed32_value", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "sfixed64_value", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "sint32_value", + "in": "query", + "required": false, + "type": "integer", + "format": "int32" + }, + { + "name": "sint64_value", + "in": "query", + "required": false, + "type": "string", + "format": "int64" + }, + { + "name": "repeated_string_value", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string" + } + }, + { + "name": "oneof_string", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "nonConventionalNameValue", + "in": "query", + "required": false, + "type": "string" + }, + { + "name": "timestamp_value", + "in": "query", + "required": false, "type": "string", - "format": "string" + "format": "date-time" + }, + { + "name": "repeated_enum_value", + "description": "repeated enum value. it is comma-separated in query.\n\n - ZERO: ZERO means 0\n - ONE: ONE means 1", + "in": "query", + "required": false, + "type": "array", + "items": { + "type": "string", + "enum": [ + "ZERO", + "ONE" + ] + } } ], "tags": [ @@ -138,8 +339,7 @@ "name": "string_value", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" }, { "name": "uint32_value", @@ -180,8 +380,7 @@ "name": "nonConventionalNameValue", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" } ], "tags": [ @@ -205,8 +404,7 @@ "name": "single_nested.name", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" }, { "name": "body", @@ -238,8 +436,7 @@ "name": "uuid", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" } ], "tags": [ @@ -261,8 +458,7 @@ "name": "uuid", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" } ], "tags": [ @@ -284,8 +480,7 @@ "name": "uuid", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" }, { "name": "body", @@ -340,8 +535,7 @@ "in": "body", "required": true, "schema": { - "type": "string", - "format": "string" + "type": "string" } } ], @@ -373,7 +567,6 @@ "properties": { "name": { "type": "string", - "format": "string", "description": "name is nested field." }, "amount": { @@ -402,8 +595,7 @@ "$ref": "#/definitions/ABitOfEverythingNested" }, "uuid": { - "type": "string", - "format": "string" + "type": "string" }, "nested": { "type": "array", @@ -444,8 +636,7 @@ "format": "boolean" }, "string_value": { - "type": "string", - "format": "string" + "type": "string" }, "uint32_value": { "type": "integer", @@ -474,16 +665,14 @@ "repeated_string_value": { "type": "array", "items": { - "type": "string", - "format": "string" + "type": "string" } }, "oneof_empty": { "$ref": "#/definitions/protobufEmpty" }, "oneof_string": { - "type": "string", - "format": "string" + "type": "string" }, "map_value": { "type": "object", @@ -494,8 +683,7 @@ "mapped_string_value": { "type": "object", "additionalProperties": { - "type": "string", - "format": "string" + "type": "string" } }, "mapped_nested_value": { @@ -505,12 +693,18 @@ } }, "nonConventionalNameValue": { - "type": "string", - "format": "string" + "type": "string" }, "timestamp_value": { "type": "string", "format": "date-time" + }, + "repeated_enum_value": { + "type": "array", + "items": { + "$ref": "#/definitions/examplepbNumericEnum" + }, + "title": "repeated enum value. it is comma-separated in query" } }, "title": "Intentionaly complicated message type to cover much features of Protobuf.\nNEXT ID: 27" @@ -533,8 +727,7 @@ "type": "object", "properties": { "uuid": { - "type": "string", - "format": "string" + "type": "string" } } }, @@ -542,8 +735,7 @@ "type": "object", "properties": { "value": { - "type": "string", - "format": "string" + "type": "string" } } } diff --git a/examples/examplepb/echo_service.swagger.json b/examples/examplepb/echo_service.swagger.json index f6c6d8b9814..9380472a3f9 100644 --- a/examples/examplepb/echo_service.swagger.json +++ b/examples/examplepb/echo_service.swagger.json @@ -34,8 +34,7 @@ "name": "id", "in": "path", "required": true, - "type": "string", - "format": "string" + "type": "string" } ], "tags": [ @@ -77,7 +76,6 @@ "properties": { "id": { "type": "string", - "format": "string", "description": "Id represents the message identifier." } }, diff --git a/examples/server/a_bit_of_everything.go b/examples/server/a_bit_of_everything.go index 88d39bb655c..5f48e797419 100644 --- a/examples/server/a_bit_of_everything.go +++ b/examples/server/a_bit_of_everything.go @@ -170,6 +170,19 @@ func (s *_ABitOfEverythingServer) Delete(ctx context.Context, msg *sub2.IdMessag return new(empty.Empty), nil } +func (s *_ABitOfEverythingServer) GetQuery(ctx context.Context, msg *examples.ABitOfEverything) (*empty.Empty, error) { + s.m.Lock() + defer s.m.Unlock() + + glog.Info(msg) + if _, ok := s.v[msg.Uuid]; ok { + s.v[msg.Uuid] = msg + } else { + return nil, grpc.Errorf(codes.NotFound, "not found") + } + return new(empty.Empty), nil +} + func (s *_ABitOfEverythingServer) Echo(ctx context.Context, msg *sub.StringMessage) (*sub.StringMessage, error) { s.m.Lock() defer s.m.Unlock() diff --git a/protoc-gen-swagger/genswagger/template.go b/protoc-gen-swagger/genswagger/template.go index 653125ae9d2..022b649fb9b 100644 --- a/protoc-gen-swagger/genswagger/template.go +++ b/protoc-gen-swagger/genswagger/template.go @@ -13,47 +13,108 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" ) +func listEnumNames(enum *descriptor.Enum) (names []string) { + for _, value := range enum.GetValue() { + names = append(names, value.GetName()) + } + return names +} + +func getEnumDefault(enum *descriptor.Enum) string { + for _, value := range enum.GetValue() { + if value.GetNumber() == 0 { + return value.GetName() + } + } + return "" +} + // messageToQueryParameters converts a message to a list of swagger query parameters. -func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter) ([]swaggerParameterObject, error) { - var parameters []swaggerParameterObject - var addParameterWithPrefix func(string, *descriptor.Field) error - addParameterWithPrefix = func(prefix string, field *descriptor.Field) error { - // make sure the parameter is not already listed as a path parameter - for _, pathParam := range pathParams { - if pathParam.Target == field { - return nil - } +func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter) (params []swaggerParameterObject, err error) { + for _, field := range message.Fields { + p, err := queryParams(message, field, "", reg, pathParams) + if err != nil { + return nil, err } - schema := schemaOfField(field, reg) - if schema.Type != "" { - // basic type, add a basic query parameter - parameters = append(parameters, swaggerParameterObject{ - Name: prefix + field.GetName(), - Description: schema.Description, - In: "query", - Type: schema.Type, - }) - return nil + params = append(params, p...) + } + return params, nil +} + +// queryParams converts a field to a list of swagger query parameters recuresively. +func queryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter) (params []swaggerParameterObject, err error) { + // make sure the parameter is not already listed as a path parameter + for _, pathParam := range pathParams { + if pathParam.Target == field { + return nil, nil + } + } + schema := schemaOfField(field, reg) + fieldType := field.GetTypeName() + if message.File != nil { + comments := fieldProtoComments(reg, message, field) + if err := updateSwaggerDataFromComments(&schema, comments); err != nil { + return nil, err } + } - // nested type, recurse - msg, err := reg.LookupMsg("", field.GetTypeName()) - if err != nil { - return fmt.Errorf("unknown message type %s", field.GetTypeName()) + isEnum := field.GetType() == pbdescriptor.FieldDescriptorProto_TYPE_ENUM + items := schema.Items + if schema.Type != "" || isEnum { + if schema.Type == "object" { + return nil, nil // TODO: currently, mapping object in query parameter is not supported } - for _, nestedField := range msg.Fields { - if err := addParameterWithPrefix(field.GetName()+".", nestedField); err != nil { - return err + if items != nil && (items.Type == "" || items.Type == "object") && !isEnum { + return nil, nil // TODO: currently, mapping object in query parameter is not supported + } + desc := schema.Description + if schema.Title != "" { // merge title because title of parameter object will be ignored + desc = strings.TrimSpace(schema.Title + ". " + schema.Description) + } + param := swaggerParameterObject{ + Name: prefix + field.GetName(), + Description: desc, + In: "query", + Type: schema.Type, + Items: schema.Items, + Format: schema.Format, + } + if isEnum { + enum, err := reg.LookupEnum("", fieldType) + if err != nil { + return nil, fmt.Errorf("unknown enum type %s", fieldType) + } + if items != nil { // array + param.Items = &swaggerItemsObject{ + Type: "string", + Enum: listEnumNames(enum), + } + } else { + param.Type = "string" + param.Enum = listEnumNames(enum) + param.Default = getEnumDefault(enum) + } + valueComments := enumValueProtoComments(reg, enum) + if valueComments != "" { + param.Description = strings.TrimLeft(param.Description+"\n\n "+valueComments, "\n") } } - return nil + return []swaggerParameterObject{param}, nil } - for _, field := range message.Fields { - if err := addParameterWithPrefix("", field); err != nil { - return parameters, err + + // nested type, recurse + msg, err := reg.LookupMsg("", fieldType) + if err != nil { + return nil, fmt.Errorf("unknown message type %s", fieldType) + } + for _, nestedField := range msg.Fields { + p, err := queryParams(msg, nestedField, prefix+field.GetName()+".", reg, pathParams) + if err != nil { + return nil, err } + params = append(params, p...) } - return parameters, nil + return params, nil } // findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service. @@ -111,12 +172,10 @@ func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, panic(err) } - for i, f := range msg.Fields { + for _, f := range msg.Fields { fieldValue := schemaOfField(f, reg) - - fieldProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "Field") - fieldProtoComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), fieldProtoPath, int32(i)) - if err := updateSwaggerDataFromComments(&fieldValue, fieldProtoComments); err != nil { + comments := fieldProtoComments(reg, msg, f) + if err := updateSwaggerDataFromComments(&fieldValue, comments); err != nil { panic(err) } @@ -173,9 +232,9 @@ func schemaOfField(f *descriptor.Field, reg *descriptor.Registry) swaggerSchemaO case array: return swaggerSchemaObject{ schemaCore: schemaCore{ - Type: "array", + Type: "array", + Items: (*swaggerItemsObject)(&core), }, - Items: (*swaggerItemsObject)(&core), } case object: return swaggerSchemaObject{ @@ -218,7 +277,8 @@ func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format st case pbdescriptor.FieldDescriptorProto_TYPE_BOOL: return "boolean", "boolean", true case pbdescriptor.FieldDescriptorProto_TYPE_STRING: - return "string", "string", true + // NOTE: in swagger specifition, format should be empty on string type + return "string", "", true case pbdescriptor.FieldDescriptorProto_TYPE_BYTES: return "string", "byte", true case pbdescriptor.FieldDescriptorProto_TYPE_UINT32: @@ -239,35 +299,22 @@ func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format st // renderEnumerationsAsDefinition inserts enums into the definitions object. func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, reg *descriptor.Registry) { - valueProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.EnumDescriptorProto)(nil)), "Value") for _, enum := range enums { enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index)) - var enumNames []string // it may be necessary to sort the result of the GetValue function. - var defaultValue string - var valueDescriptions []string - for valueIdx, value := range enum.GetValue() { - enumNames = append(enumNames, value.GetName()) - if defaultValue == "" && value.GetNumber() == 0 { - defaultValue = value.GetName() - } - - valueDescription := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), valueProtoPath, int32(valueIdx)) - if valueDescription != "" { - valueDescriptions = append(valueDescriptions, value.GetName()+": "+valueDescription) - } - } - - if len(valueDescriptions) > 0 { - enumComments += "\n\n - " + strings.Join(valueDescriptions, "\n - ") + enumNames := listEnumNames(enum) + defaultValue := getEnumDefault(enum) + valueComments := enumValueProtoComments(reg, enum) + if valueComments != "" { + enumComments = strings.TrimLeft(enumComments+"\n\n "+valueComments, "\n") } enumSchemaObject := swaggerSchemaObject{ schemaCore: schemaCore{ - Type: "string", + Type: "string", + Enum: enumNames, + Default: defaultValue, }, - Enum: enumNames, - Default: defaultValue, } if err := updateSwaggerDataFromComments(&enumSchemaObject, enumComments); err != nil { panic(err) @@ -528,7 +575,9 @@ func applyTemplate(p param) (string, error) { // Loops through all the services and their exposed GET/POST/PUT/DELETE definitions // and create entries for all of them. - renderServices(p.Services, s.Paths, p.reg) + if err := renderServices(p.Services, s.Paths, p.reg); err != nil { + panic(err) + } // Find all the service's messages and enumerations that are defined (recursively) and then // write their request and response types out as definition objects. @@ -620,6 +669,32 @@ func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) er return fmt.Errorf("no description nor summary property") } +func fieldProtoComments(reg *descriptor.Registry, msg *descriptor.Message, field *descriptor.Field) string { + protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "Field") + for i, f := range msg.Fields { + if f == field { + return protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), protoPath, int32(i)) + } + } + return "" +} + +func enumValueProtoComments(reg *descriptor.Registry, enum *descriptor.Enum) string { + protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.EnumDescriptorProto)(nil)), "Value") + var comments []string + for idx, value := range enum.GetValue() { + name := value.GetName() + str := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), protoPath, int32(idx)) + if str != "" { + comments = append(comments, name+": "+str) + } + } + if len(comments) > 0 { + return "- " + strings.Join(comments, "\n - ") + } + return "" +} + func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string { if file.SourceCodeInfo == nil { // Curious! A file without any source code info. diff --git a/protoc-gen-swagger/genswagger/template_test.go b/protoc-gen-swagger/genswagger/template_test.go index fb18ca9ab29..e0b25c16716 100644 --- a/protoc-gen-swagger/genswagger/template_test.go +++ b/protoc-gen-swagger/genswagger/template_test.go @@ -70,6 +70,7 @@ func TestMessageToQueryParameters(t *testing.T) { In: "query", Required: false, Type: "number", + Format: "double", }, }, }, @@ -94,7 +95,38 @@ func TestMessageToQueryParameters(t *testing.T) { Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(), Number: proto.Int32(1), }, + { + Name: proto.String("deep"), + Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(), + TypeName: proto.String(".example.Nested.DeepNested"), + Number: proto.Int32(2), + }, }, + NestedType: []*protodescriptor.DescriptorProto{{ + Name: proto.String("DeepNested"), + Field: []*protodescriptor.FieldDescriptorProto{ + { + Name: proto.String("b"), + Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(), + Number: proto.Int32(1), + }, + { + Name: proto.String("c"), + Type: protodescriptor.FieldDescriptorProto_TYPE_ENUM.Enum(), + TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"), + Number: proto.Int32(2), + }, + }, + EnumType: []*protodescriptor.EnumDescriptorProto{ + { + Name: proto.String("DeepEnum"), + Value: []*protodescriptor.EnumValueDescriptorProto{ + {Name: proto.String("FALSE"), Number: proto.Int32(0)}, + {Name: proto.String("TRUE"), Number: proto.Int32(1)}, + }, + }, + }, + }}, }, }, Message: "ExampleMessage", @@ -105,6 +137,20 @@ func TestMessageToQueryParameters(t *testing.T) { Required: false, Type: "string", }, + swaggerParameterObject{ + Name: "nested.deep.b", + In: "query", + Required: false, + Type: "string", + }, + swaggerParameterObject{ + Name: "nested.deep.c", + In: "query", + Required: false, + Type: "string", + Enum: []string{"FALSE", "TRUE"}, + Default: "FALSE", + }, }, }, } diff --git a/protoc-gen-swagger/genswagger/types.go b/protoc-gen-swagger/genswagger/types.go index 80b684475cc..3c6a5c9f0de 100644 --- a/protoc-gen-swagger/genswagger/types.go +++ b/protoc-gen-swagger/genswagger/types.go @@ -95,6 +95,8 @@ type swaggerParameterObject struct { Type string `json:"type,omitempty"` Format string `json:"format,omitempty"` Items *swaggerItemsObject `json:"items,omitempty"` + Enum []string `json:"enum,omitempty"` + Default string `json:"default,omitempty"` // Or you can explicitly refer to another type. If this is defined all // other fields should be empty @@ -107,6 +109,14 @@ type schemaCore struct { Type string `json:"type,omitempty"` Format string `json:"format,omitempty"` Ref string `json:"$ref,omitempty"` + + Items *swaggerItemsObject `json:"items,omitempty"` + + // If the item is an enumeration include a list of all the *NAMES* of the + // enum values. I'm not sure how well this will work but assuming all enums + // start from 0 index it will be great. I don't think that is a good assumption. + Enum []string `json:"enum,omitempty"` + Default string `json:"default,omitempty"` } type swaggerItemsObject schemaCore @@ -157,13 +167,6 @@ type swaggerSchemaObject struct { // Properties can be recursively defined Properties swaggerSchemaObjectProperties `json:"properties,omitempty"` AdditionalProperties *swaggerSchemaObject `json:"additionalProperties,omitempty"` - Items *swaggerItemsObject `json:"items,omitempty"` - - // If the item is an enumeration include a list of all the *NAMES* of the - // enum values. I'm not sure how well this will work but assuming all enums - // start from 0 index it will be great. I don't think that is a good assumption. - Enum []string `json:"enum,omitempty"` - Default string `json:"default,omitempty"` Description string `json:"description,omitempty"` Title string `json:"title,omitempty"`