Repository: zeromicro/go-zero
Pull request #2616 (2022-11-18 11:33:44)

feat: validate value in options for mapping


Patch:
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index c75bc50c..ee8c4d35 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -341,6 +341,10 @@ func (u *Unmarshaler) processFieldTextUnmarshaler(field reflect.StructField, val
 
 func (u *Unmarshaler) processFieldWithEnvValue(field reflect.StructField, value reflect.Value,
 	envVal string, opts *fieldOptionsWithContext, fullName string) error {
+	if err := validateValueInOptions(envVal, opts.options()); err != nil {
+		return err
+	}
+
 	fieldKind := field.Type.Kind()
 	switch fieldKind {
 	case reflect.Bool:


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index f806bfbb..51eb36c9 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -3254,91 +3254,85 @@ func TestUnmarshal_EnvDurationBadValue(t *testing.T) {
 	assert.NotNil(t, UnmarshalKey(emptyMap, &v))
 }
 
-func BenchmarkUnmarshalString(b *testing.B) {
-	type inner struct {
-		Value string `key:"value"`
-	}
-	m := map[string]interface{}{
-		"value": "first",
+func TestUnmarshal_EnvWithOptions(t *testing.T) {
+	type Value struct {
+		Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"`
 	}
 
-	for i := 0; i < b.N; i++ {
-		var in inner
-		if err := UnmarshalKey(m, &in); err != nil {
-			b.Fatal(err)
-		}
-	}
-}
+	const (
+		envName = "TEST_NAME_ENV_OPTIONS_MATCH"
+		envVal  = "123"
+	)
+	os.Setenv(envName, envVal)
+	defer os.Unsetenv(envName)
 
-func BenchmarkUnmarshalStruct(b *testing.B) {
-	b.ReportAllocs()
+	var v Value
+	assert.NoError(t, UnmarshalKey(emptyMap, &v))
+	assert.Equal(t, envVal, v.Name)
+}
 
-	m := map[string]interface{}{
-		"Ids": []map[string]interface{}{
-			{
-				"First":  1,
-				"Second": 2,
-			},
-		},
+func TestUnmarshal_EnvWithOptionsWrongValueBool(t *testing.T) {
+	type Value struct {
+		Enable bool `key:"enable,env=TEST_NAME_ENV_OPTIONS_BOOL,options=[true]"`
 	}
 
-	for i := 0; i < b.N; i++ {
-		var v struct {
-			Ids []struct {
-				First  int
-				Second int
-			}
-		}
-		if err := UnmarshalKey(m, &v); err != nil {
-			b.Fatal(err)
-		}
-	}
+	const (
+		envName = "TEST_NAME_ENV_OPTIONS_BOOL"
+		envVal  = "false"
+	)
+	os.Setenv(envName, envVal)
+	defer os.Unsetenv(envName)
+
+	var v Value
+	assert.Error(t, UnmarshalKey(emptyMap, &v))
 }
 
-func BenchmarkMapToStruct(b *testing.B) {
-	data := map[string]interface{}{
-		"valid": "1",
-		"age":   "5",
-		"name":  "liao",
-	}
-	type anonymous struct {
-		Valid bool
-		Age   int
-		Name  string
+func TestUnmarshal_EnvWithOptionsWrongValueDuration(t *testing.T) {
+	type Value struct {
+		Duration time.Duration `key:"duration,env=TEST_NAME_ENV_OPTIONS_DURATION,options=[1s,2s,3s]"`
 	}
 
-	for i := 0; i < b.N; i++ {
-		var an anonymous
-		if valid, ok := data["valid"]; ok {
-			an.Valid = valid == "1"
-		}
-		if age, ok := data["age"]; ok {
-			ages, _ := age.(string)
-			an.Age, _ = strconv.Atoi(ages)
-		}
-		if name, ok := data["name"]; ok {
-			names, _ := name.(string)
-			an.Name = names
-		}
-	}
+	const (
+		envName = "TEST_NAME_ENV_OPTIONS_DURATION"
+		envVal  = "4s"
+	)
+	os.Setenv(envName, envVal)
+	defer os.Unsetenv(envName)
+
+	var v Value
+	assert.Error(t, UnmarshalKey(emptyMap, &v))
 }
 
-func BenchmarkUnmarshal(b *testing.B) {
-	data := map[string]interface{}{
-		"valid": "1",
-		"age":   "5",
-		"name":  "liao",
-	}
-	type anonymous struct {
-		Valid bool   `key:"valid,string"`
-		Age   int    `key:"age,string"`
-		Name  string `key:"name"`
+func TestUnmarshal_EnvWithOptionsWrongValueNumber(t *testing.T) {
+	type Value struct {
+		Age int `key:"age,env=TEST_NAME_ENV_OPTIONS_AGE,options=[18,19,20]"`
 	}
 
-	for i := 0; i < b.N; i++ {
-		var an anonymous
-		UnmarshalKey(data, &an)
+	const (
+		envName = "TEST_NAME_ENV_OPTIONS_AGE"
+		envVal  = "30"
+	)
+	os.Setenv(envName, envVal)
+	defer os.Unsetenv(envName)
+
+	var v Value
+	assert.Error(t, UnmarshalKey(emptyMap, &v))
+}
+
+func TestUnmarshal_EnvWithOptionsWrongValueString(t *testing.T) {
+	type Value struct {
+		Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_STRING,options=[abc,123,xyz]"`
 	}
+
+	const (
+		envName = "TEST_NAME_ENV_OPTIONS_STRING"
+		envVal  = "this is a name"
+	)
+	os.Setenv(envName, envVal)
+	defer os.Unsetenv(envName)
+
+	var v Value
+	assert.Error(t, UnmarshalKey(emptyMap, &v))
 }
 
 func TestUnmarshalJsonReaderMultiArray(t *testing.T) {
@@ -3581,3 +3575,90 @@ func BenchmarkDefaultValue(b *testing.B) {
 		}
 	}
 }
+
+func BenchmarkUnmarshalString(b *testing.B) {
+	type inner struct {
+		Value string `key:"value"`
+	}
+	m := map[string]interface{}{
+		"value": "first",
+	}
+
+	for i := 0; i < b.N; i++ {
+		var in inner
+		if err := UnmarshalKey(m, &in); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func BenchmarkUnmarshalStruct(b *testing.B) {
+	b.ReportAllocs()
+
+	m := map[string]interface{}{
+		"Ids": []map[string]interface{}{
+			{
+				"First":  1,
+				"Second": 2,
+			},
+		},
+	}
+
+	for i := 0; i < b.N; i++ {
+		var v struct {
+			Ids []struct {
+				First  int
+				Second int
+			}
+		}
+		if err := UnmarshalKey(m, &v); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func BenchmarkMapToStruct(b *testing.B) {
+	data := map[string]interface{}{
+		"valid": "1",
+		"age":   "5",
+		"name":  "liao",
+	}
+	type anonymous struct {
+		Valid bool
+		Age   int
+		Name  string
+	}
+
+	for i := 0; i < b.N; i++ {
+		var an anonymous
+		if valid, ok := data["valid"]; ok {
+			an.Valid = valid == "1"
+		}
+		if age, ok := data["age"]; ok {
+			ages, _ := age.(string)
+			an.Age, _ = strconv.Atoi(ages)
+		}
+		if name, ok := data["name"]; ok {
+			names, _ := name.(string)
+			an.Name = names
+		}
+	}
+}
+
+func BenchmarkUnmarshal(b *testing.B) {
+	data := map[string]interface{}{
+		"valid": "1",
+		"age":   "5",
+		"name":  "liao",
+	}
+	type anonymous struct {
+		Valid bool   `key:"valid,string"`
+		Age   int    `key:"age,string"`
+		Name  string `key:"name"`
+	}
+
+	for i := 0; i < b.N; i++ {
+		var an anonymous
+		UnmarshalKey(data, &an)
+	}
+}