Skip to content

Commit

Permalink
URLQuery with index slice keys (#74)
Browse files Browse the repository at this point in the history
* URLQuery with index slice keys

* Added tests

* Cleanup

* Added test for MSI and cleaned up redundant code

* Added SetURLValuesSliceKeySuffix()

* Added error to SetURLValuesSliceKeySuffix()
  • Loading branch information
geseq authored and hanzei committed Jul 2, 2018
1 parent 9e1dfc1 commit b8b73a3
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 22 deletions.
88 changes: 69 additions & 19 deletions conversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"net/url"
"strconv"
)

// SignatureSeparator is the character that is used to
Expand All @@ -14,9 +15,34 @@ const SignatureSeparator = "_"

// URLValuesSliceKeySuffix is the character that is used to
// specify a suffic for slices parsed by URLValues.
// If the suffix is set to "[i]", then the index of the slice
// is used in place of i
// Ex: Suffix "[]" would have the form a[]=b&a[]=c
// OR Suffix "[i]" would have the form a[0]=b&a[1]=c
// OR Suffix "" would have the form a=b&a=c
var URLValuesSliceKeySuffix = "[]"
var urlValuesSliceKeySuffix = "[]"

const (
URLValuesSliceKeySuffixEmpty = ""
URLValuesSliceKeySuffixArray = "[]"
URLValuesSliceKeySuffixIndex = "[i]"
)

// SetURLValuesSliceKeySuffix sets the character that is used to
// specify a suffic for slices parsed by URLValues.
// If the suffix is set to "[i]", then the index of the slice
// is used in place of i
// Ex: Suffix "[]" would have the form a[]=b&a[]=c
// OR Suffix "[i]" would have the form a[0]=b&a[1]=c
// OR Suffix "" would have the form a=b&a=c
func SetURLValuesSliceKeySuffix(s string) error {
if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex {
urlValuesSliceKeySuffix = s
return nil
}

return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.")
}

// JSON converts the contained object to a JSON string
// representation
Expand Down Expand Up @@ -106,50 +132,74 @@ func (m Map) URLValues() url.Values {
}

