This is an automated email from the ASF dual-hosted git repository.

warren pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git


The following commit(s) were added to refs/heads/main by this push:
     new 0b4fd423 feat: support more viper data type
0b4fd423 is described below

commit 0b4fd42341f3fb01f3a4470d95b12f8fc51fa626
Author: fatelei <[email protected]>
AuthorDate: Tue May 24 16:46:24 2022 +0800

    feat: support more viper data type
---
 go.sum                             |  5 +-
 plugins/helper/config_util.go      | 83 +++++++++++++++++++++++++++-----
 plugins/helper/config_util_test.go | 99 +++++++++++++++++++++++++++++++++++++-
 3 files changed, 172 insertions(+), 15 deletions(-)

diff --git a/go.sum b/go.sum
index d4b04279..b9e4de6d 100644
--- a/go.sum
+++ b/go.sum
@@ -118,11 +118,11 @@ github.com/fsnotify/fsnotify v1.5.1/go.mod 
h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5
 github.com/ghodss/yaml v1.0.0/go.mod 
h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gin-contrib/cors v1.3.1 
h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
 github.com/gin-contrib/cors v1.3.1/go.mod 
h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
+github.com/gin-contrib/gzip v0.0.5 
h1:mhnVU32YnnBh2LPH2iqRqsA/eR7SAqRaD388jL2s/j0=
 github.com/gin-contrib/gzip v0.0.5/go.mod 
h1:OPIK6HR0Um2vNmBUTlayD7qle4yVVRZT0PyhdUigrKk=
 github.com/gin-contrib/sse v0.1.0 
h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod 
h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.5.0/go.mod 
h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
-github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
 github.com/gin-gonic/gin v1.7.4/go.mod 
h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
 github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
 github.com/gin-gonic/gin v1.7.7/go.mod 
h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
@@ -458,6 +458,7 @@ github.com/onsi/gomega v1.10.1/go.mod 
h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
 github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
 github.com/onsi/gomega v1.10.3/go.mod 
h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 github.com/opentracing/opentracing-go v1.1.0/go.mod 
h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/otiai10/copy v1.7.0 h1:hVoPiN+t+7d2nzzwMiDHPSOogsWAStewq3TwU05+clE=
 github.com/otiai10/copy v1.7.0/go.mod 
h1:rmRl6QPdJj6EiUqXQ/4Nn2lLXoNQjFCQbbNrxgc/t3U=
 github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod 
h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
 github.com/otiai10/curr v1.0.0/go.mod 
h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
@@ -647,6 +648,7 @@ golang.org/x/mod v0.2.0/go.mod 
h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -691,7 +693,6 @@ golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod 
h1:uSPa2vr4CLtc/ILN5o
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod 
h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod 
h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd 
h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod 
h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 
h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod 
h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
diff --git a/plugins/helper/config_util.go b/plugins/helper/config_util.go
index 49d2abf4..65a51a3c 100644
--- a/plugins/helper/config_util.go
+++ b/plugins/helper/config_util.go
@@ -18,10 +18,12 @@ limitations under the License.
 package helper
 
 import (
+       "fmt"
+       "reflect"
+
        "github.com/go-playground/validator/v10"
        "github.com/mitchellh/mapstructure"
        "github.com/spf13/viper"
-       "reflect"
 )
 
 // DecodeStruct validates `input` struct with `validator` and set it into viper
