Skip to content

Commit

Permalink
support for maps in query string of the form ?filters[key]=value
Browse files Browse the repository at this point in the history
  • Loading branch information
adamstruck committed Jan 30, 2018
1 parent 7ad17e1 commit 6e5fd3d
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
50 changes: 50 additions & 0 deletions runtime/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -18,6 +19,15 @@ import (
// A value is ignored if its key starts with one of the elements in "filter".
func PopulateQueryParameters(msg proto.Message, values url.Values, filter *utilities.DoubleArray) error {
for key, values := range values {
re, err := regexp.Compile("^(.*)\\[(.*)\\]$")
if err != nil {
return err
}
match := re.FindStringSubmatch(key)
if len(match) == 3 {
key = match[1]
values = append([]string{match[2]}, values...)
}
fieldPath := strings.Split(key, ".")
if filter.HasCommonPrefix(fieldPath) {
continue
Expand Down Expand Up @@ -84,6 +94,11 @@ func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []
case reflect.Struct:
m = f
continue
case reflect.Map:
if !isLast {
return fmt.Errorf("unexpected map field in %s", strings.Join(fieldPath, "."))
}
return populateMapField(f, values, props)
default:
return fmt.Errorf("unexpected type %s in %T", f.Type(), msg)
}
Expand Down Expand Up @@ -125,6 +140,41 @@ func fieldByProtoName(m reflect.Value, name string) (reflect.Value, *proto.Prope
return reflect.Value{}, nil, nil
}

func populateMapField(f reflect.Value, values []string, props *proto.Properties) error {
if len(values) != 2 {
return fmt.Errorf("too many values provided for key in map")
}

key, value := values[0], values[1]
keyType := f.Type().Key()
valueType := f.Type().Elem()
if f.IsNil() {
f.Set(reflect.MakeMap(f.Type()))
}

keyConv, ok := convFromType[keyType.Kind()]
if !ok {
return fmt.Errorf("unsupported field type %s", keyType)
}
valueConv, ok := convFromType[valueType.Kind()]
if !ok {
return fmt.Errorf("unsupported field type %s", valueType)
}

keyV := keyConv.Call([]reflect.Value{reflect.ValueOf(key)})
if err := keyV[1].Interface(); err != nil {
return err.(error)
}
valueV := valueConv.Call([]reflect.Value{reflect.ValueOf(value)})
if err := valueV[1].Interface(); err != nil {
return err.(error)
}

f.SetMapIndex(keyV[0].Convert(keyType), valueV[0].Convert(valueType))

return nil
}

func populateRepeatedField(f reflect.Value, values []string, props *proto.Properties) error {
elemType := f.Type().Elem()

Expand Down
46 changes: 45 additions & 1 deletion runtime/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"net/url"
"reflect"
"testing"

"time"

"github.com/golang/protobuf/proto"
Expand Down Expand Up @@ -60,6 +59,21 @@ func TestPopulateParameters(t *testing.T) {
"wrapper_bool_value": {"true"},
"wrapper_string_value": {"str"},
"wrapper_bytes_value": {"Ynl0ZXM"},
"map_value[key]": {"value"},
"map_value2[key]": {"-2"},
"map_value3[-2]": {"value"},
"map_value4[key]": {"-1"},
"map_value5[-1]": {"value"},
"map_value6[key]": {"3"},
"map_value7[3]": {"value"},
"map_value8[key]": {"4"},
"map_value9[4]": {"value"},
"map_value10[key]": {"1.5"},
"map_value11[1.5]": {"value"},
"map_value12[key]": {"2.5"},
"map_value13[2.5]": {"value"},
"map_value14[key]": {"true"},
"map_value15[true]": {"value"},
},
filter: utilities.NewDoubleArray(nil),
want: &proto3Message{
Expand All @@ -86,6 +100,21 @@ func TestPopulateParameters(t *testing.T) {
WrapperBoolValue: &wrappers.BoolValue{true},
WrapperStringValue: &wrappers.StringValue{"str"},
WrapperBytesValue: &wrappers.BytesValue{[]byte("bytes")},
MapValue: map[string]string{"key": "value"},
MapValue2: map[string]int32{"key": -2},
MapValue3: map[int32]string{-2: "value"},
MapValue4: map[string]int64{"key": -1},
MapValue5: map[int64]string{-1: "value"},
MapValue6: map[string]uint32{"key": 3},
MapValue7: map[uint32]string{3: "value"},
MapValue8: map[string]uint64{"key": 4},
MapValue9: map[uint64]string{4: "value"},
MapValue10: map[string]float32{"key": 1.5},
MapValue11: map[float32]string{1.5: "value"},
MapValue12: map[string]float64{"key": 2.5},
MapValue13: map[float64]string{2.5: "value"},
MapValue14: map[string]bool{"key": true},
MapValue15: map[bool]string{true: "value"},
},
},
{
Expand Down Expand Up @@ -503,6 +532,21 @@ type proto3Message struct {
WrapperBoolValue *wrappers.BoolValue `protobuf:"bytes,23,opt,name=wrapper_bool_value,json=wrapperBoolValue" json:"wrapper_bool_value,omitempty"`
WrapperStringValue *wrappers.StringValue `protobuf:"bytes,24,opt,name=wrapper_string_value,json=wrapperStringValue" json:"wrapper_string_value,omitempty"`
WrapperBytesValue *wrappers.BytesValue `protobuf:"bytes,26,opt,name=wrapper_bytes_value,json=wrapperBytesValue" json:"wrapper_bytes_value,omitempty"`
MapValue map[string]string `protobuf:"bytes,27,opt,name=map_value,json=mapValue" json:"map_value,omitempty"`
MapValue2 map[string]int32 `protobuf:"bytes,28,opt,name=map_value2,json=mapValue2" json:"map_value2,omitempty"`
MapValue3 map[int32]string `protobuf:"bytes,29,opt,name=map_value3,json=mapValue3" json:"map_value3,omitempty"`
MapValue4 map[string]int64 `protobuf:"bytes,30,opt,name=map_value4,json=mapValue4" json:"map_value4,omitempty"`
MapValue5 map[int64]string `protobuf:"bytes,31,opt,name=map_value5,json=mapValue5" json:"map_value5,omitempty"`
MapValue6 map[string]uint32 `protobuf:"bytes,32,opt,name=map_value6,json=mapValue6" json:"map_value6,omitempty"`
MapValue7 map[uint32]string `protobuf:"bytes,33,opt,name=map_value7,json=mapValue7" json:"map_value7,omitempty"`
MapValue8 map[string]uint64 `protobuf:"bytes,34,opt,name=map_value8,json=mapValue8" json:"map_value8,omitempty"`
MapValue9 map[uint64]string `protobuf:"bytes,35,opt,name=map_value9,json=mapValue9" json:"map_value9,omitempty"`
MapValue10 map[string]float32 `protobuf:"bytes,36,opt,name=map_value10,json=mapValue10" json:"map_value10,omitempty"`
MapValue11 map[float32]string `protobuf:"bytes,37,opt,name=map_value11,json=mapValue11" json:"map_value11,omitempty"`
MapValue12 map[string]float64 `protobuf:"bytes,38,opt,name=map_value12,json=mapValue12" json:"map_value12,omitempty"`
MapValue13 map[float64]string `protobuf:"bytes,39,opt,name=map_value13,json=mapValue13" json:"map_value13,omitempty"`
MapValue14 map[string]bool `protobuf:"bytes,40,opt,name=map_value14,json=mapValue14" json:"map_value14,omitempty"`
MapValue15 map[bool]string `protobuf:"bytes,41,opt,name=map_value15,json=mapValue15" json:"map_value15,omitempty"`
}

func (m *proto3Message) Reset() { *m = proto3Message{} }
Expand Down

0 comments on commit 6e5fd3d

Please sign in to comment.