func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) {
useSliceIndex := false
if urlValuesSliceKeySuffix == "[i]" {
useSliceIndex = true
}

for k, v := range queryMap {
val := &Value{data: v}
switch {
case val.IsObjxMap():
if key == "" {
m.parseURLValues(v.(Map), vals, k)
m.parseURLValues(val.ObjxMap(), vals, k)
} else {
m.parseURLValues(v.(Map), vals, key+"["+k+"]")
m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]")
}
case val.IsObjxMapSlice():
sliceKey := k + URLValuesSliceKeySuffix
sliceKey := k
if key != "" {
sliceKey = key + "[" + k + "]" + URLValuesSliceKeySuffix
sliceKey = key + "[" + k + "]"
}

for _, sv := range val.MustObjxMapSlice() {
m.parseURLValues(sv, vals, sliceKey)
}
case val.IsMSI():
if key == "" {
m.parseURLValues(New(v), vals, k)
if useSliceIndex {
for i, sv := range val.MustObjxMapSlice() {
sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
m.parseURLValues(sv, vals, sk)
}
} else {
m.parseURLValues(New(v), vals, key+"["+k+"]")
sliceKey = sliceKey + urlValuesSliceKeySuffix
for _, sv := range val.MustObjxMapSlice() {
m.parseURLValues(sv, vals, sliceKey)
}
}
case val.IsMSISlice():
sliceKey := k + URLValuesSliceKeySuffix
sliceKey := k
if key != "" {
sliceKey = key + "[" + k + "]" + URLValuesSliceKeySuffix
sliceKey = key + "[" + k + "]"
}

for _, sv := range val.MustMSISlice() {
m.parseURLValues(New(sv), vals, sliceKey)
if useSliceIndex {
for i, sv := range val.MustMSISlice() {
sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
m.parseURLValues(New(sv), vals, sk)
}
} else {
sliceKey = sliceKey + urlValuesSliceKeySuffix
for _, sv := range val.MustMSISlice() {
m.parseURLValues(New(sv), vals, sliceKey)
}
}
case val.IsStrSlice(), val.IsBoolSlice(),
val.IsFloat32Slice(), val.IsFloat64Slice(),
val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(),
val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice():

sliceKey := k + URLValuesSliceKeySuffix
sliceKey := k
if key != "" {
sliceKey = key + "[" + k + "]" + URLValuesSliceKeySuffix
sliceKey = key + "[" + k + "]"
}

if useSliceIndex {
for i, sv := range val.StringSlice() {
sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]"
vals.Set(sk, sv)
}
} else {
sliceKey = sliceKey + urlValuesSliceKeySuffix
vals[sliceKey] = val.StringSlice()
}

vals[sliceKey] = val.StringSlice()
default:
if key == "" {
vals.Set(k, val.String())
Expand Down
40 changes: 37 additions & 3 deletions conversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ func TestConversionURLValues(t *testing.T) {
"bools[]": []string{"true", "false"},
"mapSlice[][age]": []string{"40"},
"mapSlice[][height]": []string{"152"},
"msiData[age]": []string{"30"},
"msiData[height]": []string{"162"},
"msiData[arr][]": []string{"1", "2"},
"msiSlice[][age]": []string{"40"},
"msiSlice[][height]": []string{"152"},
}, u)
}

Expand All @@ -107,12 +112,12 @@ func TestConversionURLQuery(t *testing.T) {
assert.Nil(t, err)
require.NotNil(t, ue)

assert.Equal(t, "abc=123&bools[]=true&bools[]=false&data[age]=30&data[arr][]=1&data[arr][]=2&data[height]=162&mapSlice[][age]=40&mapSlice[][height]=152&name=Mat&stats[]=1&stats[]=2", ue)
assert.Equal(t, "abc=123&bools[]=true&bools[]=false&data[age]=30&data[arr][]=1&data[arr][]=2&data[height]=162&mapSlice[][age]=40&mapSlice[][height]=152&msiData[age]=30&msiData[arr][]=1&msiData[arr][]=2&msiData[height]=162&msiSlice[][age]=40&msiSlice[][height]=152&name=Mat&stats[]=1&stats[]=2", ue)
}

func TestConversionURLQueryNoSliceKeySuffix(t *testing.T) {
m := getURLQueryMap()
objx.URLValuesSliceKeySuffix = ""
objx.SetURLValuesSliceKeySuffix(objx.URLValuesSliceKeySuffixEmpty)
u, err := m.URLQuery()

assert.Nil(t, err)
Expand All @@ -122,7 +127,34 @@ func TestConversionURLQueryNoSliceKeySuffix(t *testing.T) {
assert.Nil(t, err)
require.NotNil(t, ue)

assert.Equal(t, "abc=123&bools=true&bools=false&data[age]=30&data[arr]=1&data[arr]=2&data[height]=162&mapSlice[age]=40&mapSlice[height]=152&name=Mat&stats=1&stats=2", ue)
assert.Equal(t, "abc=123&bools=true&bools=false&data[age]=30&data[arr]=1&data[arr]=2&data[height]=162&mapSlice[age]=40&mapSlice[height]=152&msiData[age]=30&msiData[arr]=1&msiData[arr]=2&msiData[height]=162&msiSlice[age]=40&msiSlice[height]=152&name=Mat&stats=1&stats=2", ue)
}

func TestConversionURLQueryIndexSliceKeySuffix(t *testing.T) {
m := getURLQueryMap()
m.Set("mapSlice", []objx.Map{{"age": 40, "sex": "male"}, {"height": 152}})
objx.SetURLValuesSliceKeySuffix(objx.URLValuesSliceKeySuffixIndex)
u, err := m.URLQuery()

assert.Nil(t, err)
require.NotNil(t, u)

ue, err := url.QueryUnescape(u)
assert.Nil(t, err)
require.NotNil(t, ue)

assert.Equal(t, "abc=123&bools[0]=true&bools[1]=false&data[age]=30&data[arr][0]=1&data[arr][1]=2&data[height]=162&mapSlice[0][age]=40&mapSlice[0][sex]=male&mapSlice[1][height]=152&msiData[age]=30&msiData[arr][0]=1&msiData[arr][1]=2&msiData[height]=162&msiSlice[0][age]=40&msiSlice[1][height]=152&name=Mat&stats[0]=1&stats[1]=2", ue)
}

func TestValidityURLQuerySliceKeySuffix(t *testing.T) {
err := objx.SetURLValuesSliceKeySuffix("")
assert.Nil(t, err)
err = objx.SetURLValuesSliceKeySuffix("[]")
assert.Nil(t, err)
err = objx.SetURLValuesSliceKeySuffix("[i]")
assert.Nil(t, err)
err = objx.SetURLValuesSliceKeySuffix("{}")
assert.Error(t, err)
}

func getURLQueryMap() objx.Map {
Expand All @@ -131,6 +163,8 @@ func getURLQueryMap() objx.Map {
"name": "Mat",
"data": objx.Map{"age": 30, "height": 162, "arr": []int{1, 2}},
"mapSlice": []objx.Map{{"age": 40}, {"height": 152}},
"msiData": map[string]interface{}{"age": 30, "height": 162, "arr": []int{1, 2}},
"msiSlice": []map[string]interface{}{{"age": 40}, {"height": 152}},
"stats": []string{"1", "2"},
"bools": []bool{true, false},
}
Expand Down

0 comments on commit b8b73a3

Please sign in to comment.