mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-19 15:39:17 +00:00
Add DV tag '+k8s:minItems'
This commit is contained in:
@@ -78,6 +78,14 @@ func MaxItems[T any](_ context.Context, _ operation.Operation, fldPath *field.Pa
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinItems verifies that the specified slice is not shorter than min items.
|
||||
func MinItems[T any](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ []T, min int) field.ErrorList {
|
||||
if len(value) < min {
|
||||
return field.ErrorList{field.TooFew(fldPath, len(value), min).WithOrigin("minItems")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Minimum verifies that the specified value is greater than or equal to min.
|
||||
func Minimum[T constraints.Integer](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, min T) field.ErrorList {
|
||||
if value == nil {
|
||||
|
||||
@@ -116,6 +116,54 @@ func TestMaxLength(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinItems(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
items int
|
||||
min int
|
||||
wantErrs field.ErrorList
|
||||
}{{
|
||||
name: "0 items, min 0",
|
||||
items: 0,
|
||||
min: 0,
|
||||
}, {
|
||||
name: "1 item, min 0",
|
||||
items: 1,
|
||||
min: 0,
|
||||
}, {
|
||||
name: "1 item, min 1",
|
||||
items: 1,
|
||||
min: 1,
|
||||
}, {
|
||||
name: "0 items, min 1",
|
||||
items: 0,
|
||||
min: 1,
|
||||
wantErrs: field.ErrorList{
|
||||
field.TooFew(field.NewPath("fldpath"), 0, 1).WithOrigin("minItems"),
|
||||
},
|
||||
}, {
|
||||
name: "1 item, min 2",
|
||||
items: 1,
|
||||
min: 2,
|
||||
wantErrs: field.ErrorList{
|
||||
field.TooFew(field.NewPath("fldpath"), 1, 2).WithOrigin("minItems"),
|
||||
},
|
||||
}, {
|
||||
name: "0 items, min -1",
|
||||
items: 0,
|
||||
min: -1,
|
||||
}}
|
||||
|
||||
matcher := field.ErrorMatcher{}.ByOrigin().ByDetailSubstring().ByField().ByType()
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
value := make([]bool, tc.items)
|
||||
gotErrs := MinItems(context.Background(), operation.Operation{}, field.NewPath("fldpath"), value, nil, tc.min)
|
||||
matcher.Test(t, tc.wantErrs, gotErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxItems(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
||||
@@ -112,7 +112,7 @@ func (e *Error) ErrorBody() string {
|
||||
case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeTooShort, ErrorTypeInternal:
|
||||
s = e.Type.String()
|
||||
case ErrorTypeInvalid, ErrorTypeTypeInvalid, ErrorTypeNotSupported,
|
||||
ErrorTypeNotFound, ErrorTypeDuplicate, ErrorTypeTooMany:
|
||||
ErrorTypeNotFound, ErrorTypeDuplicate, ErrorTypeTooMany, ErrorTypeTooFew:
|
||||
if e.BadValue == omitValue {
|
||||
s = e.Type.String()
|
||||
break
|
||||
@@ -201,6 +201,10 @@ const (
|
||||
// report that a given list has too many items. This is similar to FieldValueTooLong,
|
||||
// but the error indicates quantity instead of length.
|
||||
ErrorTypeTooMany ErrorType = "FieldValueTooMany"
|
||||
// ErrorTypeTooFew is used to report "too few". This is used to
|
||||
// report that a given list has too few items. This is similar to FieldValueTooLong,
|
||||
// but the error indicates quantity instead of length.
|
||||
ErrorTypeTooFew ErrorType = "FieldValueTooFew"
|
||||
// ErrorTypeInternal is used to report other errors that are not related
|
||||
// to user input. See InternalError().
|
||||
ErrorTypeInternal ErrorType = "InternalError"
|
||||
@@ -230,6 +234,8 @@ func (t ErrorType) String() string {
|
||||
return "Too long"
|
||||
case ErrorTypeTooMany:
|
||||
return "Too many"
|
||||
case ErrorTypeTooFew:
|
||||
return "Too few"
|
||||
case ErrorTypeInternal:
|
||||
return "Internal error"
|
||||
case ErrorTypeTypeInvalid:
|
||||
@@ -583,3 +589,27 @@ func (list ErrorList) RemoveCoveredByDeclarative() ErrorList {
|
||||
}
|
||||
return newList
|
||||
}
|
||||
|
||||
// TooFew returns a *Error indicating "too few". This is used to
|
||||
// report that a given list has too few items. This is similar to TooLong,
|
||||
// but the returned error indicates quantity instead of length.
|
||||
func TooFew(field *Path, actualQuantity, minQuantity int) *Error {
|
||||
var msg string
|
||||
|
||||
if minQuantity >= 0 {
|
||||
is := "items"
|
||||
if minQuantity == 1 {
|
||||
is = "item"
|
||||
}
|
||||
msg = fmt.Sprintf("must have at least %d %s", minQuantity, is)
|
||||
} else {
|
||||
msg = "has too few items"
|
||||
}
|
||||
|
||||
return &Error{
|
||||
Type: ErrorTypeTooFew,
|
||||
Field: field.String(),
|
||||
BadValue: actualQuantity,
|
||||
Detail: msg,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
// This is a test package.
|
||||
// +k8s:validation-gen-nolint
|
||||
package sliceofprimitive
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:minItems=0
|
||||
Min0Field []int `json:"min0Field"`
|
||||
|
||||
// +k8s:minItems=10
|
||||
Min10Field []int `json:"min10Field"`
|
||||
|
||||
// +k8s:minItems=0
|
||||
Min0TypedefField []IntType `json:"min0TypedefField"`
|
||||
|
||||
// +k8s:minItems=10
|
||||
Min10TypedefField []IntType `json:"min10TypedefField"`
|
||||
}
|
||||
|
||||
type IntType int
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sliceofprimitive
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
// All zero values
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min0Field: make([]int, 0),
|
||||
Min10Field: make([]int, 0),
|
||||
Min0TypedefField: make([]IntType, 0),
|
||||
Min10TypedefField: make([]IntType, 0),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]int, 1),
|
||||
Min10TypedefField: make([]IntType, 1),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 1, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 1, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]int, 9),
|
||||
Min10TypedefField: make([]IntType, 9),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 9, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 9, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]int, 10),
|
||||
Min10TypedefField: make([]IntType, 10),
|
||||
}).ExpectValid()
|
||||
|
||||
testVal := &Struct{
|
||||
Min0Field: make([]int, 1),
|
||||
Min10Field: make([]int, 11),
|
||||
Min0TypedefField: make([]IntType, 1),
|
||||
Min10TypedefField: make([]IntType, 11),
|
||||
}
|
||||
st.Value(testVal).ExpectValid()
|
||||
|
||||
// Test validation ratcheting
|
||||
st.Value(&Struct{
|
||||
Min0Field: make([]int, 1),
|
||||
Min10Field: make([]int, 1),
|
||||
Min0TypedefField: make([]IntType, 1),
|
||||
Min10TypedefField: make([]IntType, 1),
|
||||
}).OldValue(&Struct{
|
||||
Min0Field: make([]int, 1),
|
||||
Min10Field: make([]int, 1),
|
||||
Min0TypedefField: make([]IntType, 1),
|
||||
Min10TypedefField: make([]IntType, 1),
|
||||
}).ExpectValid()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by validation-gen. DO NOT EDIT.
|
||||
|
||||
package sliceofprimitive
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
// type Struct
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_Struct validates an instance of Struct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) {
|
||||
// field Struct.TypeMeta has no validation
|
||||
|
||||
// field Struct.Min0Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []int, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
return
|
||||
}(fldPath.Child("min0Field"), obj.Min0Field, safe.Field(oldObj, func(oldObj *Struct) []int { return oldObj.Min0Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []int, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
return
|
||||
}(fldPath.Child("min10Field"), obj.Min10Field, safe.Field(oldObj, func(oldObj *Struct) []int { return oldObj.Min10Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min0TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []IntType, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
return
|
||||
}(fldPath.Child("min0TypedefField"), obj.Min0TypedefField, safe.Field(oldObj, func(oldObj *Struct) []IntType { return oldObj.Min0TypedefField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []IntType, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
return
|
||||
}(fldPath.Child("min10TypedefField"), obj.Min10TypedefField, safe.Field(oldObj, func(oldObj *Struct) []IntType { return oldObj.Min10TypedefField }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
// This is a test package.
|
||||
// +k8s:validation-gen-nolint
|
||||
package sliceofstruct
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:minItems=0
|
||||
Min0Field []OtherStruct `json:"min0Field"`
|
||||
|
||||
// +k8s:minItems=10
|
||||
Min10Field []OtherStruct `json:"min10Field"`
|
||||
|
||||
// +k8s:minItems=0
|
||||
Min0TypedefField []OtherTypedefStruct `json:"min0TypedefField"`
|
||||
|
||||
// +k8s:minItems=10
|
||||
Min10TypedefField []OtherTypedefStruct `json:"min10TypedefField"`
|
||||
}
|
||||
|
||||
type OtherStruct struct{}
|
||||
|
||||
type OtherTypedefStruct OtherStruct
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package sliceofstruct
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
// All zero values
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min0Field: make([]OtherStruct, 0),
|
||||
Min10Field: make([]OtherStruct, 0),
|
||||
Min0TypedefField: make([]OtherTypedefStruct, 0),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 0),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]OtherStruct, 1),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 1),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 1, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 1, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]OtherStruct, 9),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 9),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 9, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 9, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make([]OtherStruct, 10),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 10),
|
||||
}).ExpectValid()
|
||||
|
||||
testVal := &Struct{
|
||||
Min0Field: make([]OtherStruct, 1),
|
||||
Min10Field: make([]OtherStruct, 11),
|
||||
Min0TypedefField: make([]OtherTypedefStruct, 1),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 11),
|
||||
}
|
||||
st.Value(testVal).ExpectValid()
|
||||
|
||||
// Test validation ratcheting
|
||||
st.Value(&Struct{
|
||||
Min0Field: make([]OtherStruct, 1),
|
||||
Min10Field: make([]OtherStruct, 1),
|
||||
Min0TypedefField: make([]OtherTypedefStruct, 1),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 1),
|
||||
}).OldValue(&Struct{
|
||||
Min0Field: make([]OtherStruct, 1),
|
||||
Min10Field: make([]OtherStruct, 1),
|
||||
Min0TypedefField: make([]OtherTypedefStruct, 1),
|
||||
Min10TypedefField: make([]OtherTypedefStruct, 1),
|
||||
}).ExpectValid()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by validation-gen. DO NOT EDIT.
|
||||
|
||||
package sliceofstruct
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
// type Struct
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_Struct validates an instance of Struct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) {
|
||||
// field Struct.TypeMeta has no validation
|
||||
|
||||
// field Struct.Min0Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []OtherStruct, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
return
|
||||
}(fldPath.Child("min0Field"), obj.Min0Field, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.Min0Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []OtherStruct, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
return
|
||||
}(fldPath.Child("min10Field"), obj.Min10Field, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.Min10Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min0TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
return
|
||||
}(fldPath.Child("min0TypedefField"), obj.Min0TypedefField, safe.Field(oldObj, func(oldObj *Struct) []OtherTypedefStruct { return oldObj.Min0TypedefField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
return
|
||||
}(fldPath.Child("min10TypedefField"), obj.Min10TypedefField, safe.Field(oldObj, func(oldObj *Struct) []OtherTypedefStruct { return oldObj.Min10TypedefField }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
// This is a test package.
|
||||
// +k8s:validation-gen-nolint
|
||||
package typedeftoslice
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
// Note: no validation here
|
||||
type UnvalidatedType []int
|
||||
|
||||
// +k8s:minItems=0
|
||||
type Min0Type []int
|
||||
|
||||
// +k8s:minItems=10
|
||||
type Min10Type []int
|
||||
|
||||
// Note: no validation here
|
||||
type UnvalidatedPtrType []*int
|
||||
|
||||
type SliceType []int
|
||||
|
||||
// +k8s:minItems=0
|
||||
type Min0TypedefType SliceType
|
||||
|
||||
// +k8s:minItems=10
|
||||
type Min10TypedefType SliceType
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
UnvalidatedField UnvalidatedType `json:"unvalidatedField"`
|
||||
|
||||
Min0Field Min0Type `json:"min0Field"`
|
||||
|
||||
Min10Field Min10Type `json:"min10Field"`
|
||||
|
||||
Min0TypedefField Min0TypedefType `json:"min0TypedefField"`
|
||||
|
||||
Min10TypedefField Min10TypedefType `json:"min10TypedefField"`
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package typedeftoslice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
// All zero values
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min0Field: make(Min0Type, 0),
|
||||
Min10Field: make(Min10Type, 0),
|
||||
Min0TypedefField: make(Min0TypedefType, 0),
|
||||
Min10TypedefField: make(Min10TypedefType, 0),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 0, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 0, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make(Min10Type, 1),
|
||||
Min10TypedefField: make(Min10TypedefType, 1),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 1, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 1, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make(Min10Type, 9),
|
||||
Min10TypedefField: make(Min10TypedefType, 9),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||||
field.TooFew(field.NewPath("min10Field"), 9, 10),
|
||||
field.TooFew(field.NewPath("min10TypedefField"), 9, 10),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
Min10Field: make(Min10Type, 10),
|
||||
Min10TypedefField: make(Min10TypedefType, 10),
|
||||
}).ExpectValid()
|
||||
|
||||
testVal := &Struct{
|
||||
Min0Field: make(Min0Type, 1),
|
||||
Min10Field: make(Min10Type, 11),
|
||||
Min0TypedefField: make(Min0TypedefType, 1),
|
||||
Min10TypedefField: make(Min10TypedefType, 11),
|
||||
}
|
||||
st.Value(testVal).ExpectValid()
|
||||
|
||||
// Test validation ratcheting
|
||||
st.Value(&Struct{
|
||||
Min0Field: make(Min0Type, 1),
|
||||
Min10Field: make(Min10Type, 1),
|
||||
Min0TypedefField: make(Min0TypedefType, 1),
|
||||
Min10TypedefField: make(Min10TypedefType, 1),
|
||||
}).OldValue(&Struct{
|
||||
Min0Field: make(Min0Type, 1),
|
||||
Min10Field: make(Min10Type, 1),
|
||||
Min0TypedefField: make(Min0TypedefType, 1),
|
||||
Min10TypedefField: make(Min10TypedefType, 1),
|
||||
}).ExpectValid()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by validation-gen. DO NOT EDIT.
|
||||
|
||||
package typedeftoslice
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
// type Struct
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_Min0Type validates an instance of Min0Type according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Min0Type(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj Min0Type) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_Min0TypedefType validates an instance of Min0TypedefType according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Min0TypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj Min0TypedefType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 0)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_Min10Type validates an instance of Min10Type according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Min10Type(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj Min10Type) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_Min10TypedefType validates an instance of Min10TypedefType according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Min10TypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj Min10TypedefType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.MinItems(ctx, op, fldPath, obj, oldObj, 10)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_Struct validates an instance of Struct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) {
|
||||
// field Struct.TypeMeta has no validation
|
||||
// field Struct.UnvalidatedField has no validation
|
||||
|
||||
// field Struct.Min0Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj Min0Type, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_Min0Type(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("min0Field"), obj.Min0Field, safe.Field(oldObj, func(oldObj *Struct) Min0Type { return oldObj.Min0Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj Min10Type, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_Min10Type(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("min10Field"), obj.Min10Field, safe.Field(oldObj, func(oldObj *Struct) Min10Type { return oldObj.Min10Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min0TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj Min0TypedefType, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_Min0TypedefType(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("min0TypedefField"), obj.Min0TypedefField, safe.Field(oldObj, func(oldObj *Struct) Min0TypedefType { return oldObj.Min0TypedefField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Min10TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj Min10TypedefType, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_Min10TypedefType(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("min10TypedefField"), obj.Min10TypedefField, safe.Field(oldObj, func(oldObj *Struct) Min10TypedefType { return oldObj.Min10TypedefField }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
minItemsTagName = "k8s:minItems"
|
||||
maxItemsTagName = "k8s:maxItems"
|
||||
minimumTagName = "k8s:minimum"
|
||||
maximumTagName = "k8s:maximum"
|
||||
@@ -35,6 +36,7 @@ const (
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterTagValidator(minItemsTagValidator{})
|
||||
RegisterTagValidator(maxItemsTagValidator{})
|
||||
RegisterTagValidator(minimumTagValidator{})
|
||||
RegisterTagValidator(maximumTagValidator{})
|
||||
@@ -132,11 +134,11 @@ func (maxBytesTagValidator) GetValidations(context Context, tag codetags.Tag) (V
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (mltv maxBytesTagValidator) Docs() TagDoc {
|
||||
func (mbtv maxBytesTagValidator) Docs() TagDoc {
|
||||
return TagDoc{
|
||||
Tag: mltv.TagName(),
|
||||
Tag: mbtv.TagName(),
|
||||
StabilityLevel: TagStabilityLevelBeta,
|
||||
Scopes: sets.List(mltv.ValidScopes()),
|
||||
Scopes: sets.List(mbtv.ValidScopes()),
|
||||
Description: `Indicates that a string field has a limit on its length in bytes.
|
||||
This could only allow as few as N/4 multi-byte characters.
|
||||
If you want to limit length of characters specifically, use maxLength.`,
|
||||
@@ -149,6 +151,61 @@ func (mltv maxBytesTagValidator) Docs() TagDoc {
|
||||
}
|
||||
}
|
||||
|
||||
type minItemsTagValidator struct{}
|
||||
|
||||
func (minItemsTagValidator) Init(_ Config) {}
|
||||
|
||||
func (minItemsTagValidator) TagName() string {
|
||||
return minItemsTagName
|
||||
}
|
||||
|
||||
var minItemsTagValidScopes = sets.New(
|
||||
ScopeType,
|
||||
ScopeField,
|
||||
ScopeListVal,
|
||||
ScopeMapVal,
|
||||
)
|
||||
|
||||
func (minItemsTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return minItemsTagValidScopes
|
||||
}
|
||||
|
||||
var minItemsValidator = types.Name{Package: libValidationPkg, Name: "MinItems"}
|
||||
|
||||
func (minItemsTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) {
|
||||
var result Validations
|
||||
|
||||
// NOTE: pointers to lists are not supported, so we should never see a pointer here.
|
||||
if t := util.NativeType(context.Type); t.Kind != types.Slice && t.Kind != types.Array {
|
||||
return Validations{}, fmt.Errorf("can only be used on list types (%s)", rootTypeString(context.Type, t))
|
||||
}
|
||||
|
||||
intVal, err := util.ParseInt(tag.Value)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to parse tag payload as int: %w", err)
|
||||
}
|
||||
if intVal < 0 {
|
||||
return result, fmt.Errorf("must be greater than or equal to zero")
|
||||
}
|
||||
result.AddFunction(Function(minItemsTagName, DefaultFlags, minItemsValidator, intVal))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (mitv minItemsTagValidator) Docs() TagDoc {
|
||||
return TagDoc{
|
||||
Tag: mitv.TagName(),
|
||||
StabilityLevel: TagStabilityLevelBeta,
|
||||
Scopes: mitv.ValidScopes().UnsortedList(),
|
||||
Description: "Indicates that a list has a minimum size.",
|
||||
Payloads: []TagPayloadDoc{{
|
||||
Description: "<non-negative integer>",
|
||||
Docs: "This list must be at least X items long.",
|
||||
}},
|
||||
PayloadsType: codetags.ValueTypeInt,
|
||||
PayloadsRequired: true,
|
||||
}
|
||||
}
|
||||
|
||||
type maxItemsTagValidator struct{}
|
||||
|
||||
func (maxItemsTagValidator) Init(_ Config) {}
|
||||
@@ -185,7 +242,7 @@ func (maxItemsTagValidator) GetValidations(context Context, tag codetags.Tag) (V
|
||||
if intVal < 0 {
|
||||
return result, fmt.Errorf("must be greater than or equal to zero")
|
||||
}
|
||||
// Note: maxItems short-circuits other validations.
|
||||
// Note: maxItems short-circuits other validations for safety.
|
||||
result.AddFunction(Function(maxItemsTagName, ShortCircuit, maxItemsValidator, intVal))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user