diff --git a/CHANGELOG.md b/CHANGELOG.md index aed2abc6..7dea37aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## v0.39.3 (WIP) -- Fixed JS error on `file` settings `maxSelect` change. +- Fixed JS error on `file` settings `maxSelect` change ([#7731](https://github.com/pocketbase/pocketbase/issues/7731)). + +- Other minor fixes (fixed `number` settings validator to not ignore 0 `max` values, normalized field settings tooltip format, etc.). ## v0.39.2 diff --git a/core/field_number.go b/core/field_number.go index 7f4855cb..28ba7e05 100644 --- a/core/field_number.go +++ b/core/field_number.go @@ -2,7 +2,6 @@ package core import ( "context" - "fmt" "math" validation "github.com/go-ozzo/ozzo-validation/v4" @@ -23,6 +22,12 @@ var ( _ SetterFinder = (*NumberField)(nil) ) +var ( + onlyIntValidationError = validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed") + minNumberValidationError = validation.NewError("validation_min_number_constraint", "Must be greater or equal than {{.min}}") + maxNumberValidationError = validation.NewError("validation_max_number_constraint", "Must be less or equal than {{.max}}") +) + // NumberField defines "number" type field for storing numeric (float64) value. // // The respective zero record field value is 0. @@ -151,15 +156,15 @@ func (f *NumberField) ValidateValue(ctx context.Context, app App, record *Record } if f.OnlyInt && val != float64(int64(val)) { - return validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed") + return onlyIntValidationError } if f.Min != nil && val < *f.Min { - return validation.NewError("validation_min_number_constraint", fmt.Sprintf("Must be larger than %f", *f.Min)) + return minNumberValidationError.SetParams(map[string]any{"min": *f.Min}) } if f.Max != nil && val > *f.Max { - return validation.NewError("validation_max_number_constraint", fmt.Sprintf("Must be less than %f", *f.Max)) + return maxNumberValidationError.SetParams(map[string]any{"max": *f.Max}) } return nil @@ -171,7 +176,14 @@ func (f *NumberField) ValidateSettings(ctx context.Context, app App, collection validation.By(f.checkOnlyInt), } if f.Min != nil && f.Max != nil { - maxRules = append(maxRules, validation.Min(*f.Min)) + maxRules = append(maxRules, validation.By(func(value interface{}) error { + // similar to validation.Min but doesn't ignore zero values + v, _ := value.(*float64) + if v == nil || f.Min == nil || *v >= *f.Min { + return nil + } + return minNumberValidationError.SetParams(map[string]any{"min": *f.Min}) + })) } return validation.ValidateStruct(f, @@ -190,7 +202,7 @@ func (f *NumberField) checkOnlyInt(value any) error { } if *v != float64(int64(*v)) { - return validation.NewError("validation_only_int_constraint", "Decimal numbers are not allowed.") + return onlyIntValidationError } return nil diff --git a/core/field_number_test.go b/core/field_number_test.go index eae6fb4d..79e2f122 100644 --- a/core/field_number_test.go +++ b/core/field_number_test.go @@ -237,7 +237,7 @@ func TestNumberFieldValidateSettings(t *testing.T) { []string{}, }, { - "decumal min", + "decimal min", func() *core.NumberField { return &core.NumberField{ Id: "test", @@ -248,7 +248,7 @@ func TestNumberFieldValidateSettings(t *testing.T) { []string{}, }, { - "decumal min (onlyInt)", + "decimal min (onlyInt)", func() *core.NumberField { return &core.NumberField{ Id: "test", @@ -272,7 +272,7 @@ func TestNumberFieldValidateSettings(t *testing.T) { []string{}, }, { - "decumal max", + "decimal max", func() *core.NumberField { return &core.NumberField{ Id: "test", @@ -283,7 +283,7 @@ func TestNumberFieldValidateSettings(t *testing.T) { []string{}, }, { - "decumal max (onlyInt)", + "decimal max (onlyInt)", func() *core.NumberField { return &core.NumberField{ Id: "test", @@ -307,19 +307,31 @@ func TestNumberFieldValidateSettings(t *testing.T) { []string{}, }, { - "min > max", + "min > max (0)", func() *core.NumberField { return &core.NumberField{ Id: "test", Name: "test", Min: types.Pointer(2.0), - Max: types.Pointer(1.0), + Max: types.Pointer(0.0), } }, []string{"max"}, }, { - "min <= max", + "min (0) > max", + func() *core.NumberField { + return &core.NumberField{ + Id: "test", + Name: "test", + Min: types.Pointer(0.0), + Max: types.Pointer(-1.0), + } + }, + []string{"max"}, + }, + { + "min == max", func() *core.NumberField { return &core.NumberField{ Id: "test", @@ -330,6 +342,18 @@ func TestNumberFieldValidateSettings(t *testing.T) { }, []string{}, }, + { + "min < max", + func() *core.NumberField { + return &core.NumberField{ + Id: "test", + Name: "test", + Min: types.Pointer(2.0), + Max: types.Pointer(3.0), + } + }, + []string{}, + }, } for _, s := range scenarios {