Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: validate value in options for mapping #2616

Merged
merged 1 commit into from Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions core/mapping/unmarshaler.go
Expand Up @@ -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:
Expand Down
221 changes: 151 additions & 70 deletions core/mapping/unmarshaler_test.go
Expand Up @@ -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) {
Expand Down Expand Up @@ -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)
}
}