Repository: algolia/algoliasearch-client-go (go)

Issue:
bug(suggestions): deserialization failed when languages are not set
- Algolia Client Version: master(`v3.19.1-0.20210628231352-f4915f52041a`)
- Language Version: 1.16

### Description
JSON deserialization failed by the wrong value type of `"languages":false` when we get the query suggestions config whose `languages` is not set as following.
Expected behaivour is that the suggestions API returns a json with `"languages": null`. (So this issue is not about client library but API)
https://www.algolia.com/doc/rest-api/query-suggestions/#method-param-languages

```
Error: cannot deserialize response's body: json: cannot unmarshal bool into Go struct field IndexConfiguration.languages of type []string: 
{
  "appID": "XXXXXXXX",
  "sourceIndicesAPIKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "externalIndicesAPIKey": "",
  "suggestionIndicesAPIKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "indexName": "test",
  "sourceIndices": [
    {
      "indexName": "test_source",
      "replicas": false,
      "analyticsTags": null,
      "facets": null,
      "minHits": 5,
      "minLetters": 4,
      "generate": null,
      "external": null
    }
  ],
  "languages": false,
  "exclude": null,
  "enablePersonalization": false
}
```

### Steps To Reproduce
1. Create query suggestions index config without setting `languages`.
2. Get the created suggestions index config.
```
package main

import (
	"github.com/algolia/algoliasearch-client-go/v3/algolia/search"
	"github.com/algolia/algoliasearch-client-go/v3/algolia/suggestions"
)

func main() {
	appID := "XXXXXXXXXX"
	apiKey := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

	searchConfig := search.Configuration{
		AppID:  appID,
		APIKey: apiKey,
	}
	searchClient := search.NewClientWithConfig(searchConfig)
	createIndex(searchClient, "test")
	createIndex(searchClient, "test_source")

	suggestionsConfig := suggestions.Configuration{
		AppID:  appID,
		APIKey: apiKey,
	}
	suggestionsClient := suggestions.NewClientWithConfig(suggestionsConfig)
	err := suggestionsClient.CreateConfig(suggestions.IndexConfiguration{
		IndexName: "test",
		SourceIndices: []suggestions.SourceIndex{
			{IndexName: "test_source"},
		},
	})
	if err != nil {
		panic(err)
	}

	_, err = suggestionsClient.GetConfig("test")
	if err != nil {
		panic(err)
	}
}

func createIndex(searchClient *search.Client, indexName string) {
	res, err := searchClient.InitIndex(indexName).SetSettings(search.Settings{})
	if err != nil {
		panic(err)
	}
	if err := res.Wait(); err != nil {
		panic(err)
	}
}
```

Pull request:
fix: Suggestion config bool value support
| Q                 | A
| ----------------- | ----------
| Bug fix?          | yes
| New feature?      | yes/no    <!-- please update the /CHANGELOG.md file -->
| BC breaks?        | no     
| Related Issue     | Fix #671 
| Need Doc update   | no


## Describe your change

This PR introduces `BoolOrStringArray` type to properly represent the query suggestions configuration `languages` value.

## What problem is this fixing?

Described in #671