@@ -46,7 +48,7 @@ func DecodeStruct(output *viper.Viper, input interface{}, 
data map[string]interf
 
        vf := reflect.ValueOf(input)
        if vf.Kind() != reflect.Ptr {
-               panic("input is not a pointer")
+               return fmt.Errorf("input %v is not a pointer", input)
        }
        tf := reflect.Indirect(vf).Type()
        fieldTags := make([]string, 0)
@@ -70,13 +72,34 @@ func DecodeStruct(output *viper.Viper, input interface{}, 
data map[string]interf
                        continue
                }
                vfField := vf.Elem().FieldByName(fieldName)
-               switch fieldType.String() {
-               case "string":
+               switch fieldType.Kind() {
+               case reflect.String:
                        output.Set(fieldTag, vfField.String())
-               case "int", "int64":
+               case reflect.Int, reflect.Int64:
                        output.Set(fieldTag, vfField.Int())
-               case "float64":
+               case reflect.Float64, reflect.Float32:
                        output.Set(fieldTag, vfField.Float())
+               case reflect.Bool:
+                       output.Set(fieldTag, vfField.Bool())
+               case reflect.Slice:
+                       elemType := vfField.Type().Elem().Kind()
+                       switch elemType {
+                       case reflect.String:
+                               output.Set(fieldTag, 
vfField.Interface().([]string))
+                       case reflect.Int:
+                               output.Set(fieldTag, 
vfField.Interface().([]int))
+                       }
+               case reflect.Map:
+                       keyType := vfField.Type().Key().Kind()
+                       elemType := vfField.Type().Elem().Kind()
+                       if keyType == reflect.String {
+                               switch elemType {
+                               case reflect.String:
+                                       output.Set(fieldTag, 
vfField.Interface().(map[string]string))
+                               case reflect.Interface:
+                                       output.Set(fieldTag, 
vfField.Interface().(map[string]interface{}))
+                               }
+                       }
                default:
                }
        }
@@ -89,7 +112,7 @@ func DecodeStruct(output *viper.Viper, input interface{}, 
data map[string]interf
 func EncodeStruct(input *viper.Viper, output interface{}, tag string) error {
        vf := reflect.ValueOf(output)
        if vf.Kind() != reflect.Ptr {
-               panic("output is not a pointer")
+               return fmt.Errorf("output %v is not a pointer", output)
        }
        tf := reflect.Indirect(vf).Type()
        fieldTags := make([]string, 0)
@@ -113,13 +136,51 @@ func EncodeStruct(input *viper.Viper, output interface{}, 
tag string) error {
                        continue
                }
                vfField := vf.Elem().FieldByName(fieldName)
-               switch fieldType.String() {
-               case "string":
+               switch fieldType.Kind() {
+               case reflect.String:
                        vfField.SetString(input.GetString(fieldTag))
-               case "int", "int64":
+               case reflect.Int, reflect.Int64:
                        vfField.SetInt(input.GetInt64(fieldTag))
-               case "float64":
+               case reflect.Float64:
                        vfField.SetFloat(input.GetFloat64(fieldTag))
+               case reflect.Bool:
+                       vfField.SetBool(input.GetBool(fieldTag))
+               case reflect.Slice:
+                       elem := vfField.Type().Elem()
+                       switch elem.Kind() {
+                       case reflect.String:
+                               value := input.GetStringSlice(fieldTag)
+                               stringSlice := 
reflect.MakeSlice(reflect.SliceOf(elem), 0, len(value))
+                               for _, item := range value {
+                                       stringSlice = 
reflect.Append(stringSlice, reflect.ValueOf(item))
+                               }
+                               vfField.Set(stringSlice)
+                       case reflect.Int:
+                               value := input.GetIntSlice(fieldTag)
+                               intSlice := 
reflect.MakeSlice(reflect.SliceOf(elem), 0, len(value))
+                               for _, item := range value {
+                                       intSlice = reflect.Append(intSlice, 
reflect.ValueOf(item))
+                               }
+                               vfField.Set(intSlice)
+                       }
+               case reflect.Map:
+                       key := vfField.Type().Key()
+                       elem := vfField.Type().Elem()
+                       if key.Kind() == reflect.String {
+                               mapType := reflect.MapOf(key, elem)
+                               data := reflect.MakeMap(mapType)
+                               switch elem.Kind() {
+                               case reflect.String:
+                                       for k, value := range 
input.GetStringMapString(fieldTag) {
+                                               
data.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(value))
+                                       }
+                               case reflect.Interface:
+                                       for k, value := range 
input.GetStringMap(fieldTag) {
+                                               
data.SetMapIndex(reflect.ValueOf(k), reflect.ValueOf(value))
+                                       }
+                               }
+                               vfField.Set(data)
+                       }
                default:
                }
        }
diff --git a/plugins/helper/config_util_test.go 
b/plugins/helper/config_util_test.go
index 0ddb21f6..1332d940 100644
--- a/plugins/helper/config_util_test.go
+++ b/plugins/helper/config_util_test.go
@@ -20,8 +20,9 @@ package helper
 import (
        "testing"
 
-       "github.com/apache/incubator-devlake/config"
        "github.com/stretchr/testify/assert"
+
+       "github.com/apache/incubator-devlake/config"
 )
 
 type TestStruct struct {
@@ -32,6 +33,17 @@ type TestStruct struct {
        F5 string  `env:"TEST_F5"`
 }
 
+type TestComplexStruct struct {
+       F1 string                 `env:"TEST_F1"`
+       F2 int                    `env:"TEST_F2"`
+       F3 float64                `env:"TEST_F3" mapstructure:"TEST_F3"`
+       F4 []int                  `env:"TEST_F4"`
+       F5 []string               `env:"TEST_F5"`
+       F6 bool                   `env:"TEST_F6"`
+       F7 map[string]string      `env:"TEST_F7"`
+       F8 map[string]interface{} `env:"TEST_F8"`
+}
+
 func TestSaveToConfig(t *testing.T) {
        ts := TestStruct{
                F1: "123",
@@ -54,16 +66,99 @@ func TestSaveToConfig(t *testing.T) {
        assert.Equal(t, v4, "Test")
 }
 
+func TestSaveToComplexityConfig(t *testing.T) {
+       ts := TestComplexStruct{
+               F1: "123",
+               F2: 76,
+               F3: 1.23,
+               F4: []int{1, 2, 3},
+               F5: []string{"a", "b", "c"},
+               F6: true,
+               F7: map[string]string{
+                       "foo": "bar",
+               },
+               F8: map[string]interface{}{
+                       "foo1": "bar1",
+               },
+       }
+       data := make(map[string]interface{})
+
+       v := config.GetConfig()
+       assert.Nil(t, DecodeStruct(v, &ts, data, "env"))
+       v1 := v.GetString("TEST_F1")
+       assert.Equal(t, v1, "123")
+       v2 := v.GetInt("TEST_F2")
+       assert.Equal(t, v2, 76)
+       v3 := v.GetFloat64("TEST_F3")
+       assert.Equal(t, v3, 1.23)
+       v4 := v.GetIntSlice("TEST_F4")
+       assert.Equal(t, []int{1, 2, 3}, v4)
+       v5 := v.GetStringSlice("TEST_F5")
+       assert.Equal(t, []string{"a", "b", "c"}, v5)
+       v6 := v.GetBool("TEST_F6")
+       assert.Equal(t, v6, true)
+       v7 := v.GetStringMapString("TEST_F7")
+       assert.Equal(t, map[string]string{"foo": "bar"}, v7)
+       v8 := v.GetStringMap("TEST_F8")
+       assert.Equal(t, map[string]interface{}{"foo1": "bar1"}, v8)
+}
+
 func TestLoadFromConfig(t *testing.T) {
        v := config.GetConfig()
+       ts := TestStruct{
+               F1: "123",
+               F2: 76,
+               F3: 1.23,
+               F4: "Test",
+               F5: "No Use",
+       }
+       data := make(map[string]interface{})
+       assert.Nil(t, DecodeStruct(v, &ts, data, "env"))
+
        vF := TestStruct{}
        err := EncodeStruct(v, &vF, "env")
        if err != nil {
-               panic(err)
+               assert.Error(t, err)
        }
        //assert.Nil(t, x)
        assert.Equal(t, vF.F1, "123")
        assert.Equal(t, vF.F2, 76)
        assert.Equal(t, vF.F3, 1.23)
        assert.Equal(t, vF.F4, "Test")
+       assert.Equal(t, vF.F5, "No Use")
+}
+
+func TestLoadFromComplexityConfig(t *testing.T) {
+       v := config.GetConfig()
+       ts := TestComplexStruct{
+               F1: "123",
+               F2: 76,
+               F3: 1.23,
+               F4: []int{1, 2, 3},
+               F5: []string{"a", "b", "c"},
+               F6: true,
+               F7: map[string]string{
+                       "foo": "bar",
+               },
+               F8: map[string]interface{}{
+                       "foo1": "bar1",
+               },
+       }
+       data := make(map[string]interface{})
+       assert.Nil(t, DecodeStruct(v, &ts, data, "env"))
+
+       vF := TestComplexStruct{}
+       err := EncodeStruct(v, &vF, "env")
+       if err != nil {
+               assert.Error(t, err)
+       }
+       //assert.Nil(t, x)
+       assert.Equal(t, vF.F1, "123")
+       assert.Equal(t, vF.F2, 76)
+       assert.Equal(t, vF.F3, 1.23)
+       assert.Equal(t, vF.F4, []int{1, 2, 3})
+       assert.Equal(t, vF.F5, []string{"a", "b", "c"})
+       assert.Equal(t, vF.F6, true)
+       assert.Equal(t, vF.F7, map[string]string{"foo": "bar"})
+       assert.Equal(t, vF.F8, map[string]interface{}{"foo1": "bar1"})
 }

Reply via email to