Skip to content

Commit ab0345b

Browse files
Marcus Rosénjohanbrandhorst
authored andcommitted
Added support for path param repeated primitive fields
Added conversion functions for repeated path params Updated bytes converter to support URL encoded base64 strings Added support for repeated primitive path params in protoc-gen-grpc-gateway Added support for repeated primitive path params in protoc-gen-swagger Added --repeated_path_param_separator cmd line param to support setting separator to `csv`, `ssv`, `pipes` and `tsv` Re-generated examples Added ABitOfEverythingRepeated to validate repeated path parameter functionality Added GetRepeatedQuery rpc endpoint Updated browser tests to test GetRepeatedQuery rpc endpoint Updated integration tests to test GetRepeatedQuery rpc endpoint Added GetRepeatedQuery to ABitOfEverythingServer implementation Added missing reflect.DeepEqual test Change separator type from string to rune Fixed slice duplication in string slice conversion method Reverted --allowRepeatedFieldsInBody in swagger generator Changed TODO of bytes slice proto2 function Corrected if-statement releated to repeated fields in resolveFieldPath function Rebase
1 parent 0e59487 commit ab0345b

20 files changed

+1651
-200
lines changed

examples/browser/a_bit_of_everything_service.spec.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,41 @@ describe('ABitOfEverythingService', function() {
187187
}).finally(done);
188188
});
189189
});
190+
191+
describe('GetRepeatedQuery', function() {
192+
var repeated;
193+
var expected = {
194+
path_repeated_float_value: [1.5, -1.5],
195+
path_repeated_double_value: [2.5, -2.5],
196+
path_repeated_int64_value: ["4294967296", "-4294967296"],
197+
path_repeated_uint64_value: ["0", "9223372036854775807"],
198+
path_repeated_int32_value: [2147483647, -2147483648],
199+
path_repeated_fixed64_value: ["0", "9223372036854775807"],
200+
path_repeated_fixed32_value: [0, 4294967295],
201+
path_repeated_bool_value: [true, false],
202+
path_repeated_string_value: ["foo", "bar"],
203+
path_repeated_bytes_value: ["AA==", "_w=="],
204+
path_repeated_uint32_value: [4294967295, 0],
205+
path_repeated_enum_value: ["ONE", "ONE"],
206+
path_repeated_sfixed32_value: [-2147483648, 2147483647],
207+
path_repeated_sfixed64_value: ["-4294967296", "4294967296"],
208+
path_repeated_sint32_value: [2147483646, -2147483647],
209+
path_repeated_sint64_value: ["4611686018427387903", "-4611686018427387904"]
210+
};
211+
212+
beforeEach(function(done) {
213+
client.ABitOfEverythingService.GetRepeatedQuery(expected).then(function(resp) {
214+
repeated = resp.obj;
215+
}).catch(function(err) {
216+
done.fail(err);
217+
}).then(done);
218+
});
219+
220+
it('should echo the request back', function() {
221+
// API will echo a non URL safe encoding
222+
expected.path_repeated_bytes_value = ["AA==", "/w=="];
223+
expect(repeated).toEqual(expected);
224+
});
225+
});
190226
});
191227