Patch:
diff --git a/algolia/suggestions/bool_or_string_array.go b/algolia/suggestions/bool_or_string_array.go
new file mode 100644
index 00000000..45b1e728
--- /dev/null
+++ b/algolia/suggestions/bool_or_string_array.go
@@ -0,0 +1,52 @@
+package suggestions
+
+import "encoding/json"
+
+// BoolOrStringArray can hold either a bool or a []string.
+// IsBool indicates the valid value: Bool or StringArray.
+type BoolOrStringArray struct {
+	IsBool      bool
+	Bool        bool
+	StringArray []string
+}
+
+func NewBool(boolValue bool) BoolOrStringArray {
+	return BoolOrStringArray{
+		IsBool:      true,
+		Bool:        boolValue,
+		StringArray: []string{},
+	}
+}
+
+func NewStringArray(array []string) BoolOrStringArray {
+	return BoolOrStringArray{
+		IsBool:      false,
+		Bool:        false,
+		StringArray: array,
+	}
+}
+
+// UnmarshalJSON decodes a JSON bool or string array into a BoolOrStringArray.
+func (b *BoolOrStringArray) UnmarshalJSON(data []byte) error {
+	err := json.Unmarshal(data, &b.Bool)
+	if err == nil {
+		b.IsBool = true
+		b.StringArray = []string{}
+		return nil
+	}
+	err = json.Unmarshal(data, &b.StringArray)
+	if err == nil {
+		b.IsBool = false
+		b.Bool = false
+		return nil
+	}
+	return err
+}
+
+// MarshalJSON encodes a BoolOrStringArray to a JSON bool or string array.
+func (b BoolOrStringArray) MarshalJSON() ([]byte, error) {
+	if b.IsBool {
+		return json.Marshal(b.Bool)
+	}
+	return json.Marshal(b.StringArray)
+}
diff --git a/algolia/suggestions/client_config.go b/algolia/suggestions/client_config.go
index d2279d73..71308ffb 100644
--- a/algolia/suggestions/client_config.go
+++ b/algolia/suggestions/client_config.go
@@ -12,8 +12,11 @@ type IndexConfiguration struct {
 	IndexName string `json:"indexName"`
 	// List of source indices used to generate a Query Suggestions index.
 	SourceIndices []SourceIndex `json:"sourceIndices"`
-	// De-duplicate singular and plural suggestions.
-	Languages []string `json:"languages,omitempty"`
+	// De-duplicate singular and plural suggestions. Can be either a list languages []string or a boolean.
+	// true value means that all the languages are supported.
+	// false value means that singulars and plurals are not considered the same for matching purposes (foot will not find feet).
+	// []string a list of language ISO codes for which singular and plural suggestions should be enabled.
+	Languages BoolOrStringArray `json:"languages"`
 	// List of words and patterns to exclude from the Query Suggestions index.
 	Exclude []string `json:"exclude,omitempty"`
 }


Test patch:
diff --git a/algolia/suggestions/client_config_test.go b/algolia/suggestions/client_config_test.go
new file mode 100644
index 00000000..906868f2
--- /dev/null
+++ b/algolia/suggestions/client_config_test.go
@@ -0,0 +1,29 @@
+package suggestions
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestConfig_Unmarshal(t *testing.T) {
+	var c IndexConfiguration
+	data := []byte(`{"languages":["ja"]}`)
+	err := json.Unmarshal(data, &c)
+	require.NoError(t, err)
+	require.False(t, c.Languages.IsBool)
+	require.Equal(t, []string{"ja"}, c.Languages.StringArray)
+
+	c = IndexConfiguration{}
+	data = []byte(`{"languages":false}`)
+	err = json.Unmarshal(data, &c)
+	require.NoError(t, err)
+	require.False(t, c.Languages.Bool)
+
+	c = IndexConfiguration{}
+	data = []byte(`{}`)
+	err = json.Unmarshal(data, &c)
+	require.NoError(t, err)
+	require.False(t, c.Languages.IsBool)
+}
diff --git a/cts/suggestions/config_test.go b/cts/suggestions/config_test.go
index 15cf8df6..e2f8df0a 100644
--- a/cts/suggestions/config_test.go
+++ b/cts/suggestions/config_test.go
@@ -45,7 +45,7 @@ func TestConfig(t *testing.T) {
 				Generate:      nil,
 			},
 		},
-		Languages: []string{"en"},
+		Languages: suggestions.NewStringArray([]string{"en"}),
 		Exclude:   nil,
 	}
 
@@ -61,7 +61,7 @@ func TestConfig(t *testing.T) {
 	})
 
 	t.Run("Update the query suggestion index", func(t *testing.T) {
-		indexConfig.Languages = []string{"ja"}
+		indexConfig.Languages = suggestions.NewStringArray([]string{"ja"})
 		err := querySuggestionsClient.UpdateConfig(indexConfig)
 		require.NoError(t, err)