Test Setup Failed
Pull Request — main (#58)
by Igor
01:34
created

validation_test.NestingLimitConstraint.SetUp   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nop 0
dl 0
loc 2
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/it"
9
	"github.com/muonsoft/validation/validator"
10
)
11
12
// It is recommended to make a custom constraint to check for nesting limit.
13
type NestingLimitConstraint struct {
14
	limit int
15
}
16
17
func (c NestingLimitConstraint) SetUp() error {
18
	return nil
19
}
20
21
func (c NestingLimitConstraint) Name() string {
22
	return "NestingLimitConstraint"
23
}
24
25
func (c NestingLimitConstraint) ValidateProperty(property *Property, scope validation.Scope) error {
26
	// You can read any passed context value from scope.
27
	level, ok := scope.Context().Value(nestingLevelKey).(int)
28
	if !ok {
29
		// Don't forget to handle missing value.
30
		return fmt.Errorf("nesting level not found in context")
31
	}
32
33
	if level >= c.limit {
34
		return scope.
35
			BuildViolation("nestingLimitReached", "Maximum nesting level reached.").
36
			CreateViolation()
37
	}
38
39
	return nil
40
}
41
42
func ItIsNotDeeperThan(limit int) NestingLimitConstraint {
43
	return NestingLimitConstraint{limit: limit}
44
}
45
46
// Properties can be nested.
47
type Property struct {
48
	Name       string
49
	Properties []Property
50
}
51
52
// You can declare you own constraint interface to create custom constraints.
53
type PropertyConstraint interface {
54
	validation.Constraint
55
	ValidateProperty(property *Property, scope validation.Scope) error
56
}
57
58
// To create your own functional argument for validation simply create a function with
59
// a typed value and use the validation.NewArgument constructor.
60
func PropertyArgument(property *Property, options ...validation.Option) validation.Argument {
61
	return validation.NewArgument(options, func(constraint validation.Constraint, scope validation.Scope) error {
62
		if c, ok := constraint.(PropertyConstraint); ok {
63
			return c.ValidateProperty(property, scope)
64
		}
65
		// If you want to use built-in constraints for checking for nil or empty values
66
		// such as it.IsNil() or it.IsBlank().
67
		if c, ok := constraint.(validation.NilConstraint); ok {
68
			if property == nil {
69
				return c.ValidateNil(scope)
70
			}
71
			return nil
72
		}
73
74
		return validation.NewInapplicableConstraintError(constraint, "Property")
75
	})
76
}
77
78
type recursionKey string
79
80
const nestingLevelKey recursionKey = "nestingLevel"
81
82
func (p Property) Validate(ctx context.Context, validator *validation.Validator) error {
83
	return validator.Validate(
84
		// Incrementing nesting level in context with special function.
85
		contextWithNextNestingLevel(ctx),
86
		// Executing validation for maximum nesting level of properties.
87
		PropertyArgument(&p, ItIsNotDeeperThan(3)),
88
		validation.StringProperty("name", p.Name, it.IsNotBlank()),
89
		// This should run recursive validation for properties.
90
		validation.IterableProperty("properties", p.Properties),
91
	)
92
}
93
94
// This function increments current nesting level.
95
func contextWithNextNestingLevel(ctx context.Context) context.Context {
96
	level, ok := ctx.Value(nestingLevelKey).(int)
97
	if !ok {
98
		level = -1
99
	}
100
101
	return context.WithValue(ctx, nestingLevelKey, level+1)
102
}
103
104
func ExampleValidator_Validate_usingContextWithRecursion() {
105
	properties := []Property{
106
		{
107
			Name: "top",
108
			Properties: []Property{
109
				{
110
					Name: "middle",
111
					Properties: []Property{
112
						{
113
							Name: "low",
114
							Properties: []Property{
115
								// This property should cause a violation.
116
								{Name: "limited"},
117
							},
118
						},
119
					},
120
				},
121
			},
122
		},
123
	}
124
125
	err := validator.Validate(context.Background(), validation.Iterable(properties))
126
127
	fmt.Println(err)
128
	// Output:
129
	// violation at '[0].properties[0].properties[0].properties[0]': Maximum nesting level reached.
130
}
131