Test Failed
Pull Request — main (#71)
by Igor
02:01
created

validation_test.ValidBrand   A

Complexity

Conditions 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nop 2
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
package validation_test
2
3
import (
4
	"context"
5
	"fmt"
6
7
	"github.com/muonsoft/validation"
8
	"github.com/muonsoft/validation/validator"
9
)
10
11
type Brand struct {
12
	Name string
13
}
14
15
type BrandRepository struct {
16
	brands []Brand
17
}
18
19
func (repository *BrandRepository) FindByName(ctx context.Context, name string) ([]Brand, error) {
20
	found := make([]Brand, 0)
21
22
	for _, brand := range repository.brands {
23
		if brand.Name == name {
24
			found = append(found, brand)
25
		}
26
	}
27
28
	return found, nil
29
}
30
31
// You can declare you own constraint interface to create custom constraints.
32
type BrandConstraint interface {
33
	ValidateBrand(brand *Brand, scope validation.Scope) error
34
}
35
36
// To create your own functional argument for validation simply create a function with
37
// a typed value and use the validation.NewArgument constructor.
38
func ValidBrand(brand *Brand, constraints ...BrandConstraint) validation.ValidatorArgument {
39
	return validation.NewArgument(func(scope validation.Scope) (*validation.ViolationList, error) {
40
		violations := validation.NewViolationList()
41
42
		for i := range constraints {
43
			err := violations.AppendFromError(constraints[i].ValidateBrand(brand, scope))
44
			if err != nil {
45
				return nil, err
46
			}
47
		}
48
49
		return violations, nil
50
	})
51
}
52
53
// UniqueBrandConstraint implements BrandConstraint.
54
type UniqueBrandConstraint struct {
55
	brands *BrandRepository
56
}
57
58
func (c *UniqueBrandConstraint) SetUp() error {
59
	return nil
60
}
61
62
func (c *UniqueBrandConstraint) Name() string {
63
	return "UniqueBrandConstraint"
64
}
65
66
func (c *UniqueBrandConstraint) ValidateBrand(brand *Brand, scope validation.Scope) error {
67
	// usually, you should ignore empty values
68
	// to check for an empty value you should use it.NotBlankConstraint
69
	if brand == nil {
70
		return nil
71
	}
72
73
	// you can pass the context value from the scope
74
	brands, err := c.brands.FindByName(scope.Context(), brand.Name)
75
	// here you can return a service error so that the validation process
76
	// is stopped immediately
77
	if err != nil {
78
		return err
79
	}
80
	if len(brands) == 0 {
81
		return nil
82
	}
83
84
	// use the scope to build violation with translations
85
	return scope.
86
		BuildViolation("notUniqueBrand", `Brand with name "{{ name }}" already exists.`).
87
		// you can inject parameter value to the message here
88
		AddParameter("{{ name }}", brand.Name).
89
		CreateViolation()
90
}
91
92
func ExampleNewArgument_customArgumentConstraintValidator() {
93
	repository := &BrandRepository{brands: []Brand{{"Apple"}, {"Orange"}}}
94
	isEntityUnique := &UniqueBrandConstraint{brands: repository}
95
96
	brand := Brand{Name: "Apple"}
97
98
	err := validator.Validate(
99
		// you can pass here the context value to the validation scope
100
		context.WithValue(context.Background(), exampleKey, "value"),
101
		ValidBrand(&brand, isEntityUnique),
102
	)
103
104
	fmt.Println(err)
105
	// Output:
106
	// violation: Brand with name "Apple" already exists.
107
}
108