From d2357167d8e54bc80204641416468b4be52245b0 Mon Sep 17 00:00:00 2001 From: Rhyanz46 Date: Thu, 11 Jul 2024 19:34:36 +0800 Subject: [PATCH] add `RequiredIf` check --- map_validator/functions.go | 36 +++++++++- map_validator/models.go | 2 + test/required_if_test.go | 124 ++++++++++++++++++++++++++++++++++ test/required_without_test.go | 6 +- 4 files changed, 163 insertions(+), 5 deletions(-) create mode 100644 test/required_if_test.go diff --git a/map_validator/functions.go b/map_validator/functions.go index bd2716c..10c7964 100644 --- a/map_validator/functions.go +++ b/map_validator/functions.go @@ -196,7 +196,39 @@ func validateRecursive(wrapper *RulesWrapper, key string, data map[string]interf } } if !required { - return nil, errors.New(fmt.Sprintf("if field '%s' is null you need to put value in this %v field", field, dependenciesField)) + return nil, errors.New(fmt.Sprintf("if field '%s' is null you need to put value in %v field", field, dependenciesField)) + } + } + } + + // put required if values + if wrapper != nil && len(rule.RequiredIf) > 0 { + for _, unique := range rule.RequiredIf { + if wrapper.requiredIf == nil { + wrapper.requiredIf = &map[string][]string{} + } + + if _, exists := (*wrapper.requiredIf)[unique]; !exists { + (*wrapper.requiredIf)[unique] = []string{} + } + (*wrapper.requiredIf)[unique] = append((*wrapper.requiredIf)[unique], key) + } + } + + if endOfLoop && wrapper.requiredIf != nil { + for _, field := range *wrapper.filledField { + var required bool + dependenciesField := (*wrapper.requiredIf)[field] + if len(dependenciesField) == 0 { + continue + } + for _, XField := range dependenciesField { + if !isDataInList(XField, *wrapper.nullFields) { + required = true + } + } + if !required { + return nil, errors.New(fmt.Sprintf("if field '%s' is filled you need to put value in %v field also", field, dependenciesField)) } } } @@ -233,7 +265,7 @@ func validate(field string, dataTemp map[string]interface{}, validator Rules, da var sliceData []interface{} //var isListObject bool - if len(validator.RequiredWithout) > 0 { + if len(validator.RequiredWithout) > 0 || len(validator.RequiredIf) > 0 { validator.Null = true } diff --git a/map_validator/models.go b/map_validator/models.go index b7154c1..75bcce7 100644 --- a/map_validator/models.go +++ b/map_validator/models.go @@ -39,6 +39,7 @@ type RulesWrapper struct { filledField *[]string nullFields *[]string requiredWithout *map[string][]string + requiredIf *map[string][]string } type Rules struct { @@ -60,6 +61,7 @@ type Rules struct { RegexString string Unique []string RequiredWithout []string + RequiredIf []string Object *RulesWrapper ListObject *RulesWrapper diff --git a/test/required_if_test.go b/test/required_if_test.go new file mode 100644 index 0000000..96ed9e2 --- /dev/null +++ b/test/required_if_test.go @@ -0,0 +1,124 @@ +package test + +import ( + "github.com/Rhyanz46/go-map-validator/map_validator" + "reflect" + "strings" + "testing" +) + +func TestRequiredIf(t *testing.T) { + check, err := map_validator.NewValidateBuilder().SetRules(map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "name": {Type: reflect.String}, + "flavor": {Type: reflect.String, RequiredWithout: []string{"name"}}, + "custom_flavor": {Type: reflect.String, RequiredIf: []string{"name"}}, + }, + }).Load(map[string]interface{}{ + "name": "SSD", + //"custom_flavor": "large", + }) + if err != nil { + t.Errorf("Expected not have error, but got error : %s", err) + return + } + + expected := "' is filled you need to put value in [" + _, err = check.RunValidate() + if err == nil || !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error contains '%s', but we got %s", expected, err) + } +} + +func TestChildRequiredIfStandardError(t *testing.T) { + role := map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "data": {Object: &map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "unit_size": {Type: reflect.Int, RequiredIf: []string{"size"}}, + "size": {Type: reflect.Int, RequiredIf: []string{"unit_size"}}, + }, + }}, + }, + } + payload := map[string]interface{}{ + "data": map[string]interface{}{ + "size": 1, + //"unit_size": 2, + }, + } + check, err := map_validator.NewValidateBuilder().SetRules(role).Load(payload) + if err != nil { + t.Errorf("Expected not have error, but got error : %s", err) + return + } + expected := "' is filled you need to put value in [" + _, err = check.RunValidate() + if err == nil || !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error contains '%s', but we got %s", expected, err) + } +} + +func TestChildRequiredIfStandardOk(t *testing.T) { + role := map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "data": {Object: &map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "unit_size": {Type: reflect.Int, RequiredIf: []string{"size"}}, + "size": {Type: reflect.Int, RequiredIf: []string{"unit_size"}}, + }, + }}, + }, + } + payload := map[string]interface{}{ + "data": map[string]interface{}{ + "size": 1, + "unit_size": 2, + }, + } + check, err := map_validator.NewValidateBuilder().SetRules(role).Load(payload) + if err != nil { + t.Errorf("Expected not have error, but got error : %s", err) + return + } + _, err = check.RunValidate() + if err != nil { + t.Errorf("Expected not have error, but got error : %s", err) + return + } +} + +func TestChildRequiredIf(t *testing.T) { + role := map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "data": {Object: &map_validator.RulesWrapper{ + Rules: map[string]map_validator.Rules{ + "name": {Type: reflect.String}, + "flavor": {Type: reflect.String, RequiredWithout: []string{"custom_flavor", "size"}}, + "custom_flavor": {Type: reflect.String, RequiredWithout: []string{"flavor", "size"}}, + "unit_size": {Enum: &map_validator.EnumField[any]{Items: []string{"GB", "MB", "TB"}}, RequiredIf: []string{"size"}}, + "size": {Type: reflect.Int, RequiredWithout: []string{"flavor", "custom_flavor"}, RequiredIf: []string{"unit_size"}}, + }, + }}, + }, + } + payload := map[string]interface{}{ + "data": map[string]interface{}{ + "name": "sabalong", + "flavor": "normal", + "size": 1, + //"unit_size": 2, + }, + } + check, err := map_validator.NewValidateBuilder().SetRules(role).Load(payload) + if err != nil { + t.Errorf("Expected not have error, but got error : %s", err) + return + } + expected := "' is filled you need to put value in [" + _, err = check.RunValidate() + + if err == nil || !strings.Contains(err.Error(), expected) { + t.Errorf("Expected error contains '%s', but we got %s", expected, err) + } +} diff --git a/test/required_without_test.go b/test/required_without_test.go index 5b2ea40..e712efa 100644 --- a/test/required_without_test.go +++ b/test/required_without_test.go @@ -22,7 +22,7 @@ func TestRequiredWithout(t *testing.T) { return } - expected := "is null you need to put value in this" + expected := "is null you need to put value in " _, err = check.RunValidate() if !strings.Contains(err.Error(), expected) { t.Errorf("Expected error with text at least %s, but we got %s", expected, err) @@ -53,9 +53,9 @@ func TestChildRequiredWithout(t *testing.T) { t.Errorf("Expected not have error, but got error : %s", err) return } - expected := "is null you need to put value in this" + expected := "is null you need to put value in " _, err = check.RunValidate() - if !strings.Contains(err.Error(), expected) { + if err == nil || !strings.Contains(err.Error(), expected) { t.Errorf("Expected error with text at least %s, but we got %s", expected, err) } }