Repository: algolia/algoliasearch-client-go
Pull request #615 (2020-06-26 11:29:11)

fix(transport): correctly URL-encode values (only used by API keys for now)


Patch:
diff --git a/algolia/transport/utils.go b/algolia/transport/utils.go
index a75adf1b..e845b757 100644
--- a/algolia/transport/utils.go
+++ b/algolia/transport/utils.go
@@ -54,8 +54,12 @@ func URLEncode(itf interface{}) string {
 		case int:
 			value = strconv.Itoa(v)
 		default:
-			jsonValue, _ := json.Marshal(v)
-			value = string(jsonValue)
+			if v == nil {
+				value = ""
+			} else {
+				jsonValue, _ := json.Marshal(v)
+				value = string(jsonValue)
+			}
 		}
 		values.Add(key, value)
 	}
@@ -73,15 +77,7 @@ func URLDecode(data []byte, itf interface{}) error {
 
 	m := make(map[string]interface{})
 	for k, v := range values {
-		if len(v) == 1 {
-			m[k] = convertValue(v[0])
-		} else {
-			var s []interface{}
-			for _, value := range v {
-				s = append(s, convertValue(value))
-			}
-			m[k] = s
-		}
+		m[k] = convertValue(v[0])
 	}
 
 	data, err = json.Marshal(m)
@@ -94,14 +90,20 @@ func URLDecode(data []byte, itf interface{}) error {
 }
 
 func convertValue(v string) interface{} {
-	if len(v) >= 2 && v[0] == '[' && v[len(v)-1] == ']' {
-		var s []interface{}
-		for _, v := range strings.Split(v[1:len(v)-1], ",") {
-			s = append(s, convertValue(v))
+	if len(v) == 0 {
+		return nil
+	}
+	if len(v) == 1 {
+		return v
+	}
+	if isArray(v) {
+		var arr []interface{}
+		err := json.Unmarshal([]byte(v), &arr)
+		if err != nil {
+			debug.Printf("cannot decode array value %s: %v\n", v, err)
 		}
-		return s
+		return arr
 	}
-
 	trimmed := strings.Trim(v, `"`)
 	if b, err := strconv.ParseBool(trimmed); err == nil {
 		return b
@@ -114,3 +116,9 @@ func convertValue(v string) interface{} {
 	}
 	return trimmed
 }
+
+func isArray(v string) bool {
+	firstChar := v[0]
+	lastChar := v[len(v)-1]
+	return firstChar == '[' && lastChar == ']'
+}


Test patch:
diff --git a/algolia/search/key_test.go b/algolia/search/key_test.go
index 00c325d5..31cdbe4f 100644
--- a/algolia/search/key_test.go
+++ b/algolia/search/key_test.go
@@ -1,11 +1,13 @@
 package search
 
 import (
+	"encoding/json"
 	"testing"
 	"time"
 
-	"github.com/algolia/algoliasearch-client-go/v3/algolia/opt"
 	"github.com/stretchr/testify/require"
+
+	"github.com/algolia/algoliasearch-client-go/v3/algolia/opt"
 )
 
 func TestAreKeysEqual(t *testing.T) {
@@ -62,3 +64,37 @@ func TestAreKeysEqual(t *testing.T) {
 		require.Equal(t, v2, c.k2.Validity)
 	}
 }
+
+func TestKeyDeserializer(t *testing.T) {
+	for _, c := range []struct {
+		payload  string
+		expected Key
+	}{
+		{
+			`{}`,
+			Key{CreatedAt: time.Unix(0, 0)},
+		},
+		{
+			`{"queryParameters":"facetFilters=[[%22privatePlatformCodes:-ha%22], [%22biddingStatus:-Closed%22, %22saleStatus:None%22]]"}`,
+			Key{
+				CreatedAt: time.Unix(0, 0),
+				QueryParameters: KeyQueryParams{
+					QueryParams: QueryParams{
+						FacetFilters: opt.FacetFilterAnd(
+							opt.FacetFilter("privatePlatformCodes:-ha"),
+							opt.FacetFilterOr(
+								opt.FacetFilter("biddingStatus:-Closed"),
+								opt.FacetFilter("saleStatus:None"),
+							),
+						),
+					},
+				},
+			},
+		},
+	} {
+		var got Key
+		err := json.Unmarshal([]byte(c.payload), &got)
+		require.NoError(t, err, "cannot unmarshal key payload %s", c.payload)
+		require.Equal(t, c.expected, got, "keys are not equal for payload %s", c.payload)
+	}
+}
diff --git a/algolia/transport/utils_test.go b/algolia/transport/utils_test.go
index aa5c04f6..11092a3c 100644
--- a/algolia/transport/utils_test.go
+++ b/algolia/transport/utils_test.go
@@ -2,11 +2,11 @@ package transport
 
 import (
 	"fmt"
-	"reflect"
 	"testing"
 
-	"github.com/algolia/algoliasearch-client-go/v3/algolia/call"
 	"github.com/stretchr/testify/require"
+
+	"github.com/algolia/algoliasearch-client-go/v3/algolia/call"
 )
 
 func TestShuffle(t *testing.T) {
@@ -77,13 +77,13 @@ func TestURLEncodeDecode(t *testing.T) {
 		},
 	} {
 		encoded1 := URLEncode(c.input)
-		require.Equal(t, c.encoded, encoded1, "wrong URL-encoded string for input %q", c.input)
+		require.Equal(t, c.encoded, encoded1, "wrong URL-encoded string for input %#v", c.input)
 
-		decoded := reflect.New(reflect.TypeOf(c.input)).Interface()
+		decoded := c.input
 		err := URLDecode([]byte(encoded1), &decoded)
-		require.NoError(t, err, "cannot decode back the input %q once encoded (%s)", c.input, encoded1)
+		require.NoError(t, err, "cannot decode back the input %#v once encoded (%s)", c.input, encoded1)
 
 		encoded2 := URLEncode(decoded)
-		require.Equal(t, c.encoded, encoded2, "wrong URL-encoded string for input %q", c.input)
+		require.Equal(t, c.encoded, encoded2, "wrong URL-encoded string for input %#v", c.input)
 	}
 }