Passed
Push — main ( 36e3e5...da04d5 )
by Rushan
01:11 queued 10s
created

dateBrand   A

Complexity

Conditions 4

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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