examples/clients/abe/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ go_library(
1515
"echo_service_api.go",
1616
"enum_helper.go",
1717
"examplepb_a_bit_of_everything.go",
18+
"examplepb_a_bit_of_everything_repeated.go",
1819
"examplepb_body.go",
1920
"examplepb_numeric_enum.go",
2021
"message_path_enum_nested_path_enum.go",

examples/clients/abe/a_bit_of_everything_service_api.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,110 @@ func (a ABitOfEverythingServiceApi) GetQuery(uuid string, singleNestedName strin
634634
return successPayload, localVarAPIResponse, err
635635
}
636636

637+
/**
638+
*
639+
*
640+
* @param pathRepeatedFloatValue repeated values. they are comma-separated in path
641+
* @param pathRepeatedDoubleValue
642+
* @param pathRepeatedInt64Value
643+
* @param pathRepeatedUint64Value
644+
* @param pathRepeatedInt32Value
645+
* @param pathRepeatedFixed64Value
646+
* @param pathRepeatedFixed32Value
647+
* @param pathRepeatedBoolValue
648+
* @param pathRepeatedStringValue
649+
* @param pathRepeatedBytesValue
650+
* @param pathRepeatedUint32Value
651+
* @param pathRepeatedEnumValue
652+
* @param pathRepeatedSfixed32Value
653+
* @param pathRepeatedSfixed64Value
654+
* @param pathRepeatedSint32Value
655+
* @param pathRepeatedSint64Value
656+
* @return *ExamplepbABitOfEverythingRepeated
657+
*/
658+
func (a ABitOfEverythingServiceApi) GetRepeatedQuery(pathRepeatedFloatValue []float32, pathRepeatedDoubleValue []float64, pathRepeatedInt64Value []string, pathRepeatedUint64Value []string, pathRepeatedInt32Value []int32, pathRepeatedFixed64Value []string, pathRepeatedFixed32Value []int64, pathRepeatedBoolValue []bool, pathRepeatedStringValue []string, pathRepeatedBytesValue []string, pathRepeatedUint32Value []int64, pathRepeatedEnumValue []string, pathRepeatedSfixed32Value []int32, pathRepeatedSfixed64Value []string, pathRepeatedSint32Value []int32, pathRepeatedSint64Value []string) (*ExamplepbABitOfEverythingRepeated, *APIResponse, error) {
659+
660+
var localVarHttpMethod = strings.ToUpper("Get")
661+
// create path and map variables
662+
localVarPath := a.Configuration.BasePath + "/v1/example/a_bit_of_everything_repeated/{path_repeated_float_value}/{path_repeated_double_value}/{path_repeated_int64_value}/{path_repeated_uint64_value}/{path_repeated_int32_value}/{path_repeated_fixed64_value}/{path_repeated_fixed32_value}/{path_repeated_bool_value}/{path_repeated_string_value}/{path_repeated_bytes_value}/{path_repeated_uint32_value}/{path_repeated_enum_value}/{path_repeated_sfixed32_value}/{path_repeated_sfixed64_value}/{path_repeated_sint32_value}/{path_repeated_sint64_value}"
663+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_float_value"+"}", fmt.Sprintf("%v", pathRepeatedFloatValue), -1)
664+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_double_value"+"}", fmt.Sprintf("%v", pathRepeatedDoubleValue), -1)
665+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_int64_value"+"}", fmt.Sprintf("%v", pathRepeatedInt64Value), -1)
666+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_uint64_value"+"}", fmt.Sprintf("%v", pathRepeatedUint64Value), -1)
667+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_int32_value"+"}", fmt.Sprintf("%v", pathRepeatedInt32Value), -1)
668+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_fixed64_value"+"}", fmt.Sprintf("%v", pathRepeatedFixed64Value), -1)
669+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_fixed32_value"+"}", fmt.Sprintf("%v", pathRepeatedFixed32Value), -1)
670+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_bool_value"+"}", fmt.Sprintf("%v", pathRepeatedBoolValue), -1)
671+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_string_value"+"}", fmt.Sprintf("%v", pathRepeatedStringValue), -1)
672+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_bytes_value"+"}", fmt.Sprintf("%v", pathRepeatedBytesValue), -1)
673+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_uint32_value"+"}", fmt.Sprintf("%v", pathRepeatedUint32Value), -1)
674+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_enum_value"+"}", fmt.Sprintf("%v", pathRepeatedEnumValue), -1)
675+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_sfixed32_value"+"}", fmt.Sprintf("%v", pathRepeatedSfixed32Value), -1)
676+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_sfixed64_value"+"}", fmt.Sprintf("%v", pathRepeatedSfixed64Value), -1)
677+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_sint32_value"+"}", fmt.Sprintf("%v", pathRepeatedSint32Value), -1)
678+
localVarPath = strings.Replace(localVarPath, "{"+"path_repeated_sint64_value"+"}", fmt.Sprintf("%v", pathRepeatedSint64Value), -1)
679+
680+
localVarHeaderParams := make(map[string]string)
681+
localVarQueryParams := url.Values{}
682+
localVarFormParams := make(map[string]string)
683+
var localVarPostBody interface{}
684+
var localVarFileName string
685+
var localVarFileBytes []byte
686+
// authentication '(OAuth2)' required
687+
// oauth required
688+
if a.Configuration.AccessToken != ""{
689+
localVarHeaderParams["Authorization"] = "Bearer " + a.Configuration.AccessToken
690+
}
691+
// authentication '(BasicAuth)' required
692+
// http basic authentication required
693+
if a.Configuration.Username != "" || a.Configuration.Password != ""{
694+
localVarHeaderParams["Authorization"] = "Basic " + a.Configuration.GetBasicAuthEncodedString()
695+
}
696+
// authentication '(ApiKeyAuth)' required
697+
// set key with prefix in header
698+
localVarHeaderParams["X-API-Key"] = a.Configuration.GetAPIKeyWithPrefix("X-API-Key")
699+
// add default headers if any
700+
for key := range a.Configuration.DefaultHeader {
701+
localVarHeaderParams[key] = a.Configuration.DefaultHeader[key]
702+
}
703+
704+
// to determine the Content-Type header
705+
localVarHttpContentTypes := []string{ "application/json", "application/x-foo-mime", }
706+
707+
// set Content-Type header
708+
localVarHttpContentType := a.Configuration.APIClient.SelectHeaderContentType(localVarHttpContentTypes)
709+
if localVarHttpContentType != "" {
710+
localVarHeaderParams["Content-Type"] = localVarHttpContentType
711+
}
712+
// to determine the Accept header
713+
localVarHttpHeaderAccepts := []string{
714+
"application/json",
715+
"application/x-foo-mime",
716+
}
717+
718+
// set Accept header
719+
localVarHttpHeaderAccept := a.Configuration.APIClient.SelectHeaderAccept(localVarHttpHeaderAccepts)
720+
if localVarHttpHeaderAccept != "" {
721+
localVarHeaderParams["Accept"] = localVarHttpHeaderAccept
722+
}
723+
var successPayload = new(ExamplepbABitOfEverythingRepeated)
724+
localVarHttpResponse, err := a.Configuration.APIClient.CallAPI(localVarPath, localVarHttpMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, localVarFileName, localVarFileBytes)
725+
726+
var localVarURL, _ = url.Parse(localVarPath)
727+
localVarURL.RawQuery = localVarQueryParams.Encode()
728+
var localVarAPIResponse = &APIResponse{Operation: "GetRepeatedQuery", Method: localVarHttpMethod, RequestURL: localVarURL.String()}
729+
if localVarHttpResponse != nil {
730+
localVarAPIResponse.Response = localVarHttpResponse.RawResponse
731+
localVarAPIResponse.Payload = localVarHttpResponse.Body()
732+
}
733+
734+
if err != nil {
735+
return successPayload, localVarAPIResponse, err
736+
}
737+
err = json.Unmarshal(localVarHttpResponse.Body(), &successPayload)
738+
return successPayload, localVarAPIResponse, err
739+
}
740+
637741
/**
638742
*
639743
*
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* A Bit of Everything
3+
*
4+
* No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
5+
*
6+
* OpenAPI spec version: 1.0
7+
* Contact: [email protected]
8+
* Generated by: https://github.com/swagger-api/swagger-codegen.git
9+
*/
10+
11+
package abe
12+
13+
type ExamplepbABitOfEverythingRepeated struct {
14+
15+
PathRepeatedFloatValue []float32 `json:"path_repeated_float_value,omitempty"`
16+
17+
PathRepeatedDoubleValue []float64 `json:"path_repeated_double_value,omitempty"`
18+
19+
PathRepeatedInt64Value []string `json:"path_repeated_int64_value,omitempty"`
20+
21+
PathRepeatedUint64Value []string `json:"path_repeated_uint64_value,omitempty"`
22+
23+
PathRepeatedInt32Value []int32 `json:"path_repeated_int32_value,omitempty"`
24+
25+
PathRepeatedFixed64Value []string `json:"path_repeated_fixed64_value,omitempty"`
26+
27+
PathRepeatedFixed32Value []int64 `json:"path_repeated_fixed32_value,omitempty"`
28+
29+
PathRepeatedBoolValue []bool `json:"path_repeated_bool_value,omitempty"`
30+
31+
PathRepeatedStringValue []string `json:"path_repeated_string_value,omitempty"`
32+
33+
PathRepeatedBytesValue []string `json:"path_repeated_bytes_value,omitempty"`
34+
35+
PathRepeatedUint32Value []int64 `json:"path_repeated_uint32_value,omitempty"`
36+
37+
PathRepeatedEnumValue []ExamplepbNumericEnum `json:"path_repeated_enum_value,omitempty"`
38+
39+
PathRepeatedSfixed32Value []int32 `json:"path_repeated_sfixed32_value,omitempty"`
40+
41+
PathRepeatedSfixed64Value []string `json:"path_repeated_sfixed64_value,omitempty"`
42+
43+
PathRepeatedSint32Value []int32 `json:"path_repeated_sint32_value,omitempty"`
44+
45+
PathRepeatedSint64Value []string `json:"path_repeated_sint64_value,omitempty"`
46+
}

