Repository: Nerzal/gocloak
Pull request #32 (2019-03-14 13:15:33)

Remove duplicated code when using GetQueryParams.
Since the inheritance in GO structures doesn't work as i expected,
the BaseParams structure and GetQueryParams functons for each of the
Params structures have been removed as uneeded.
It makes the code simpler. Also I added the unit tests for GetQueryParams.




Patch:
diff --git a/client.go b/client.go
index 8ad4574..02f2a27 100644
--- a/client.go
+++ b/client.go
@@ -364,7 +364,7 @@ func (client *gocloak) SetPassword(token string, userID string, realm string, pa
 
 // ExecuteActionsEmail executes an actions email
 func (client *gocloak) ExecuteActionsEmail(token string, realm string, params ExecuteActionsEmail) error {
-	queryParams, err := params.GetQueryParams()
+	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return err
 	}
@@ -609,7 +609,7 @@ func (client *gocloak) GetComponents(token string, realm string) (*[]Component,
 // GetUsers get all users in realm
 func (client *gocloak) GetUsers(token string, realm string, params GetUsersParams) (*[]User, error) {
 	var result []User
-	queryParams, err := params.GetQueryParams()
+	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return nil, err
 	}
@@ -694,7 +694,7 @@ func (client *gocloak) GetGroup(token string, realm string, groupID string) (*Gr
 // GetGroups get all groups in realm
 func (client *gocloak) GetGroups(token string, realm string, params GetGroupsParams) (*[]Group, error) {
 	var result []Group
-	queryParams, err := params.GetQueryParams()
+	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return nil, err
 	}
@@ -742,7 +742,7 @@ func (client *gocloak) GetClientRole(token string, realm string, clientID string
 // GetClients gets all clients in realm
 func (client *gocloak) GetClients(token string, realm string, params GetClientsParams) (*[]Client, error) {
 	var result []Client
-	queryParams, err := params.GetQueryParams()
+	queryParams, err := GetQueryParams(params)
 	if err != nil {
 		return nil, err
 	}
diff --git a/models.go b/models.go
index 5bdc3f8..5257ade 100644
--- a/models.go
+++ b/models.go
@@ -2,8 +2,13 @@ package gocloak
 
 import "encoding/json"
 
-// getQueryParams converts the struct to map[string]string
-func getQueryParams(s interface{}) (map[string]string, error) {
+// GetQueryParams converts the struct to map[string]string
+// The fields tags must have `json:"<name>,string,omitempty"` format for all types, except strings
+// The string fields must have: `json:"<name>,omitempty"`. The `json:"<name>,string,omitempty"` tag for string field
+// will add additional double quotes.
+// "string" tag allows to convert the non-string fields of a structure to map[string]string.
+// "omitempty" allows to skip the fields with default values.
+func GetQueryParams(s interface{}) (map[string]string, error) {
 	b, err := json.Marshal(s)
 	if err != nil {
 		return nil, err
@@ -13,18 +18,7 @@ func getQueryParams(s interface{}) (map[string]string, error) {
 	if err != nil {
 		return nil, err
 	}
-	return res, nil	
-}
-
-// BaseParams provides basic functionality for all QueryParams structures.
-// The fields tags must have `json:"<name>,string,omitempty"` format.
-// "string" tag allows to convert the structure to map[string]string.
-// "omitempty" allows to skip the fields with default values.
-type BaseParams struct{}
-
-// GetQueryParams converts the struct to map[string]string
-func (s BaseParams) GetQueryParams() (map[string]string, error) {
-	return getQueryParams(s)
+	return res, nil
 }
 
 // APIError represents an api error
@@ -166,7 +160,6 @@ type UserGroup struct {
 
 // GetUsersParams represents the optional parameters for getting users
 type GetUsersParams struct {
-	BaseParams
 	BriefRepresentation *bool  `json:"briefRepresentation,string,omitempty"`
 	Email               string `json:"email,omitempty"`
 	First               int    `json:"first,string,omitempty"`
@@ -177,14 +170,8 @@ type GetUsersParams struct {
 	Username            string `json:"username,omitempty"`
 }
 
-// GetQueryParams converts the struct to map[string]string
-func (s GetUsersParams) GetQueryParams() (map[string]string, error) {
-	return getQueryParams(s)
-}
-
 // ExecuteActionsEmail represents parameters for executing action emails
 type ExecuteActionsEmail struct {
-	BaseParams
 	UserID      string   `json:"-"`
 	ClientID    string   `json:"clientId,omitempty"`
 	Lifespan    int      `json:"lifespan,string,omitempty"`
@@ -192,11 +179,6 @@ type ExecuteActionsEmail struct {
 	Actions     []string `json:"-"`
 }
 
-// GetQueryParams converts the struct to map[string]string
-func (s ExecuteActionsEmail) GetQueryParams() (map[string]string, error) {
-	return getQueryParams(s)
-}
-
 // Group is a Group
 type Group struct {
 	ID        string        `json:"id,omitempty"`
@@ -207,17 +189,11 @@ type Group struct {
 
 // GetGroupsParams represents the optional parameters for getting groups
 type GetGroupsParams struct {
-	BaseParams
 	First  int    `json:"first,string,omitempty"`
 	Max    int    `json:"max,string,omitempty"`
 	Search string `json:"search,omitempty"`
 }
 
-// GetQueryParams converts the struct to map[string]string
-func (s GetGroupsParams) GetQueryParams() (map[string]string, error) {
-	return getQueryParams(s)
-}
-
 // Role is a role
 type Role struct {
 	ID                 string              `json:"id,omitempty"`
@@ -287,16 +263,10 @@ type Client struct {
 
 // GetClientsParams represents the query parameters
 type GetClientsParams struct {
-	BaseParams
 	ClientID     string `json:"clientId,omitempty"`
 	ViewableOnly bool   `json:"viewableOnly,string,omitempty"`
 }
 
-// GetQueryParams converts the struct to map[string]string
-func (s GetClientsParams) GetQueryParams() (map[string]string, error) {
-	return getQueryParams(s)
-}
-
 // UserInfo is returned by the userinfo endpoint
 type UserInfo struct {
 	Sub               string      `json:"sub"`


Test patch:
diff --git a/client_test.go b/client_test.go
index 571c52d..3fe9983 100644
--- a/client_test.go
+++ b/client_test.go
@@ -10,6 +10,7 @@ import (
 	"net/url"
 	"os"
 	"path/filepath"
+	"reflect"
 	"runtime"
 	"strconv"
 	"sync"
@@ -64,6 +65,14 @@ func FailIf(t *testing.T, cond bool, msg string, args ...interface{}) {
 	}
 }
 
+func AssertEquals(t *testing.T, exp interface{}, act interface{}) {
+	FailIf(
+		t,
+		!reflect.DeepEqual(exp, act),
+		"The expected and actual results are not equal.\nExpected: %+v.\nActual:   %+v", exp, act)
+}
+
+
 func GetConfig(t *testing.T) *Config {
 	configOnce.Do(func() {
 		configFileName, ok := os.LookupEnv("GOCLOAK_TEST_CONFIG")
@@ -261,6 +270,39 @@ func SetUpTestUser(t *testing.T, client GoCloak) {
 	})
 }
 
+func TestBaseParams_GetQueryParams(t *testing.T) {
+	t.Parallel()
+
+	type TestParams struct {
+		IntField    int    `json:"int_field,string,omitempty"`
+		StringField string `json:"string_field,omitempty"`
+		BoolField   bool   `json:"bool_field,string,omitempty"`
+	}
+
+	params, err := GetQueryParams(TestParams{})
+	FailIfErr(t, err, "GetQueryParams failed")
+	FailIf(
+		t,
+		len(params) > 0,
+		"Params must be empty, but got: %+v", params)
+
+	params, err = GetQueryParams(TestParams{
+		IntField:    1,
+		StringField: "fake",
+		BoolField:   true,
+	})
+	FailIfErr(t, err, "GetQueryParams failed")
+	AssertEquals(t, map[string]string{
+		"int_field": "1",
+		"string_field": "fake",
+		"bool_field": "true",
+	}, params)
+}
+
+// ---------
+// API tests
+// ---------
+
 func TestGocloak_RequestPermission(t *testing.T) {
 	t.Parallel()
 	cfg := GetConfig(t)
@@ -776,7 +818,8 @@ func CreateRealmRole(t *testing.T, client GoCloak) (func(), string) {
 		token.AccessToken,
 		cfg.GoCloak.Realm,
 		Role{
-			Name: roleName,
+			Name:        roleName,
+			ContainerID: "asd",
 		})
 	FailIfErr(t, err, "CreateRealmRole failed")
 	tearDown := func() {