examples/integration/integration_test.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package integration_test
33
import (
44
"bytes"
55
"context"
6+
"encoding/base64"
67
"encoding/json"
78
"fmt"
89
"io"
@@ -266,6 +267,7 @@ func TestABE(t *testing.T) {
266267
testABEBulkEcho(t, 8080)
267268
testABEBulkEchoZeroLength(t, 8080)
268269
testAdditionalBindings(t, 8080)
270+
testABERepeated(t, 8080)
269271
}
270272

271273
func testABECreate(t *testing.T, port int) {
@@ -835,6 +837,125 @@ func testAdditionalBindings(t *testing.T, port int) {
835837
}
836838
}
837839

840+
func testABERepeated(t *testing.T, port int) {
841+
f := func(v reflect.Value) string {
842+
var f func(v reflect.Value, idx int) string
843+
s := make([]string, v.Len())
844+
switch v.Index(0).Kind() {
845+
case reflect.Slice:
846+
f = func(v reflect.Value, idx int) string {
847+
t := v.Index(idx).Type().Elem().Kind()
848+
if t == reflect.Uint8 {
849+
return base64.URLEncoding.EncodeToString(v.Index(idx).Interface().([]byte))
850+
}
851+
// Could handle more elegantly
852+
panic("unknown slice of type: " + t.String())
853+
}
854+
default:
855+
f = func(v reflect.Value, idx int) string {
856+
return fmt.Sprintf("%v", v.Index(idx).Interface())
857+
}
858+
}
859+
for i := 0; i < v.Len(); i++ {
860+
s[i] = f(v, i)
861+
}
862+
return strings.Join(s, ",")
863+
}
864+
want := gw.ABitOfEverythingRepeated{
865+
PathRepeatedFloatValue: []float32{
866+
1.5,
867+
-1.5,
868+
},
869+
PathRepeatedDoubleValue: []float64{
870+
2.5,
871+
-2.5,
872+
},
873+
PathRepeatedInt64Value: []int64{
874+
4294967296,
875+
-4294967296,
876+
},
877+
PathRepeatedUint64Value: []uint64{
878+
0,
879+
9223372036854775807,
880+
},
881+
PathRepeatedInt32Value: []int32{
882+
2147483647,
883+
-2147483648,
884+
},
885+
PathRepeatedFixed64Value: []uint64{
886+
0,
887+
9223372036854775807,
888+
},
889+
PathRepeatedFixed32Value: []uint32{
890+
0,
891+
4294967295,
892+
},
893+
PathRepeatedBoolValue: []bool{
894+
true,
895+
false,
896+
},
897+
PathRepeatedStringValue: []string{
898+
"foo",
899+
"bar",
900+
},
901+
PathRepeatedBytesValue: [][]byte{
902+
[]byte{0x00},
903+
[]byte{0xFF},
904+
},
905+
PathRepeatedUint32Value: []uint32{
906+
0,
907+
4294967295,
908+
},
909+
PathRepeatedEnumValue: []gw.NumericEnum{
910+
gw.NumericEnum_ZERO,
911+
gw.NumericEnum_ONE,
912+
},
913+
PathRepeatedSfixed32Value: []int32{
914+
2147483647,
915+
-2147483648,
916+
},
917+
PathRepeatedSfixed64Value: []int64{
918+
4294967296,
919+
-4294967296,
920+
},
921+
PathRepeatedSint32Value: []int32{
922+
2147483647,
923+
-2147483648,
924+
},
925+
PathRepeatedSint64Value: []int64{
926+
4611686018427387903,
927+
-4611686018427387904,
928+
},
929+
}
930+
url := fmt.Sprintf("http://localhost:%d/v1/example/a_bit_of_everything_repeated/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s/%s", port, f(reflect.ValueOf(want.PathRepeatedFloatValue)), f(reflect.ValueOf(want.PathRepeatedDoubleValue)), f(reflect.ValueOf(want.PathRepeatedInt64Value)), f(reflect.ValueOf(want.PathRepeatedUint64Value)), f(reflect.ValueOf(want.PathRepeatedInt32Value)), f(reflect.ValueOf(want.PathRepeatedFixed64Value)), f(reflect.ValueOf(want.PathRepeatedFixed32Value)), f(reflect.ValueOf(want.PathRepeatedBoolValue)), f(reflect.ValueOf(want.PathRepeatedStringValue)), f(reflect.ValueOf(want.PathRepeatedBytesValue)), f(reflect.ValueOf(want.PathRepeatedUint32Value)), f(reflect.ValueOf(want.PathRepeatedEnumValue)), f(reflect.ValueOf(want.PathRepeatedSfixed32Value)), f(reflect.ValueOf(want.PathRepeatedSfixed64Value)), f(reflect.ValueOf(want.PathRepeatedSint32Value)), f(reflect.ValueOf(want.PathRepeatedSint64Value)))
931+
932+
resp, err := http.Get(url)
933+
if err != nil {
934+
t.Errorf("http.Post(%q) failed with %v; want success", url, err)
935+
return
936+
}
937+
defer resp.Body.Close()
938+
buf, err := ioutil.ReadAll(resp.Body)
939+
if err != nil {
940+
t.Errorf("iotuil.ReadAll(resp.Body) failed with %v; want success", err)
941+
return
942+
}
943+
944+
if got, want := resp.StatusCode, http.StatusOK; got != want {
945+
t.Errorf("resp.StatusCode = %d; want %d", got, want)
946+
t.Logf("%s", buf)
947+
}
948+
949+
var msg gw.ABitOfEverythingRepeated
950+
if err := jsonpb.UnmarshalString(string(buf), &msg); err != nil {
951+
t.Errorf("jsonpb.UnmarshalString(%s, &msg) failed with %v; want success", buf, err)
952+
return
953+
}
954+
if got := msg; !reflect.DeepEqual(got, want) {
955+
t.Errorf("msg= %v; want %v", &got, &want)
956+
}
957+
}
958+
838959
func TestTimeout(t *testing.T) {
839960
url := "http://localhost:8080/v2/example/timeout"
840961
req, err := http.NewRequest("GET", url, nil)

0 commit comments

Comments
 (0)